diff --git a/apps/ai-game-creator-shell/src-tauri/prompts/runtime/texts/direct-tools.json b/apps/ai-game-creator-shell/src-tauri/prompts/runtime/texts/direct-tools.json index f2e8b8d93..0437256ed 100644 --- a/apps/ai-game-creator-shell/src-tauri/prompts/runtime/texts/direct-tools.json +++ b/apps/ai-game-creator-shell/src-tauri/prompts/runtime/texts/direct-tools.json @@ -43,8 +43,8 @@ "agc_remove_background.parameters.sourceLocalAssetId": "必须来自 agc_list_registered_assets 返回的当前项目图片资源 localAssetId", "agc_remove_background.parameters.backgroundMode": "可选抠图模式:complex 用语义分割识别前景,flat 用纯色背景抠图;确定背景为纯色时优先使用 flat。省略时使用 complex", "agc_remove_background.parameters.screenColor": "flat 模式可选背景色;传 auto 或 #RRGGBB,省略时由服务自动检测", - "agc_browser_playtest.description": "使用客户端浏览器验证当前构建产物。visual 检查双端画面/布局/资源;gameplay 运行固定场景的真实输入/状态/重来检查。与 agc_run_validation 共用当前回合预算,相同输入的成功证据可复用;达标后交付,不再追加非阻塞润色。", - "agc_browser_playtest.parameters.attempt": "旧调用兼容字段;真实次数由客户端持久分配,不能用此字段重置预算", + "agc_browser_playtest.description": "使用客户端浏览器验证当前构建产物。visual 检查双端画面/布局/资源;gameplay 运行固定场景的真实输入/状态/重来检查。与 agc_run_validation 共享宿主回合归属与操作控制,相同输入的成功证据可复用;达标后交付,不再追加非阻塞润色。", + "agc_browser_playtest.parameters.attempt": "旧调用兼容字段;实际执行序号由客户端持久分配,此字段不能改写宿主记录", "agc_browser_playtest.parameters.mode": "visual 用于图片/颜色/布局定向复核;gameplay 用于玩法或输入变化,须提供固定场景的真实状态接口。缺省 visual 只证明视觉检查。", "agc_browser_playtest.parameters.scenario": "gameplay 场景,缺省 generic-v1;先读 agc-browser-playtest 的证据合同,不得伪造状态或用视觉检查冒充通关", "agc_environment_check.description": "检查客户端配套 Node/npm 的实际版本和浏览器 CDP 健康。新建入口已由宿主自动预检,此工具用于环境诊断或新出现的环境故障;阻塞时报告原因,不自行下载工具链或全盘搜索。只读诊断和非 Web 编辑器工程无需调用。不会安装依赖或消耗验证预算。", diff --git a/apps/ai-game-creator-shell/src-tauri/src/config.rs b/apps/ai-game-creator-shell/src-tauri/src/config.rs index 48ff9c917..d9e1ad7fa 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/config.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/config.rs @@ -3710,9 +3710,12 @@ pub(crate) fn migrate_legacy_game_creator_agent_mode(path: &Path) -> Result<(), return Ok(()); } let content = read_game_creator_private_file_to_string(path, "客户端配置文件", 256 * 1024)?; - let mut config = serde_json::from_str::(&content) - .map_err(|error| format!("解析客户端配置失败:{}: {error}", path.display()))?; - let mut changed = false; + // 桌面 setup 此时还未注册运行配置目录;先归一化,再由本入口统一安全写回。 + let normalized = normalize_game_creator_runtime_limits(path, &content)?; + let mut config = + serde_json::from_str::(normalized.as_deref().unwrap_or(&content)) + .map_err(|error| format!("解析客户端配置失败:{}: {error}", path.display()))?; + let mut changed = normalized.is_some(); changed |= ensure_game_creator_custom_llm_file_fields(&mut config); let inferred_agent_mode = config .agent_mode @@ -4001,18 +4004,32 @@ pub(crate) fn read_game_creator_config_file(path: &Path) -> Result Result { - let mut value: serde_json::Value = serde_json::from_str(&content) + let Some(migrated) = normalize_game_creator_runtime_limits(path, &content)? else { + return Ok(content); + }; + // 共享模板是只读输入;只有 App 管理的私有配置在迁移成功后安全写回。 + if game_creator_config_path_is_runtime_managed(path) { + write_game_creator_config_atomically(path, &migrated)?; + } + Ok(migrated) +} + +fn normalize_game_creator_runtime_limits( + path: &Path, + content: &str, +) -> Result, String> { + let mut value: serde_json::Value = serde_json::from_str(content) .map_err(|error| format!("解析客户端配置失败:{}: {error}", path.display()))?; let Some(validation) = value .get_mut("validation") .and_then(serde_json::Value::as_object_mut) else { - return Ok(content); + return Ok(None); }; let removed_execution = validation.remove("maxExecutionSeconds").is_some(); let removed_turn = validation.remove("maxTurnSeconds").is_some(); if !removed_execution && !removed_turn { - return Ok(content); + return Ok(None); } let config: GameCreatorAppConfigFile = serde_json::from_value(value.clone()) .map_err(|error| format!("迁移客户端配置失败:{}: {error}", path.display()))?; @@ -4024,11 +4041,7 @@ fn migrate_game_creator_runtime_limits(path: &Path, content: String) -> Result String { mod validation_config_tests { use super::*; + #[test] + fn startup_migrates_runtime_limits_before_registering_the_config_directory() { + let temp = tempfile::tempdir().unwrap(); + let _guard = crate::tests::use_test_runtime_config_dir(temp.path().into()); + // 保持配置测试锁和恢复 guard,但模拟普通桌面 setup 尚未注册目录的状态。 + *game_creator_runtime_config_dir_lock().lock().unwrap() = None; + for file in [ + GAME_CREATOR_CONFIG_FILE_NAME, + GAME_CREATOR_LOCAL_CONFIG_FILE_NAME, + ] { + let path = temp.path().join(file); + for validation in [ + serde_json::json!({"maxRuns":17,"maxExecutionSeconds":900,"maxTurnSeconds":1800}), + serde_json::json!({"maxRuns":17,"maxExecutionSeconds":900}), + serde_json::json!({"maxRuns":17,"maxTurnSeconds":1800}), + ] { + let old = serde_json::json!({ + "schemaVersion": GAME_CREATOR_APP_CONFIG_SCHEMA_VERSION, + "agentMode": GAME_CREATOR_AGENT_MODE_CODEX_APP_SERVER, + "validation": validation, + "llm": {"customEnabled":true,"visibleModels":[],"webSearchEnabled":true,"maxRetries":9} + }); + write_game_creator_config_atomically(&path, &old.to_string()).unwrap(); + migrate_legacy_game_creator_agent_mode(&path).unwrap(); + assert!(game_creator_runtime_config_dir().is_none()); + let migrated = fs::read_to_string(&path).unwrap(); + let value: serde_json::Value = serde_json::from_str(&migrated).unwrap(); + assert_eq!(value["validation"], serde_json::json!({"maxRuns":17})); + assert_eq!(value["llm"]["maxRetries"], 9); + assert_eq!(value["llm"]["customEnabled"], true); + migrate_legacy_game_creator_agent_mode(&path).unwrap(); + assert_eq!(fs::read_to_string(&path).unwrap(), migrated); + } + for invalid in [ + r#"{"validation":{"maxRuns":0,"maxTurnSeconds":1800}}"#, + r#"{"validation":{"maxRuns":3,"unknown":true,"maxTurnSeconds":1800}}"#, + ] { + write_game_creator_config_atomically(&path, invalid).unwrap(); + assert!(migrate_legacy_game_creator_agent_mode(&path).is_err()); + assert_eq!(fs::read_to_string(&path).unwrap(), invalid); + } + } + } + #[test] fn retired_runtime_limits_are_migrated_once_without_changing_other_settings() { let temp = tempfile::tempdir().unwrap(); diff --git a/docs/project-memory/plans/【实施计划】移除GameAgent宿主运行时间上限-2026-10-06.md b/docs/project-memory/plans/【实施计划】移除GameAgent宿主运行时间上限-2026-10-06.md index 798f76365..8cad59a26 100644 --- a/docs/project-memory/plans/【实施计划】移除GameAgent宿主运行时间上限-2026-10-06.md +++ b/docs/project-memory/plans/【实施计划】移除GameAgent宿主运行时间上限-2026-10-06.md @@ -36,7 +36,7 @@ ## 实现顺序 1. 先列出所有 tick、期限检查、Exhausted 生产者与消费者。区分时间耗尽、交付复核次数耗尽和历史终态,不能整体删除 Exhausted。核对计时字段在统计/日志中的读取者,保留有现役用途的用时。 -2. 收敛配置:正式结构只保留 maxRuns;仅在现有持久化加载边界识别并删除 maxExecutionSeconds/maxTurnSeconds,校验后在 App 管理的配置目录安全写回当前格式,之后正常加载无需转换;仓库旁的共享只读配置仅在加载边界转换,不自动改写源文件。不保留 deprecated 字段、serde 别名、默认值或关闭开关,不开放任意未知字段兼容;写回失败报告真实错误,不标记迁移成功或覆盖其它配置。 +2. 收敛配置:正式结构只保留 maxRuns;桌面启动与普通读取共用纯数据归一化,在任何严格反序列化前移除退役字段,启动入口通过自身原子写回路径保存,不依赖运行配置目录注册顺序。仅在现有持久化加载边界识别并删除 maxExecutionSeconds/maxTurnSeconds,校验后在 App 管理的配置目录安全写回当前格式,之后正常加载无需转换;仓库旁的共享只读配置仅在加载边界转换,不自动改写源文件。不保留 deprecated 字段、serde 别名、默认值或关闭开关,不开放任意未知字段兼容;写回失败报告真实错误,不标记迁移成功或覆盖其它配置。 3. 执行账本升为 `agc-direct-execution.v4`:对已发布 v1/v2/v3 在加载边界白名单移除 maxExecutionMs/maxTurnMs,延续原有必要的旧合同迁移,保留用时与归属事实;校验与取得归属后安全持久化 v4。新 v4 类型无这两个字段且拒绝未知字段;历史终态及报告不改,旧活动操作继续执行原恢复检查。更新早期项目侧账本的提示原因,不将缺少可信归属的历史记录提升为执行权限。迁移只做数据转换,不调用旧预算逻辑;不再支持这些旧升级来源时删除转换与专属测试。 4. 删除宿主时间超限终态与仅服务该上限的周期任务/时钟回退期限检查;删除失去职责的 tick 方法及全部调用,不保留返回成功的空壳。逐调用点保留阶段、取消、操作身份与锁内检查,尤其是 begin_paid_dispatch、写入取得锁后和 begin_sealing;不删除整个执行适配器 watchdog。移除仅供预算使用且已无统计消费方的时钟状态与辅助方法。 5. 同步验证/交付回执、配置类型和文案;保留 usedExecutionMs 等已用时事实。缓存与历史回执若含旧上限,只作为历史字段读取,不据此重新启用限制或改写证据哈希。 @@ -86,7 +86,7 @@ git diff --check | 超过旧上限仍可执行与正常结束 | `direct_execution` 定向组:无合同完成、并发执行统计、长时间写入/付费后取消、同回合恢复 | 37 passed | | 合同复核与真实证据 | `direct_delivery` 定向组 | 12 passed | | 验证缓存与回执 | `direct_validation` 定向组 | 9 passed | -| 配置迁移与恢复 | `config::` 定向组:迁移落盘、重复读取、只读源、备份恢复、未知字段及写入失败 | 18 passed | +| 配置迁移与恢复 | `config::` 定向组:启动前归一化、迁移落盘、重复读取、只读源、备份恢复、未知字段及写入失败 | 19 passed | | 独立 Codex 超时、关闭与进程退出 | `codex_app_server` 定向组;包括一小时旧回合及累计执行 2000 秒的协议夹具,分别正常回复及合同交付 | 144 passed,4 个既有 ignored | | Claude 独立执行行为 | `claude_code_cli` 定向组 | 13 passed | | 提示词边界与编译 | `--test prompt_source_boundaries --test runtime_prompt_bundle_build` | 6 + 18 passed | @@ -99,3 +99,9 @@ Rust 定向命令在上述 manifest 下加 `--bin genarrative-ai-game-creator-sh 未验证:真实付费/模型请求、手动 GUI/CLI 操作、Windows Job 专项及真实连续运行超过 30 分钟。长时间边界通过修改测试私有账本与操作开始时刻验证,无产品测试开关。Codex 的 4 个既有 ignored 涉及真实捆绑目录、OAuth、子进程辅助夹具与需要认证的模型 smoke;不算新增通过证据。旧记录迁移由数据加载触发,本次只操作临时测试数据。 主规范与共享决策已同步;当前只有这一个里程碑,不推进其它工作。运行时验收后删除临时里程碑与实施计划,持久验证边界保留在主规范。 + +## 评审修复:桌面启动配置迁移顺序 + +- 启动的 agent-mode 迁移原先直接严格解析旧配置,先于普通读取的字段移除,导致 AppData 中仍有时间字段时启动失败。启动与普通读取现共用纯归一化函数;启动将字段移除计入自身 changed,沿原安全写回路径保存,无须提前注册全局目录。 +- 新回归在配置测试锁下清空运行配置目录,覆盖主配置与本地覆盖、单项与双项退役字段、重复启动及非法字段拒绝不覆盖。修复前实测出现 `unknown field maxExecutionSeconds`;修复后配置组 19 项通过。 +- 浏览器试玩描述改为共享操作归属,旧 attempt 只描述客户端执行序号;共享决策与 Claude 超时排障说明同步到已移除宿主时间上限的现状。 diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index c05a6e63f..8f9c35684 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -9807,17 +9807,17 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在 - 普通工具准入只检查原回合活动状态、并发和既有权限,不要求先登记交付合同;成功/失败/取消只结算本次操作,删除全局 Draining 与执行/返修批次计数。 - 验证失败和源码漂移影响对应证据,不阻断无关工作。远端不确定结果沿资源自身 operation/幂等记录核对;本地执行器失控或持久状态损坏仍结束回合。 -- 保留累计执行时间、整轮墙钟、原生执行前审批、关闭时清理与原回合写入/付费提交检查。模型执行结束时先关闭准入,确认清理后才允许交付反馈继续;普通失败不进入关闭阶段。 +- 累计执行与整轮耗时仅用于观测,保留原生执行前审批、关闭时清理与原回合写入/付费提交检查。模型执行结束时先关闭准入,确认清理后才允许交付反馈继续;普通失败不进入关闭阶段。 - `validation.maxRuns` 只保留交付回复复核用途;合同的创建与两类要求简化见下条决策,视觉/玩法判据保持原样。图片工具仍等待结果,Codex 调度不变。 -- v2 执行账本只对白名单 v1 字段迁移,保留预算与终态,不恢复旧活动权限;没有可信时间记录的更早项目侧账本不授予同回合新预算。 +- 执行账本现为 v4,已发布 v1/v2/v3 只在加载边界白名单迁移,保留用时、复核计数与终态,不恢复旧活动权限;更早项目侧账本不能证明宿主归属,不授予同回合执行权限。 - 权威边界与验收入口:[AI 游戏创作智能体 App 实施计划](../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md#direct-操作控制2026-10-04)。Windows Job 退出证明仍须由 Windows 环境验收。 ## 2026-10-04:Direct 合同按用户意图登记、正常响应结束后复核 -- 合同自动检查只在模型正常结束响应后触发;删除操作结算与补丁成功触发的提前封口,状态查询保持只读评估。异常终止与预算耗尽仍独立处理,不以证据齐备覆盖失败事实。 +- 合同自动检查只在模型正常结束响应后触发;删除操作结算与补丁成功触发的提前封口,状态查询保持只读评估。异常终止与交付复核次数耗尽仍独立处理,不以证据齐备覆盖失败事实。 - 提示 Agent 在用户要求制作、完成或交付游戏时登记合同,由 Agent 理解用户意图;首次输入也按同一用户意图条件登记,提示与技能不另设空项目或首次输入的免登记说明。无合同既不阻断普通操作,也不阻断正常结束。 - 删除 artifact/command 合同要求及宿主自动补入项,保留普通文件、命令与构建能力。视觉/玩法的现有判据和证据真实性校验不改,仅在主动注册后应用原有新 Web 视觉/玩法补充;用户于 2026-10-05 确认这两类检查保持现状。 -- 旧未完成合同不得因过滤退役要求变成成功;保留旧预算、终态和操作身份,版本迁移与恢复有独立验收。 +- 旧未完成合同不得因过滤退役要求变成成功;保留用时、复核计数、终态和操作身份,版本迁移与恢复有独立验收。 - 权威规则:[Direct 合同规则](../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md#宿主验收与执行许可合同)。运行时已实现;验证边界保留在主规范,不以协议夹具代替真实模型或 Windows 专项证据。 ## 2026-10-05:Direct 重构手动 GUI 检查与范围收敛 @@ -9830,7 +9830,7 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在 - 回合收尾和合同完成使用平台可证明的受控归属退役:Windows 为自有 Job,Unix 为受控进程组。进程组成功退出不再被完整子树标志误判为中断,但不宣称逃逸后代已退出;缺失或失败的清理仍阻止完成。 - Linux 排除僵尸时必须保守处理不完整扫描;数字 PID 的读取/解析不确定性不能成为组已空的证据,已确认消失的 PID 可以忽略。 -- 正常关闭连接的迟到通知不创建或覆盖成功回合的终态报告;无合同收尾新产生的预算报告优先返回,其他真实关闭错误保留。 +- 正常关闭连接的迟到通知不创建或覆盖成功回合的终态报告;无合同收尾优先返回既有终态报告,其他真实关闭错误保留;收尾不再因宿主时间产生预算报告。 - 权威边界见[Direct 合同规则](../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md#宿主验收与执行许可合同)。视觉/玩法判据不变;宿主时间上限已由 2026-10-06 决策移除。 ## 2026-10-05:AGC 标题栏项目标签评审后续(空态、视图门控与文案) diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index 0c6d227a4..40d3d8921 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -6477,7 +6477,7 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/` - **根因**:`claude_code_cli.rs` 先把 AGC 进程继承来的 `ANTHROPIC_BASE_URL` / `ANTHROPIC_AUTH_TOKEN` 原样复制给 sidecar,再让它们优先于平台会话(`claude_base_url` 里 `std::env::var("ANTHROPIC_BASE_URL")` 排在 `.or_else` 最前,凭据有 `if … is_none()` 守卫因此永远不覆盖)。实测:直接跑 sidecar 探针,`claude.exe`(PID 43256)的两条 443 连接落在 `198.18.2.60` = 用户个人的 `yunyi.rdzhvip.com`,完全没碰 `dev.genarrative.world` / Router;同一个 token 对 `POST https://yunyi.rdzhvip.com/claude/v1/messages` 45 秒不返回(Bearer 与 `x-api-key` 都一样)。用户 `~/.claude/settings.json` 里的 env 反而无关——sidecar 用 `settingSources: []` 关掉了设置文件来源,事故来源是**进程环境**。 - **现行口径**:cc 的路由与凭据只由 AGC 决定,本机 `ANTHROPIC_*` **完全不参与**(`claude_code_route` 的入参里没有进程环境)。官方模型 + 平台会话 → `{apiBaseUrl}/api/llm/anthropic` + `Bearer <平台 access token>`;`customEnabled` 或配了 `llm.apiKey` → AGC 配置的 baseUrl(自定义端点用 `x-api-key`,其余保持 Bearer);没登录也没配 key 就报「cc 模式需要先登录陶泥儿账号」,不再退回本机环境。`ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` 也不再进 `copy_env` 白名单,只由解析出的路由下发。个人中转要走 AGC 的 `customEnabled` + baseUrl + apiKey,不做隐式继承。 - **同时(配置隔离)**:sidecar 子进程把 `HOME`、`USERPROFILE`、`CLAUDE_CONFIG_DIR` 全部指向项目内隔离目录(`.agent/runtime/claude-code/home`)。只改 `HOME` 不够——Windows 上 Node 的 `os.homedir()` 只看 `USERPROFILE`,Claude Code 会照样读用户自己的 `~/.claude.json`(个人 `mcpServers`、插件市场、凭据)并把 `projects/`、`sessions/` 写回真实 profile。实测隔离前 CLI 先花 ~90 秒在个人配置上、真实 `~/.claude/sessions` 每次回合都被写;隔离后 `time_to_request_ms=41`,且只写隔离目录。 -- **同时(超时语义)**:`requestTimeoutMs`(默认 180000)在 cc 执行器里只作**静默预算**:sidecar 每有一条事件就重置,连续静默超过预算才按 `sidecar-turn-timeout` 收口;另有 `CLAUDE_CODE_TURN_MAX_DURATION`(45 分钟)兜底事件流假活,正常情况更早到的是 DirectProject 的 `maxTurnSeconds`。原来「整回合 180 秒墙钟」会把「网关慢但仍在下发事件」的正常回合掐死——实测 19:46 那轮 sidecar 一直在出事件,第 180 秒被墙钟杀掉。 +- **同时(超时语义)**:`requestTimeoutMs`(默认 180000)在 cc 执行器里只作**静默预算**:sidecar 每有一条事件就重置,连续静默超过预算才按 `sidecar-turn-timeout` 收口;另有 `CLAUDE_CODE_TURN_MAX_DURATION`(45 分钟)兜底事件流假活;DirectProject 的宿主墙钟上限已移除,不再存在更早触发的 `maxTurnSeconds`。原来「整回合 180 秒墙钟」会把「网关慢但仍在下发事件」的正常回合掐死——实测 19:46 那轮 sidecar 一直在出事件,第 180 秒被墙钟杀掉。 - **排查手段**:sidecar 每次启动都写一行 `agent.direct_codex.claude_route source= host=<…> auth=`(只写主机名,不含路径与凭据)。这一行是判断"回合到底发到哪个网关"的唯一低成本证据。 - **注意**:日志脱敏标记包含 `credential`、`x-api-key`、`bearer `、`token=`、`api_key`,命中即整行替换成 ``。诊断行只能写 `source=`/`host=`/`auth=` 这类自查过的字段(第一版写成 `credential=bearer`,整行被吃掉过一次)。 - **验证**:`cargo test --features=cocos-editor-execute,unity-editor-execute,godot-editor-execute --bin genarrative-ai-game-creator-shell claude_code_cli::tests::` 11 passed,覆盖平台优先、自定义端点保留配置、未登录时不回落本机环境且报「先登录」、baseUrl `/v1` 归一化、主机名诊断、sidecar 环境隔离(`USERPROFILE`/`CLAUDE_CONFIG_DIR` 指向隔离目录且不带本机 `ANTHROPIC_*`);另用本地假 Anthropic 端点跑通真 sidecar:`REQ HEAD /api/hello` → `REQ POST /v1/messages?beta=true auth=bearer` → `result=PROBE_OK`。 diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md index 6271c9959..d46182b0f 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -181,7 +181,7 @@ UI 编辑器的“分析参考图”步骤、Rust 命令 `suggest_ui_design_sema - 交付合同与视觉/玩法证据判据不变,validation.maxRuns 继续只限制交付复核次数。正常响应结束后才能启动交付封口;缺失证据或退出证明仍不能判定完成,真实失败保持原原因。 - 保留已用执行时长及回合时间的可观测信息,但不能再据此拒绝操作、取消执行或产生时间耗尽报告。新状态回执不再公开 maxExecutionMs 或虚假的剩余时间;历史回执按原记录展示。 - 清理要求:彻底删除两项限制的运行时代码、配置字段、默认值、校验、专属定时任务、错误分支及失效测试/说明;不保留 deprecated 字段、禁用开关、空壳 tick 或新旧执行双路径。时间统计只有仍被现役观测消费时才保留。 -- 数据迁移:仅在持久化加载边界将旧配置与旧账本转成当前格式;从配置删除 maxExecutionSeconds/maxTurnSeconds,从账本删除 maxExecutionMs/maxTurnMs,校验后安全落盘,每份记录成功迁移后不再转换。仓库旁共享配置属于只读输入,仅在加载边界转换,不自动改写源文件。正式配置类型、执行账本类型及运行时不认识这些旧上限;其它未知字段继续严格校验。保留回合身份、用时、合同、证据、操作、复核计数和退出事实;旧 Completed/Interrupted/Exhausted 终态与报告不重开或覆盖。旧活动状态仍按归属与恢复规则处理,更早项目侧账本不能升级为可信宿主权限或证据。迁移只保留支持已发布存量数据所必需的字段识别与测试,退出条件为不再支持含这些字段的升级来源,届时同步删除迁移与专属测试,不保留永久兼容层。 +- 数据迁移:仅在持久化加载边界将旧配置与旧账本转成当前格式;从配置删除 maxExecutionSeconds/maxTurnSeconds,从账本删除 maxExecutionMs/maxTurnMs,校验后安全落盘,每份记录成功迁移后不再转换。桌面启动的主配置与本地覆盖必须在首次严格反序列化之前移除退役字段;启动迁移的安全写回不依赖全局运行配置目录已注册,失败时不覆盖原文件。仓库旁共享配置属于只读输入,仅在加载边界转换,不自动改写源文件。正式配置类型、执行账本类型及运行时不认识这些旧上限;其它未知字段继续严格校验。保留回合身份、用时、合同、证据、操作、复核计数和退出事实;旧 Completed/Interrupted/Exhausted 终态与报告不重开或覆盖。旧活动状态仍按归属与恢复规则处理,更早项目侧账本不能升级为可信宿主权限或证据。迁移只保留支持已发布存量数据所必需的字段识别与测试,退出条件为不再支持含这些字段的升级来源,届时同步删除迁移与专属测试,不保留永久兼容层。 - 非目标:不承诺无限运行,不调整 Provider 重试、工具调度、付费额度、资源幂等、外部 MCP、公开 API 或数据库;不恢复已因旧预算结束的回合。 - 验收:用受控时间或已用时夹具越过两条旧边界后,回合仍可正常执行、写入、登记合同和结束;同时验证取消、真实 Provider 超时、断连、交付复核失败、迁移及退出证明仍有效。GUI/CLI 共用入口及 Codex/Claude 的共享宿主状态均纳入边界核对,真实平台 smoke 与模拟时间证据分别记录。