From 25beb3ad5973b73d8e7a7fee3458e3f34c210636 Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Sat, 3 Oct 2026 11:42:37 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20cc=20=E5=9B=9E=E5=A4=8D?= =?UTF-8?q?=E5=8F=AA=E5=9C=A8=E9=87=8D=E8=BF=9B=E9=A1=B9=E7=9B=AE=E6=97=B6?= =?UTF-8?q?=E6=89=8D=E5=8F=AF=E8=A7=81=EF=BC=9A=E8=A1=A5=E4=B8=8B=E5=8F=91?= =?UTF-8?q?=20item.completed=20=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - claude_code_cli.rs 在 cc 解析成功、回复落盘之后,用同一个条目 id 下发 ThreadEvent::item_completed(ThreadItem::Message{role:"assistant"}),聊天区按事件流投影即可实时显示本轮回复 - 之前只落盘 project.jsonl、不下发条目事件,用户看到的是"用户气泡 + 本轮结束于 … · 耗时",回复要重进项目才从历史读出来 - pitfalls.md 补记第四条根因与 CDP + DOM 断言验收证据 --- .../src-tauri/src/agent/claude_code_cli.rs | 18 +++++++++++++++++- docs/project-memory/shared-memory/pitfalls.md | 5 +++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/claude_code_cli.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/claude_code_cli.rs index 4709994ba..483f42b8e 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/claude_code_cli.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/claude_code_cli.rs @@ -991,7 +991,7 @@ pub(crate) async fn direct_game_creator_claude_code_chat_at( let item = serde_json::json!({ "type": "message", "role": "assistant", - "id": item_id, + "id": item_id.clone(), "content": [{ "type": "output_text", "text": text }], }); if let Err(error) = append_direct_project_history_item_at(root, &item) { @@ -999,6 +999,22 @@ pub(crate) async fn direct_game_creator_claude_code_chat_at( direct_turn_trace("claude-parse-error"); return Err(format!("写入本项目对话历史失败:{error}")); } + // 聊天区是按 `item.completed` 事件流投影的(codex 路径在 rawResponseItem/completed + // 时下发同款事件),只落盘历史不会让本轮回复出现在界面上——重进项目才看得到。 + // 条目身份与落盘的历史条目保持同一个,重进项目按 id 去重。 + let at = crate::agent::direct_now_ms(); + crate::agent::append_thread_event( + &crate::agent::thread_id_for_project(root), + ThreadEvent::item_completed( + ThreadItem::Message { + item_id, + role: "assistant".to_string(), + text: text.clone(), + at, + }, + at, + ), + ); } Err(error) => { // 这一段必须走 stderr:AppData 日志的脱敏会把含 turnToken= 的整行替换掉, diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index f209c9f42..b9b691b1f 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -6276,7 +6276,8 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/` - **根因 1(缺终态)**:终态 `turn.completed` 的"深层出口"只在 codex app-server 那条路径里(`DirectTurnTerminalContext::write` → `complete_turn`)。cc 执行器(`direct_game_creator_claude_code_chat_at`)整轮成功返回后没有任何人写终态,于是 `thread_manager::dispatch` 里占用对象的兜底把一轮已经拿到回复的回合收成 `HostDropped`。诊断行 `agent.direct_turn.host_dropped … panicking=false` 且**没有** `agent.direct_turn.panic` 行,就是这条(不是 panic,是"没写终态就结束")。 - **根因 2(缺落盘)**:assistant 回复只存在于 SDK 事件流里。codex 路径由 `finish_direct_project_collect_history` 写进 `.agent/conversations/project.jsonl`,cc 没有对应步骤,所以即使回合收口成功,UI 也读不到回复。 - **根因 3(工具被拒)**:sidecar 用 `permissionMode: 'dontAsk'` 且没有 `allowedTools`,宿主 MCP 工具(`mcp__agc__*`)一律被直接拒绝,模型只能回"没有权限"。 -- **现行口径**:cc 成功出口由放行侧补写 `DirectTurnTerminal::completed()`(`finish_if_unfinished` 幂等,codex 已写过终态时是空操作);cc 解析成功后必须把回复按 `{"type":"message","role":"assistant","id":"direct-codex::assistant","content":[{"type":"output_text","text":…}]}` 落进项目历史,落盘失败按回合失败收口;sidecar 按 `mcp__` 前缀整体放行请求里声明的 MCP 服务器(权限策略在宿主侧执行)。 +- **根因 4(界面看不到回复)**:聊天区是按 `item.completed` 事件流投影的(codex 路径在 `rawResponseItem/completed` 时下发 `ThreadItem::Message`),只把回复落进 `project.jsonl` 不会让本轮出现在界面上——用户看到"用户气泡 + 本轮结束于 … · 耗时",回复只在重进项目时从历史读出来。 +- **现行口径**:cc 成功出口由放行侧补写 `DirectTurnTerminal::completed()`(`finish_if_unfinished` 幂等,codex 已写过终态时是空操作);cc 解析成功后必须①把回复按 `{"type":"message","role":"assistant","id":"direct-codex::assistant","content":[{"type":"output_text","text":…}]}` 落进项目历史,②用**同一个 id** 下发 `ThreadEvent::item_completed(ThreadItem::Message{role:"assistant"})`(落盘失败按回合失败收口);sidecar 按 `mcp__` 前缀整体放行请求里声明的 MCP 服务器(权限策略在宿主侧执行)。 - **诊断口径**:`agent.direct_turn.host_dropped` / `agent.direct_turn.panic` 里的令牌字段必须写 `tt=`,写 `turnToken=` 会命中脱敏标记,整行变成 ``,离线只剩"说不出原因"的 HostDropped。 -- **验证**:dev 栈里用 CDP 注入真实回合(`node %TEMP%\agc-cdp.mjs `):读文件轮 `claude-parse-done chars=108`,`.agent/conversations/project.jsonl` 出现 `direct-codex:cdp-…:assistant` 条目,回复内容与 `game/index.html` 前两行(`` / ``)逐字一致;同一日志里不再出现新的 `host_dropped`。`cargo test … -- claude_code_cli::tests direct_turn_failure::tests` 19 passed。 +- **验证**:dev 栈里用 CDP 注入真实回合(`node %TEMP%\agc-cdp.mjs `):①读文件轮 `claude-parse-done chars=108`,`.agent/conversations/project.jsonl` 出现 `direct-codex:cdp-…:assistant` 条目,回复内容与 `game/index.html` 前两行(`` / ``)逐字一致(证明宿主工具真的执行了);②聊天视图打开时注入 `只回三个字:收到了`,DOM 断言(`document.body.innerText`)同时出现用户气泡 `11:40:05`、助手回复 `收到了` 与 `本轮结束于 11:40:16 · 耗时 10.7秒`(证明 `item.completed` 实时投影生效,不必重进项目);同一日志不再出现新的 `host_dropped`。`cargo test … -- claude_code_cli::tests direct_turn_failure::tests` 19 passed。 - **关联**:`apps/ai-game-creator-shell/src-tauri/src/agent/thread_manager/dispatch.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/direct_turn_failure.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/claude_code_cli.rs`、`apps/ai-game-creator-shell/agent-sidecar/src/index.mjs`。