Repository navigation
Background agent's row ends when the agent list says waiting #246
Description
What happens
A background general-purpose agent starts its own background shell (sleep 25; echo shell-done) and ends its turn to wait for it. About 6 s in, its Agents & shells row turns ✗ general-purpose · bg waiter · …, with waiting as the reason that the row cut. endTask also queues a ✗ general-purpose waiting toast. All this happens while the shell keeps running under it. 8 s later the agent row leaves, and the shell row moves up to the top level as ◐ $ sleep 25; …. When the shell ends, the agent runs again and finishes, but its row never comes back and no done toast comes. Live on 2.1.292 at release/0.5.0 and at v0.4.0. The live-check row "Ask Claude to start a general-purpose agent that itself runs sleep 30 in the background" in docs/testing/live-checks.md expects the agent row to stay ◐ with the shell nested under it.
Why
A tagged probe logged what $.agent.list() gave for that agent: running → waiting (as the agent's turn ended with its shell live) → running (when the shell's notification woke it) → completed. The vendored AgentStatus type already lists 'pending' | 'running' | 'waiting' | 'idle' | 'completed' | 'failed' | 'killed' at v0.4.0, and says waiting means held on background work the agent owns, and that only completed, failed and killed are ended.
applyAgentList in ctui/hooks/tasks.ts treats every word except running as an end. On waiting it calls endTask with status waiting and a toast, so the row draws ✗ with waiting as its reason. dropEnded then deletes the row 8 s later. The later running and completed find no held task (held?.kind !== 'agent'), so nothing restarts the row or raises the done toast. The shell row's parent no longer exists, so it draws at the top level.
The agent's first <task-notification> also arrives at the waiting point, with <status>completed</status> and a note that the result may be interim. applyNotification already ignores an agent's non-failed notice, so that notice plays no part in the bug.
Ask
In applyAgentList, end a held agent only on completed, failed or killed. Treat pending, waiting, idle and any other word as live: the row stays ◐ with no toast, and an ended row that reads one of them runs again, as running does today. Keep the rest of the change-only logic as it is.
Update MEMORY.md's "Task ends come from notifications; running tasks carry across /clear and /resume" to say that, from 2.1.292, a background agent reads waiting in $.agent.list() while it waits on its own background work and then reads running again. Also say that only completed, failed and killed end a row.
Test seam: ctui/tests/tasks.test.ts for applyAgentList: a running agent whose list goes waiting stays running with no toast, and the list then going running and completed gives one done toast. In ctui/tests/agents-sidebar.test.tsx, add a scenario: $.agent.spawn in the background, a background Bash tool.call with that agentId, and the world's list set to waiting. Assert the agent row stays ◐ with the shell nested under it as └, with no toast. Then set the list to running and then completed, and assert ✓ and a done toast.
Ship it from bugfix/agent-waiting-row, cut from release/0.5.0, with its pull request into release/0.5.0.
Activity
a1exk-dev commentedon Oct 10, 2026
Fixed in #254, merged into release/0.5.0.