-
Notifications
You must be signed in to change notification settings - Fork 915
fix(tui): show real terminal status for background agents #197
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5337ccd
d9849cc
a80e8f7
22289b4
4057e7b
e0e190b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| "@moonshot-ai/agent-core": patch | ||
| "@moonshot-ai/kimi-code": patch | ||
| --- | ||
|
|
||
| Show the real terminal status of background agents in the transcript so lost, failed, and killed ones no longer appear as completed, and include the resume agent id and recovery instructions in the failure notification so the model can resume reliably. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -174,6 +174,97 @@ export class StreamingUIController { | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Push the actual terminal status of a background agent task into the | ||
| * matching `Agent` tool call component so its snapshot phase no longer | ||
| * trusts the spawn-success ToolResult (which would otherwise label every | ||
| * terminated bg agent — including `lost` ones — as `✓ Completed`). | ||
| * | ||
| * Resolution policy: an `args.agentId` is treated as authoritative — we | ||
| * either find a card whose `getSubagentAgentId()` returns the same id | ||
| * (in-memory metadata for live foreground, parsed from the spawn-success | ||
| * `agent_id: ...` line for live backgrounded and replayed cards) or we | ||
| * skip. We deliberately do NOT fall back to description match when | ||
| * `agentId` is provided, because: | ||
| * - On resume, `applyTerminalBackgroundAgentStatuses` iterates every | ||
| * persisted terminal task, including ones whose tool calls fell | ||
| * outside the `REPLAY_TURN_LIMIT` window. A description fallback | ||
| * would let an old `lost` task stamp its status onto an unrelated | ||
| * recent Agent card that happens to share `args.description`. | ||
| * - During a live spawn / terminate race, the same card can briefly | ||
| * appear in both `_pendingToolComponents` and `transcriptContainer`, | ||
| * so a description match could double-visit the same component and | ||
| * mark itself ambiguous. agentId match short-circuits on the first | ||
| * hit and is immune. | ||
| * | ||
| * Description fallback is kept as a best-effort path only when | ||
| * `agentId` is unknown — that is, on resume of pre-PR sessions whose | ||
| * disk records pre-date `agent_id` persistence. | ||
| * | ||
| * Search scope includes both in-flight components and already-mounted | ||
| * cards (some live in `transcriptContainer` standalone, others are | ||
| * borrowed by an `AgentGroupComponent` and reachable only via | ||
| * `getToolComponents()`). | ||
| * | ||
| * Returns true iff a component was found and updated. | ||
| */ | ||
| applyBackgroundTaskTerminalStatus(args: { | ||
| agentId?: string | undefined; | ||
| description: string; | ||
| status: 'completed' | 'failed' | 'killed' | 'lost'; | ||
| /** | ||
| * Real failure message to surface on the card. Pass the `subagent.failed` | ||
| * event's `error` for live crashes — it is far more useful than the | ||
| * friendly generic the card falls back to. Omit on the resume / terminate | ||
| * path where no real error is available. | ||
| */ | ||
| errorText?: string | undefined; | ||
| }): boolean { | ||
| const useAgentIdOnly = args.agentId !== undefined; | ||
| let agentIdMatch: ToolCallComponent | undefined; | ||
| let descMatch: ToolCallComponent | undefined; | ||
| let descAmbiguous = false; | ||
| const visit = (tc: ToolCallComponent): void => { | ||
| if (agentIdMatch !== undefined) return; | ||
| if (useAgentIdOnly) { | ||
| if (tc.getSubagentAgentId() === args.agentId) agentIdMatch = tc; | ||
| return; | ||
| } | ||
| if (tc.getAgentToolDescription() !== args.description) return; | ||
| if (descMatch !== undefined) { | ||
| descAmbiguous = true; | ||
| return; | ||
| } | ||
| descMatch = tc; | ||
| }; | ||
|
|
||
| for (const tc of this._pendingToolComponents.values()) { | ||
| visit(tc); | ||
| if (agentIdMatch !== undefined) break; | ||
| } | ||
| if (agentIdMatch === undefined) { | ||
| for (const child of this.host.state.transcriptContainer.children) { | ||
| if (child instanceof ToolCallComponent) { | ||
| visit(child); | ||
|
Comment on lines
+246
to
+248
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a background Agent terminates before its spawn-success Useful? React with 👍 / 👎. |
||
| } else if (child instanceof AgentGroupComponent) { | ||
| for (const tc of child.getToolComponents()) { | ||
| visit(tc); | ||
| if (agentIdMatch !== undefined) break; | ||
| } | ||
| } | ||
| if (agentIdMatch !== undefined) break; | ||
| } | ||
| } | ||
| const target = useAgentIdOnly | ||
| ? agentIdMatch | ||
| : descAmbiguous | ||
| ? undefined | ||
| : descMatch; | ||
| if (target === undefined) return false; | ||
| target.setBackgroundTaskTerminalStatus(args.status, { errorText: args.errorText }); | ||
| return true; | ||
| } | ||
|
|
||
| /** Registers a tool call that arrived via tool.call.started. | ||
| * Clears any pending streaming state for this id, updates or creates the | ||
| * component, and returns whether the call was new (no previous entry). */ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On resume this loops over every persisted terminal background task, but
renderRecordsonly mounts the lastREPLAY_TURN_LIMITturns and persisted agent tasks often arrive withoutagentId, forcing the description fallback. If an older terminal background agent outside the replay window shares a description with the single renderedAgentcard, this call applies the old task'slost/failed/completedstatus to the unrelated recent card, so the transcript can show a wrong terminal state.Useful? React with 👍 / 👎.