You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Status: 子代理会话异常残留显示为绿色——harness 被强制终止后无法区分正常完成与异常残留
现象
DSH harness 曾被手动强制终止(并行运行约 18 个 agent 时出现死循环卡死,用户手动杀进程),随后重新启动。
重启后,GUI 中子代理目录里的部分子代理会话没有正常结束,却显示为绿色(
done),与真正正常完成的会话视觉上完全无法区分。用户无法分辨"真正完成"与"异常残留(孤儿/仅存档)"。ready态(定义为:仅存在于存储中、可续接、非终态、不是等待收集的结果),1 个 running。环境
deepseek-ai/deepseek-harness(idR_kgDOT3T1gw)47f943859b(Merge pull request #2519 from deepseek-harness/feat/npm-public)根因链(三问三答)
1) 会话持久化是否记录"异常中断"标记?—— 记录,但只落在会话日志。
持久层在加载/准备一个"冷"(已存档)会话、且其最后一个 turn 未闭合时,会合成并持久提交
turn/end {kind: 'interrupted'}:packages/core/session/src/repair.ts:131——interruptedTurnClosers()push 出一个{ type: 'turn/end', …, data: { turn, reason: { kind: 'interrupted' } } }。packages/session/session-persistence/src/coordinator.ts:944-945——commitPrepared()在存在 closers 时调用backend.commitRepair(...)将这组合成事件落盘。所以强制 kill 后,孤儿子代理的会话日志里确实存在
turn/end {interrupted}标记。2) 重启/恢复时孤儿会话如何加载与显示?—— 加载成
inactive,interrupted标记从不被读取。ctx.sessions为空,孤儿会话是"冷"候选,走list-children.ts的持久化折叠路径。packages/subagent/subagent/src/list-children.ts:376/:408—— 冷子代理一律activity: 'inactive';:274live 才'running'。childRow()(:412-435)只接收activity: 'running' | 'inactive',从不检查该子代理日志尾部的turn/end {interrupted|aborted|completed}。SubagentListEntry的 JSDoc(:49-55)明确:store snapshot activity "inactivemeans it exists only in persistence. Neither encodes a durable outcome"。即:暴露给 GUI 的状态枚举只有
running/inactive(服务端)/ready/idle/running(模型端list_agents,tool-subagent-control/src/list-agents.ts:59-63),没有interrupted/aborted/completed。孤儿会话的interrupted标记停在日志里,到不了状态枚举。3) UI 颜色映射是否区分终态与孤儿态?—— 不区分;但同仓已有正确范例。
packages/client/ui-subagent/src/client/SubagentCatalogAction.tsx:348:done(绿)。:202(加载占位)、:586(触发点)同理。packages/client/ui-primitives/src/StateDot.module.css—done→绿:33-35,ongoing→蓝:49-52,error→红:41-43(目录中从未使用)。packages/client/ui-workflow-run/src/client/WorkflowRunPanel.tsx:33-43:running→ongoing、completed→done、failed→error、cancelled/interrupted→warning。packages/client/ui-workflow-run/src/client/workflow-definition.ts:12:WorkflowRunStatus = 'running' | 'completed' | 'failed' | 'cancelled' | 'interrupted',由statusFromStopReason(:63)驱动。即"中断/失败应有明显区分色"的基础设施已经存在,只是没有被接入子代理状态。
根因定级
SubagentListEntry.activity只有running/inactive,不含终态结局(list-children.ts:49-55、:274/376/408/412-435)——孤儿/中断/失败统一塌缩为inactive。这是最底层成因(数据契约缺陷),现象上"重启后孤儿会话没有被打上任何中断标记"。done(SubagentCatalogAction.tsx:348)——孤儿与正常完成同色。turn/end {interrupted}(repair.ts:131、coordinator.ts:944-945),但list-children从不读取它;客户端manager.ts:930又把任何 disengage 折叠为inactive。(三者是同一根因的三个层面:状态空间定义 → UI 取色分支 → 中断信息传播。与 workflow-run 面板的成熟行为形成直接反差。)
期望行为
done(现状保留)。ongoing(现状保留)。interrupted/cancelled用 amberwarning或独立灰色存档色;failed用红error。ready(仅存档、可续接、非终态)→ 灰色/存档色,不能与"完成"同色。建议
SubagentListEntry(或新增subagent/outcome类 projection)上暴露running/idle/ready/completed/failed/interrupted,来源直接读子代理日志尾部的turn/end(kind: completed/max-tokens/interrupted/aborted/error/disposed;可复用sdkStopReason/subagent-dsh-sdk的终态映射惯例)。SubagentCatalogAction.tsx:348(及:202、:586)由二分running/inactive扩展为多态取色,把error、warning真正路由到StateDot;与WorkflowRunPanel.tsx:33-43对齐为统一规则。list-children.ts的childRow(:412-435)在冷折叠时读取事件尾部的turn/endreason,把interrupted映射为可表达的状态;客户端manager.ts:930保留失败/中断通道,避免 disengage 即塌缩为inactive。ui-subagent/README.md:33("The catalog has no durable outcome"已知限制)与组件级快照/单测,覆盖各结局颜色。未确认项
pnpm run test:gui/ 实机落盘复现(只读调研,不改动)。commitRepair(从而真正落盘interrupted),还是仅在用户打开该会话时加载修复"——这一时序细节未在本次只读调查中逐一行验证(影响:重启后立刻展示时interrupted标记是否已经落盘并由list-children可见)。ready/孤儿视觉方案(灰 vs amber vs 独立徽标)由维护者/设计定夺。All reactions