合并:把 origin/master 并入回合错误重构分支并保留双方行为
- 解决 direct_runtime/mod.rs 冲突:保留本分支 typed 回合错误重构(TurnError/EnqueueError 与 record_direct_codex_failure_facts 拆分),接入 master 的诊断 v3 口径(persist_agent_runtime_error 新签名 + typed error 字段)、prompt 反馈改截断不脱敏、项目写锁改名 - 解决 thread_manager/dispatch.rs 冲突:保留本分支 TurnError/TurnCompletion 换代与 finish_turn_failure 分类层,接住 master 的 tt= 日志改名与 cc 执行器整轮成功后补写 completed 终态 - 解决 direct_tools_mcp.rs 冲突:采用 master 版(本分支唯一改动是 external MCP 容量测试读 body,master 已有等价实现) - 处理 direct_turn_failure.rs 删除/修改冲突:维持本分支删除,把 completed() 语义迁进 TurnCompletion::Completed 并补回对应测试 - 处理 runtime_driver/entrypoints.rs 修改/删除冲突:接受 master 退役自建 Agent Runtime 的删除(本分支只在该文件做 direct_now_ms 改名) - 合并 decision-log.md:双方条目都保留 - 修复 auto-merge 漏掉的语义冲突:claude_code_cli.rs 的 direct_now_ms() 改名 now_ms();诊断测试改为断言 AGENT_RUNTIME_ERROR_SCHEMA_VERSION、detail 字段改 message;record_direct_codex_failure_facts 新增 typed_error 入参(TurnError 不序列化,落盘用 classify 投影出的 TurnFailure)
This commit is contained in:
@@ -35,7 +35,7 @@ AGC 项目开发聊天框当前同时从多处取数据:Direct 回合事件(
|
||||
- 执行通道断开同样是失败终态,也必须带 `failure`:连接级故障(app-server 进程退出 / stdout 流断 / JSON 行越界)与回合事件通道关闭都算,`kind="transport-failed"`、`message` 用宿主当场写下的那份诊断(含 `exitStatus` 与 stderr 摘要,已脱敏截断)。宿主在检测到连接终止的第一时间把这条事实记到本回合的执行适配器上,终态判定再从适配器读:执行适配器的看门狗盯着同一个 `closed` 标志,用调用点局部变量会输给这场调度竞争,失败原因就只剩日志、界面只会看到"本轮已结束"。判据是"适配器是否已由宿主主动关闭"——宿主自己收束(正常终态 / 用户主动停止 / 预算与交付收尾)走的是同一个 `TransportClosed` 事件,但这些不算失败。
|
||||
- 终态由**事实**判定,不由收尾阶段反推:判定按优先级取「宿主当场记下的失败(通道断开 / 等待超时 / app-server 单方面中断)→ 本回合的错误结果是 Err → 只有收尾阶段的账本读不出来时才用交付报告」,**有载荷一定写 `status="failed"`**,没载荷才用收尾阶段推出来的 `status`。收尾会把 ledger 阶段推成 `Interrupted`,让阶段决定终态就会把已经失败的一轮讲成"已结束"。模型自报失败(原生 `turn/completed.status="failed"` 的 `error`,带 `codexErrorInfo` 分类)不为载荷新增输入字段:宿主把原生 `error` 的 `codexErrorInfo` 解析成 typed 分类后当作本回合的错误结果,走同一条通道进载荷;交付报告只说明"收束到哪一步",不得顶掉原因。
|
||||
- 回合失败在宿主内部是 **typed** 的:`agent/direct_turn_error.rs` 的 `DirectTurnError` 每个变体自带字段(并发拒绝带两个 invocation id、模型失败带分类、超时带撞的是哪条上限、通道断开带宿主诊断),**调用级拒绝**(这一轮没有开始)与**回合级失败**(这一轮已开始并被判失败)不共用判据,分流只认 `is_turn_failure()`。判据不再对原因文本做子串匹配,`LlmError` 只在平台层入口出现一次(`DirectTurnError::from_model_call`)。线上载荷 `{kind, message}`、命令边界字符串与 CLI 返回值都由这一个出口投影出来,Rust 侧任何地方都不再解析它们。
|
||||
- 分页锚点取原始条目 id;一次翻页操作在前端自动连拉,直到出现可显示条目或 `hasMore=false`,上限 5 页。
|
||||
- 分页锚点取原始条目 id;一次翻页操作在前端自动连拉,直到出现可显示条目或 `hasMore=false`,上限 5 页。(**按 2026-10-02 ADR 补充**:更早历史的触发时机、加载指示、失败重试与视口锚定改由 [`【ADR】DirectProject对话滚动与历史自动加载-2026-10-02`](./【ADR】DirectProject对话滚动与历史自动加载-2026-10-02.md) 规定;本条的分页锚点口径与连拉上限仍然有效。)
|
||||
- `notify` 是唯一唤醒来源:`subscribe` 的 bootstrap 事件本身就是该 subscriber 此刻要处理的事件(游标已在队尾),前端直接 reduce 它们,不需要为了取这批事件再补一次 `consume`,之后完全由 `notify` 驱动,不设低频 tick 或任何轮询兜底。唯一例外是回执竞态:Rust 侧一注册完 subscriber 就开始 `notify`,前端却要等回执才知道自己的 `subscriptionId`,这段窗口内的通知只能记成欠账,回执到达后立刻补一次 `consume` 取回,否则该回合的尾部事件会卡在队列里等一个可能永不出现的下一次通知。
|
||||
- 迁移按一次干净切换落地:不做灰度、不做运行时开关、不双跑;允许提交序列里存在「新源已启用、旧代码尚未删除」的中间窗口,禁止反向的「新源未启用、旧源已删」。
|
||||
- 思考过程与工具活动同样从运行态事件与历史条目推断,界面展示保持不变。
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# 【ADR】DirectProject对话滚动与历史自动加载-2026-10-02
|
||||
|
||||
状态:已接受
|
||||
|
||||
## 背景
|
||||
|
||||
DirectProject 聊天区(`apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectConversation/`)当前的滚动与历史体验有三个问题:
|
||||
|
||||
1. 更早历史要靠常驻按钮「显示更早的对话」(`.message-history-more`)手动拉,但 `handleScroll` 里已经有一条「滚到顶部 24px 以内就自动加载」的路径——按钮是半冗余的第二入口,还没有任何加载中的视觉反馈(`historyLoadingRef` 是 ref,渲染不出来)。
|
||||
2. 用户滚上去之后没有任何「回到最新」的入口,只能自己一直滚到底。
|
||||
3. 展开一折叠块(`<details>` 或 `[aria-expanded]`)时浏览器保持 `scrollTop` 不变,新展开的正文长在视口下方,用户必须再滚一次才能看到;反过来在底部展开时内容直接顶出可视区。
|
||||
|
||||
同一目录下的 `PlanningChatView` 复用 `.project-chat-message-list` / `.project-chat-conversation` 两个类名,所以任何滚动容器结构改动都必须留在 DirectProject 本地,不能改公共类。
|
||||
|
||||
## 决策
|
||||
|
||||
### 1. 更早历史:删掉按钮,自动加载 + 内联加载行
|
||||
|
||||
- 删除「显示更早的对话」按钮与 `DirectProjectConversation` 里的条件渲染;`.message-history-more` 样式**保留**——策划对话(`PlanningChatView`)仍在用同一套类名与按钮,等它一起改造时再删(见「影响」的后续项)。
|
||||
- 触发一(滚动):`scrollTop <= 24` 且 `historyHasMore` 且不在加载中且没有失败记录时自动加载。
|
||||
- 触发二(填充视口):首帧之后内容填不满视口(`scrollHeight <= clientHeight`)时继续加载,直到填满或 `hasMore=false`;不允许出现「历史比视口短、又没有按钮」的死局。
|
||||
- 两个触发都不越过既有的首屏订阅锚点 `lastCompletedItemId`;一次加载仍最多连拉 5 页(口径见 [`【ADR】DirectProject对话历史单一事实源-2026-09-16`](./【ADR】DirectProject对话历史单一事实源-2026-09-16.md))。
|
||||
- 加载中在列表最上方(比最旧一条回合更靠上)挂载一行 `role="status"`、`aria-live="polite"` 的「正在加载更早的对话」,带旋转圈;延迟 150ms 才显示,加载结束即卸载。它按需挂载,靠位置补偿(见第 2 条)保证下面的消息不跳。
|
||||
- 失败:挂起自动加载,列表顶部保留一行内联错误行——`role="alert"` 只包住「加载更早对话失败」文案本身,重试是可聚焦按钮、留在 live region 之外(assertive + atomic 的 live region 里不放交互控件);**不自动重试**,只有点重试(或切换项目)才重新开始;重试成功后错误行消失。
|
||||
- 一次加载与它所属的**世代**绑定:切换项目或新起一次读取都推进世代号(`historyLoadTokenRef`),旧世代落地时整段失效——不合并条目、不写游标、不关加载态。只比项目路径不够:A→B→A 之后在飞的旧读取又落回同一个路径,原守卫放行,会把新一代的加载行与 `historyLoadingRef` 这道并发闸门一起改掉。换项目的推进放在**渲染期**(与 `projectPathRef` 同一处),不放在复位 effect 里:passive effect 走宏任务、promise 续体走微任务,旧读取可能在「切换提交完成、复位 effect 还没跑」的窗口里落地,那时世代号还是旧的,守卫会放行。
|
||||
|
||||
### 2. 前插锚定:回合 key + 块身份 + 偏移,关掉浏览器原生锚定
|
||||
|
||||
- 锚点 = 顶部可见块的 `data-turn-key` + `data-block-key` + 相对列表顶边的像素偏移。
|
||||
- 合并更早历史前记录锚点,合并后按同一 key/块身份把偏移还原,较早内容出现在上方而用户正在读的位置不动。
|
||||
- 块身份来自 `DirectChatBlock.key`(条目块是 `${回合 key}:${条目 itemId}`,本地说明块是 `messageId`),只要求**在同一回合内唯一**。
|
||||
- 不按块序号定位:`DirectProjectTurn` 的 `renderTurnProcess` 对运行中的回合把过程块**平铺**、对已结束的回合把它们折进一个 `<details>`,收口时整个回合的块序号都会后移(`[用户, 工具组1, 工具组2]` → `[用户, 执行过程, 工具组1, 工具组2, 终态正文, 终态文案]`),序号锚点会解析到隔壁块并按错误基准写 `scrollTop`,表现为一帧跳动。
|
||||
- 锚点块没有布局盒时(被折进**收起**的 `<details>`、或已从列表移除)`restoreTurnAnchor` 返回 false:调用方据此放弃这次补偿,拿当前可见位置重新起锚,不写 `scrollTop`(不回跳,也不假装对齐)。同理,读锚点时跳过没有布局盒的块——它既不是「用户正在读的位置」,也还原不出来。
|
||||
- 列表上加 `overflow-anchor: none`,关掉浏览器原生 scroll anchoring,保证只有一套补偿在跑(不做「两套补偿交替生效」)。
|
||||
- 一次加载期间(`historyLoading` 为真)所有补偿——ResizeObserver 回调、加载行挂载、历史合并——都还原同一个冻结锚点;加载结束再刷新锚点。
|
||||
- 块集合(列表里所有同时带 `data-turn-key` 与 `data-block-key` 的节点,即所有可锚定的块)在列表子元素没变时复用同一份缓存,缓存由子元素增删(与 `ResizeObserver` 重订阅共用同一个 `MutationObserver`)失效:否则一次滚动帧里读锚点、还原锚点会各查一遍整份列表,长会话下就是 O(块数) 的滚动抖动。
|
||||
|
||||
### 3. 回到底部胶囊
|
||||
|
||||
- 列表底部居中的悬浮胶囊(`sticky`),跟着滚动容器走、不随内容滚走。
|
||||
- 距底部超过 48px 时出现,文案「回到底部」;用户不跟随时来了新的终态内容就改成「有新回复 · 回到底部」。
|
||||
- 点击:平滑滚到底部 + 恢复跟随最新 + 清除「有新回复」,随后按钮自行消失。
|
||||
- 平滑滚动期间滚动位置归这次程序化滚动所有:滚动事件不再翻转「跟随最新」,布局补偿也不写 `scrollTop`(写一次就会取消动画并把画面拉回原处,表现为「点了只下去一屏、到不了底」)。滚到贴底阈值即交还控制权;用户中途用滚轮 / 触摸 / 键盘打断则立刻交还,不会卡住后续跟随。
|
||||
- 动画期间内容变高(流式正文、图片撑开)时,点击瞬间记下的 `scrollHeight` 已经不是底部:补偿不写 `scrollTop`,而是把动画目标重新对准新的底部。否则动画停在旧目标上、等不到「贴底」那次滚动事件,`programmaticScrollRef` 不会交还——跟随与布局补偿整段挂起,而 `scrollToBottom` 已把胶囊按「已贴底」隐掉,用户停在底部之上却没有任何指示与自动跟随。
|
||||
|
||||
### 4. 跟随最新与折叠展开
|
||||
|
||||
- 距底部 ≤ 48px 视为「在底部」,即跟随最新;用户手动滚回去(进入阈值)就自动恢复跟随,不需要额外开关。
|
||||
- 用 `ResizeObserver` 观察列表的直接子元素(`MutationObserver` 负责在子元素变化时重订阅)——任何高度变化:
|
||||
- 跟随时 → 贴底;
|
||||
- 不跟随时 → 还原顶部可见回合锚点(用户正在读的那条折叠头留在原位);若刚刚展开的那个块头部仍然可见,则按展开前记录的屏幕位置把它冻结,并把新展开的正文露出来:正文底边超出视口就只滚到刚好露出底边,正文比视口还高则对齐正文顶部;
|
||||
- 收起(正文高度归零)不额外滚动,只冻结头部。
|
||||
- 新一回合开始(`turnInFlight` 由假转真)时强制恢复跟随并贴底,替代原先由视图直接写 `shouldFollowLatestRef` 的做法。
|
||||
|
||||
### 5. 结构与样式
|
||||
|
||||
- 滚动所有权(列表 ref、跟随最新、ResizeObserver、锚点还原、胶囊可见性)搬进 `DirectProjectConversation` 这一层;`DirectProjectChatView` 只传加载状态与回调,不再持有 `messagesRef` / `shouldFollowLatestRef` / `handleScroll`。
|
||||
- 控制器对渲染层暴露可渲染的 `historyLoading` 与 `historyError`(外加 `retryEarlierHistory`),取代只存在于 ref 里的加载标志。
|
||||
- 新增元素全部用内联 Tailwind 工具类,不改 `styles.css`;列表本身仍保持 `message-list project-chat-message-list` 类名不变,只追加 `overflow-anchor` 工具类。
|
||||
|
||||
### 6. 换会话时复位滚动所有权
|
||||
|
||||
- 列表容器在切项目时**不会重挂载**:`DirectProjectChatView` 在 `App.tsx` 只有一处渲染、没有 `key`,内部的 `DirectProjectConversation` 也没有 `key`,而 `useConversationScroll` 的跟随最新 / `atBottom` / `hasNewReply` / 前插锚点 / 折叠头 / 程序化滚动标记只在挂载时初始化一次。控制器自己按项目重置了历史状态,滚动所有权却一直漏着。
|
||||
- 症状:在项目 A 往上滚过再切到 B——① B 的首屏不贴底,用户得自己往下滚;② B 的第一批回合会在「不跟随」分支被算成新内容,胶囊在新项目上直接显示「有新回复 · 回到底部」,可用户根本没在 B 里离开过底部。
|
||||
- 决策:把会话身份(`conversationKey`,DirectProject 传项目路径)作为**显式信号**传给 `useConversationScroll`;身份变化时复位上述滚动所有权状态、把终态指纹同步成新会话内容、再贴底。不改变组件生命周期,同一个项目重开(身份不变)也不会被当成新会话;将来策划对话复用同一个 hook 时用的是同一套信号。
|
||||
|
||||
## 备选方案与取舍
|
||||
|
||||
1. **保留按钮 + 只加自动加载**:加载中仍靠按钮做唯一反馈,且删掉按钮后失败路径没有补救入口;按钮本身与滚动自动加载重复。
|
||||
2. **在列表外面套一层 viewport 做浮层定位**:`PlanningChatView` 共用同一套容器规则,且工作台里 `.project-chat-conversation` 是 `display: block` + `height: 100%` 几何,套一层就会让 `height: 100%` 的列表塌成内容高度;改公共类会连带策划对话。改用列表内的 `sticky` 胶囊,零结构改动。
|
||||
3. **只依赖原生 CSS scroll anchoring**:前插能免费对齐,但做不到「跟随时展开要贴底」,也无法在加载期间冻结同一套锚点;因此显式补偿 + 关闭原生锚定。
|
||||
4. **展开后总是把正文滚进视口**:对正文比视口矮的折叠块会把画面大幅上移,打断正在读历史的用户;采用「跟随时贴底 / 否则冻结折叠头 + 只滚到刚好露出新展开正文的最小位移」。
|
||||
5. **平滑滚动期间照常处理滚动事件与布局补偿**:程序化滚动会被应用自己的补偿打断(第一次写 `scrollTop` 即取消动画),用户点了「回到底部」也停在半路;因此改为滚动期间冻结这两条路径。
|
||||
6. **自动重试失败的历史加载**:弱网下会反复打接口,且用户看不出到底在重试还是在挂起;改为挂起 + 内联重试行。
|
||||
7. **用 `key={projectPath}` 让 React 重建会话列表**:改动最小,但整份消息列表连同加载行的 150ms 延迟计时一起重建,而且「同一个项目重开」也会被当成新会话;改用显式身份信号复位,重建与否与「换没换会话」解耦。
|
||||
8. **锚点存块序号(`index`)而不是块身份**:序号在「回合 running→finished」时会因过程被折进 `<details>` 而整体后移,解析到隔壁块;改成稳定块身份只多一层 `data-block-key`,且块不可见仍要单独判失败,所以「失效锚点」省下的那点改动不值得留下错位。
|
||||
9. **锚点块被折进收起的 `<details>` 时把还原改成找同回合内其它可见块**:会按另一条消息的基准写 `scrollTop`,比放弃这次补偿更糟;放弃只表现为不自动对齐,不回跳。
|
||||
|
||||
## 影响
|
||||
|
||||
- 历史加载失败不再只写顶部状态行,而是落到列表里的内联错误行;顶部状态行仍保留首屏读取失败等其它用途。
|
||||
- 滚动是表现层行为,正式状态仍在后端投影与运行态事件;本 ADR 不新增领域概念。
|
||||
- 阈值(触顶 24px、贴底 48px、spinner 150ms)是可按手感调整的常量,集中放在 `components/DirectProjectConversation/conversationScrollPolicy.ts`。
|
||||
- 验收:纯函数与 jsdom 组件测试覆盖阈值、锚点还原(含收口后按块身份仍指向同一块、锚点块被折叠隐藏时放弃还原)、加载/错误行、胶囊文案与显隐;滚动观感(顶部加载圈、胶囊出现与消失、底部展开回贴、历史前插不跳、长回合收口时视口不跳、切项目后首屏贴底且不误报「有新回复」)必须真机手动验收——jsdom 没有布局。
|
||||
- 明确的后续项(不在本次范围):`PlanningChatView` 与 `App.tsx` 遗留 `message-history-more` 路径的同款改造、未读条数徽标、Playwright 端到端。
|
||||
@@ -0,0 +1,43 @@
|
||||
# 【ADR】退役AGC独立Agent Runtime与CLI执行面-2026-10-02
|
||||
|
||||
状态:已接受
|
||||
|
||||
## 背景
|
||||
|
||||
AGC(`apps/ai-game-creator-shell`)长期同时存在两套「Agent 执行」:正式对话面已经收敛到 DirectProject 单容器(Rust `agent/direct_*` + codex app-server),而 `src-tauri/src/agent/runtime_*`、`agent/prompt.rs`、`agent_native_tools.rs`、`collaboration.rs`、`delegation.rs`、`goal.rs`、`context_compaction.rs`、`isolated_agent.rs`、`tool_plan_handoff/` 这一整套自建 Runtime(runtime driver / protocol / tools / actions / state)仍在编译、测试、注册 Tauri 命令、并以 `project-supervisor` 身份承担自主构建与专业 Agent 编排。
|
||||
|
||||
这套 Runtime 的入口只剩历史沉淀:前端没有任何渲染或调用入口(`ProjectSupervisorView`、Supervisor 运行态面板与专业 Agent 对话已在 2026-09-22 前后退役),`resume/confirm/read_game_creator_agent_runtime*`、`chat_with_game_creator_agent`、`*_agent_goal`、`schedule_game_creator_agent_ready_tasks`、`start_game_creator_supervisor_runtime_task` 等命令没有现役调用方;真实链路只由 `scripts/agent-runtime-real-e2e.mjs`、`agent-runtime-steer-real-e2e.mjs`、`smoke-agent-run-local-provider.mjs`、`llm-transient-fault-proxy.mjs` 等专用 harness 与它自己的 Rust 测试覆盖。外部 Runner(`--agent-runner`)里也跟着背了一份项目 execution-owner、known-roots 和 `runner.status` / `runner.shutdown_if_idle` 的 CLI 面,但其中真正仍在服役的只有编辑器桥与 manifest relay。
|
||||
|
||||
保留它的代价持续存在:一个没有用户入口的执行栈要求每次改动都同步维护四类东西——Rust Runtime 内核与专属测试、Tauri 命令注册与 `scripts/check-config.mjs` 白名单、真实 e2e harness 与 CI 预热 job、以及多份权威文档里按「现役」描述的协议与预算。
|
||||
|
||||
## 决策
|
||||
|
||||
- 自建 Agent Runtime 执行面整体退役,按「从未存在」处理:实现、专属测试、Tauri 命令注册、构建期门禁条目、真实 e2e harness、CI job 与文档承诺一并删除,历史由 Git 保存,不保留 feature flag、双跑路径或墓碑注释。
|
||||
- Rust 删除范围:`agent/runtime_driver/`、`agent/runtime_protocol/`、`agent/runtime_tools/`、`agent/runtime_actions/`、`agent/runtime_state.rs`、`agent/runtime_adapter.rs`、`agent/prompt.rs`、`agent_native_tools.rs`、`collaboration.rs`、`delegation.rs`、`goal.rs`、`context_compaction.rs`、`isolated_agent.rs`、`provider_handoff.rs`、`provider_retry.rs`、`tool_plan_handoff/`、`user_input.rs` 及其 `src/tests/` 下的专属用例与 fixture;`agent/generation/` 只保留现役生成路径(画布 / 资源生成、prompt 上下文装载、pass artifact 落盘与 trace),`generation/run_lifecycle.rs`、`generation/role_briefs.rs`、`generation/tests.rs` 一并删除。
|
||||
- Tauri 命令退役:`control_agent_run`、`generate_local_game_draft`、`chat_with_game_creator_agent`、`chat_with_game_creator_role_agent(_stream)`、`start/read/edit/pause/resume/clear_game_creator_agent_goal`、`start_game_creator_supervisor_runtime_task`、`compact_game_creator_agent_runtime_context`、`cancel/retry/confirm_retry/confirm/reject_game_creator_agent_runtime_task`、`answer_game_creator_agent_runtime_user_input`、`read_game_creator_agent_runtime(s)`、`resume/confirm_resume_game_creator_agent_runtime_tasks`、`schedule_game_creator_agent_ready_tasks` 全部移出 `desktop.rs` 的 `generate_handler!` 与 `check-config.mjs` 白名单;`agent` 会话命令(`list/create/fork/set_active/archive_game_creator_agent_session`)继续保留。
|
||||
- CLI 退役:`--agent-run` / `--agent-enqueue` / `--agent-steer` / `--agent-resume` / `--agent-context-compact` / `--agent-goal-*` / `--agent-task` / `--agent-runner-status` / `--runner-shutdown-if-idle` 与其 CLI 解析、配置目录要求、测试一并删除;`CliCommand` 收敛为 `LlmStatus | EnvironmentCheck | PreviewServe`。`--agent-runner` 模式与 `--gui-owner-required` 保留。
|
||||
- 外部 Runner 收缩:`runner/project_owner.rs`、项目 execution-owner / known-roots 注册表、`runner.status`、read-only configure、跨启动 owner 认领与 `shutdown_if_idle` 的项目级语义全部删除;Runner 现在只剩编辑器桥 RPC(`*.editor.rpc` / `*.editor.ack` / `*.editor.mark_uncertain`)与 `runner.attach_gui_owner` + GUI owner 参与锁 / watchdog。`runner.rs` 顶部保留 `TODO(retire-runner)` 记录「等编辑器执行收进 GUI 进程后可整体退役」的规划。
|
||||
- 前端退役:删除 `agentRuntimeById` 状态与 `read_game_creator_agent_runtimes` / `resume_game_creator_agent_runtime_tasks` / `confirm_resume_game_creator_agent_runtime_tasks` 调用链、`features/agent-runtime/model.ts` 中只服务 Runtime 投影的归一化/合并/格式化函数、`app/types.ts` 的 `AgentRuntime*` / `AgentGoal*` / `GameCreatorAgentRuntimeUpdateEvent` 类型、`AgentStatusCard` 的 `runtime*` 字段与 `features/project-summary/agentPresentation.ts` 的 `projectAgentRuntimeSummaries` / `formatAgentCardRuntimeStatus`,以及 `onAgentRuntimeSummariesChange` / `activeProjectAgentRuntimeSummaries` 在 app-shell、WorkspaceLauncher、`view/project-development` 与 `App.tsx` 的透传。
|
||||
- harness 与 CI 退役:删除 `apps/ai-game-creator-shell/scripts/agent-runtime-real-e2e/`、`agent-runtime-real-e2e.mjs`、`agent-runtime-steer-real-e2e.mjs`、`smoke-agent-run-local-provider.mjs`、`llm-transient-fault-proxy.mjs` 与 `tests/llmTransientFaultProxy.test.ts`;`scripts/check-native-shells.mjs` 去掉 `agc-rust-smoke` 分组,`.gitea/workflows/project-ci.yml` 去掉 `ai-game-creator-shell-rust-smoke` job,root / App `package.json` 去掉全部 `agent-*` / `agent-runtime:*` / `agent-run:smoke` 脚本,缓存维护脚本同步去掉对应 job 名。
|
||||
|
||||
## 备选方案与取舍
|
||||
|
||||
1. **只删前端入口,保留 Rust Runtime 与命令**:命令与 Runtime 内核继续编译、测试、进白名单,等于把「没有用户入口的执行栈」永久固化,正是本次要消除的成本。
|
||||
2. **保留 Runtime 作为「本地 CLI 能力」**:`--agent-*` 与 `runner.status` 只服务真实 e2e harness,没有产品路径;保留它就要继续维护 harness、CI job 和缓存预热,收益为零。
|
||||
3. **保留兼容别名或 feature flag**:没有现役调用方、公开契约或持久化数据需要兼容,兼容层只会把死词汇表留在解析与注册层。
|
||||
4. **连带退役编辑器桥与 `--agent-runner`**:编辑器 RPC、回执确认、不确定执行 fence 与 Windows 作业对象隔离目前仍依赖独立进程托管,一次性搬进 GUI 是独立的较大重构(见 `runner.rs` 的 `TODO(retire-runner)`),不在本次范围。
|
||||
|
||||
## 影响
|
||||
|
||||
- 产品可见行为不变:正式对话、资源工作台、生成与编辑器链路本来就不经过自建 Runtime;删除后项目运行态由 DirectProject 自己的订阅与缓存(`projectResourceLiveUpdateModel` 等)持有,专业 Agent 状态卡片只从 manifest + run trace 推导。
|
||||
- AGC Rust crate 的编译面与测试面显著缩小(删除 140 个源文件),`cargo check` / 分片测试不再需要 Runtime fixture、Goal sidecar、Supervisor 协作与 tool-plan handoff 用例。
|
||||
- 术语收敛:「Runtime」在 AGC 里此后指 DirectProject / codex app-server 执行面,「Supervisor」不再是正式运行身份;后续文档与注释不得再按现役描述自建 Runtime。
|
||||
- 新增门禁边界:`scripts/check-config.mjs` 的 native-only 白名单不得再收留已删命令;退役概念不新增守卫测试或字符串钉桩,防止回归依靠「没有解析层 / 没有注册」的架构边界。
|
||||
- 保留项:AGC 会话命令、项目权限策略词汇表、DirectProject 的 `enqueue_direct_codex_turn` / `cancel_direct_codex_turn` 链路、编辑器桥(`runner.attach_gui_owner`、participant lock、watchdog)与 `--agent-runner` 模式均不受影响。
|
||||
|
||||
## 验证
|
||||
|
||||
- `cd apps/ai-game-creator-shell/src-tauri && cargo check --tests --bin genarrative-ai-game-creator-shell`、`cargo test --bin genarrative-ai-game-creator-shell runner::`。
|
||||
- `cd apps/ai-game-creator-shell && node scripts/check-config.mjs`、`npx tsc -p tsconfig.json --noEmit`。
|
||||
- `npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts` 与 `npx vitest run scripts/project-ci-workflow.test.ts`。
|
||||
- `git diff --check`、`npm run check:encoding`。
|
||||
Reference in New Issue
Block a user