[Desktop][Codex Multi-Agent] 子 Agent 已返回 FINAL_ANSWER 且主 turn 完成后,任务卡仍显示运行中
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 401
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
**提交人**: panwei
**客户端版本**: 0.1.31
---
## 现象
在 Cindy Desktop 使用 Codex 原生 Multi-Agent 时,子 Agent 已返回 `FINAL_ANSWER`,主 Agent 也已经输出最终结果并结束当前 turn,但对应的子 Agent 卡片仍显示“Codex / 运行中”,spinner 持续转动。
## 复现步骤
1. 新建或打开一个 Codex 会话。
2. 让 Codex 通过 collaboration 工具启动两个或多个子 Agent。
3. 等待子 Agent 完成任务并向主 Agent 返回 `FINAL_ANSWER`。
4. 等待主 Agent 汇总并输出最终回复。
5. 确认当前 turn 已结束。
6. 查看消息流中的子 Agent 卡片。
## 期望行为
- 子 Agent 返回最终结果后,对应任务卡切换为 `completed`。
- 子 Agent 失败或被终止时,分别显示 `failed` 或 `stopped`。
- 子 Agent 线程仍可复用但当前未执行任务时,不应显示为“运行中”,可以显示“已完成”或“空闲”。
- 父 turn 结束后,已完成的历史任务卡不再显示 spinner。
- 父 turn 结束后迟到的合法子任务终态仍能被接收。
- 重新加载历史会话后可以恢复准确的子任务终态。
## 实际行为
- 子 Agent 已返回最终结果,但任务卡继续显示 `running`。
- 主 turn 的 `done` 只结束主会话 streaming,没有收口残留子任务状态。
- 重新加载会话或重启客户端后,卡片通常恢复为已完成,说明残留的是实时内存状态,并非任务仍在实际执行。
- 子任务终态没有持久化,历史恢复时可能只能根据 `tool_result` 推断,无法准确区分 `completed`、`failed` 和 `stopped`。
## 初步技术分析
1. `packages/maker-core/src/agents/codex/translator.ts:1297-1363`
`collabAgentToolCall` 的 started/updated 阶段发送 `agent_task_update: running`,只有收到对应 `itemCompleted` 才发送 completed/failed。
2. 实际 Codex rollout 中只观察到 `sub_agent_activity kind=started`。子 Agent 最终结果以带 author 的 `agent_message / FINAL_ANSWER` 返回,没有对应 terminal activity,因此任务卡没有收到终态。
3. `packages/maker-shared/src/agentTask.ts:11-14`
`agent_task_update` 是 live-only 状态,不会持久化。
4. `apps/desktop/src/renderer/lib/makerChatStore.ts:2269-2313,2409-2498`
Renderer 保留 running update;普通 turn done 不会清理或对账 `taskUpdates`。
5. `apps/desktop/src/renderer/components/chat/AgentTaskCard.tsx:168-170`
卡片优先使用 `update?.status`,残留 running 会覆盖已经存在的 tool result。
6. `packages/maker-core/src/agents/codex/index.ts:4564-4567,5917-5926`
已完成 turn 的 stale-event guard 可能丢弃父 turn 完成后迟到的子任务 `itemCompleted`。
未发现 toolUseId 或 alias 合并异常。主要问题是 spawn 调用 ID 与真实 child thread/path 之间缺少完整生命周期映射。
## 建议修复方向
- 建立 spawn tool ID 与 child thread ID/agent path 的映射。
- 消费子 Agent 的最终 agent message、失败、停止和中断事件,并转换为任务终态。
- 允许已知子任务在父 turn 完成后收到迟到 terminal 事件,但不能让迟到 started/updated 重新激活任务。
- 持久化子任务终态,或在历史恢复时从权威数据重新水合。
- 不要在父 turn done 时无条件把所有子任务标记为 completed,因为确实存在跨 turn 继续运行的后台子 Agent。
## 验收标准
- 子 Agent 返回 FINAL_ANSWER 后,任务卡停止动画并显示已完成。
- 子 Agent 失败或停止时显示准确终态。
- 父 turn 完成后到达的合法终态不会被 stale-event guard 丢弃。
- 刷新或重启前后任务卡状态一致。
- 真正跨 turn 继续运行的子 Agent 不会被错误标记为完成。
---
**版本区域**: CN
**OS**: win32 x64 (10.0.19045)
**界面语言**: zh-CN
Contributor guide
Research direction
Start with the Codex lifecycle handling in packages/maker-core/src/agents/codex/translator.ts and index.ts, then trace taskUpdates through apps/desktop/src/renderer/lib/makerChatStore.ts and AgentTaskCard.tsx. Verify how agent_task_update and agent_message events are represented in packages/maker-shared/src/agentTask.ts. Done means terminal child states remain accurate after FINAL_ANSWER, late events, reloads, and genuine cross-turn execution.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- electron, typescript
- Domain
- ai, desktop
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100