Summary
When the main agent delegates a subagent with run_in_background=true, the coordinator intermittently freezes (turn hangs, no streaming, no tool activity), and background agents occasionally end as task.lost with no result. This makes the multi-agent workflow (parallel subagents) unreliable in the TUI.
Observed
- Lead calls
Agent(run_in_background=true, subagent_type=...) → tool returns a task id immediately (correct), but the lead's turn then stalls instead of continuing.
- Later, a
task.lost notification arrives for the subagent ("Background agent lost … its conversation history is preserved across session restarts") with no result ever delivered. In the same session, the subagent had produced no output before being lost.
~/.kimi-code/sessions/<wd>/session_*/agents/agent-N/wire.jsonl for the lost agent exists but the main wire shows no completion event for the task.
Environment
kimi-code 0.30.0 (Homebrew), macOS, interactive TUI, yolo mode, [background] max_running_tasks unset (default 4).
Code pointers (dist/main.mjs, 0.30.0)
Places worth profiling, from a static read:
AgentTool spawn path (this.subagentHost.spawn(...)) is awaited inline before registerTask — if spawn does MCP/skill/agent discovery (mcp.waitForInitialLoad is called in the step loop), a background launch pays that cost on the lead's turn.
backgroundManager.registerTask(...) → runTaskLifecycle(entry) is called synchronously at registration; any synchronous persistence (persistLive, outputWriteQueue) on the main loop would block the turn.
- The foreground/background split happens in
waitForForegroundRelease / formatForegroundResult; the background path returns via formatBackgroundAgentResult — but the freeze occurs after the tool result is returned, which points at the turn loop or the completion-notification/steer machinery rather than the tool itself.
task.lost is emitted by the ghost-reconcile path ("lost tasks from a prior CLI process") — suggesting the subagent task's process/heartbeat died without the manager noticing in time.
Comparison with Claude Code 2.1.220
- Claude ships a Monitor tool ("stream events from a background process") so the lead can observe a background agent without blocking; kimi has no equivalent (only TaskOutput snapshots).
- Claude delivers completion via a
task_notification (completed/failed/stopped) message type; kimi injects a synthetic user-role message that forces a new turn — which may contribute to the perceived stall when it lands mid-work.
Asks
- A way to diagnose: a debug flag/log for the background task lifecycle (spawn → register → lifecycle → completion) so we can attach a trace.
- Ideally: make
spawn non-blocking for the lead turn (defer discovery to the subagent's own loop), and surface a non-turn-stealing completion signal.
- Consider a Monitor-style streaming tool for background agents.
Happy to provide session wires privately if useful.
Summary
When the main agent delegates a subagent with
run_in_background=true, the coordinator intermittently freezes (turn hangs, no streaming, no tool activity), and background agents occasionally end astask.lostwith no result. This makes the multi-agent workflow (parallel subagents) unreliable in the TUI.Observed
Agent(run_in_background=true, subagent_type=...)→ tool returns a task id immediately (correct), but the lead's turn then stalls instead of continuing.task.lostnotification arrives for the subagent ("Background agent lost … its conversation history is preserved across session restarts") with no result ever delivered. In the same session, the subagent had produced no output before being lost.~/.kimi-code/sessions/<wd>/session_*/agents/agent-N/wire.jsonlfor the lost agent exists but the main wire shows no completion event for the task.Environment
kimi-code 0.30.0 (Homebrew), macOS, interactive TUI, yolo mode,
[background] max_running_tasksunset (default 4).Code pointers (dist/main.mjs, 0.30.0)
Places worth profiling, from a static read:
AgentToolspawn path (this.subagentHost.spawn(...)) is awaited inline beforeregisterTask— if spawn does MCP/skill/agent discovery (mcp.waitForInitialLoadis called in the step loop), a background launch pays that cost on the lead's turn.backgroundManager.registerTask(...)→runTaskLifecycle(entry)is called synchronously at registration; any synchronous persistence (persistLive,outputWriteQueue) on the main loop would block the turn.waitForForegroundRelease/formatForegroundResult; the background path returns viaformatBackgroundAgentResult— but the freeze occurs after the tool result is returned, which points at the turn loop or the completion-notification/steer machinery rather than the tool itself.task.lostis emitted by the ghost-reconcile path ("lost tasks from a prior CLI process") — suggesting the subagent task's process/heartbeat died without the manager noticing in time.Comparison with Claude Code 2.1.220
task_notification(completed/failed/stopped) message type; kimi injects a synthetic user-role message that forces a new turn — which may contribute to the perceived stall when it lands mid-work.Asks
spawnnon-blocking for the lead turn (defer discovery to the subagent's own loop), and surface a non-turn-stealing completion signal.Happy to provide session wires privately if useful.