diff --git a/docs/4-implementation/agent-tools/subagent.md b/docs/4-implementation/agent-tools/subagent.md index 35ccbe6f3..fc44daed2 100644 --- a/docs/4-implementation/agent-tools/subagent.md +++ b/docs/4-implementation/agent-tools/subagent.md @@ -357,6 +357,29 @@ The daemon reads the cap with the model list, on every `start` and `continue`, so a change applies to the next call. A cap of 0 refuses every `start` and `continue`, and the message says the cap is 0. Without a swarm value, or when the read fails, no cap applies. A failed read logs a warning naming the agent and the error, and the start proceeds: an unreachable swarm must not block subagent work. +## Run state the swarm sees + +The daemon tells the swarm whether each subagent is running, so the swarm UI can +list them without guessing from their terminal rows. The task that publishes the +terminal rows (`swarm_term`) also writes one value per subagent to the +`subagent-status` KV bucket, under the key `.` +(`swarm_wire::subagent_status`; the bucket and the controller side are in +[`controller.md`](../swarm/controller.md)). It publishes as the agent, with the +same credential as the rows, and nothing is published without a queue address. + +- A run's start writes `running`, with the model and effort it started with. A + goal run's gap between turns isn't an end, so the value stays `running`. +- While a subagent runs, the task republishes its value every 60 seconds. Only + `ts` moves, so a reader sees a daemon that died as stale after three missed + beats. +- A run's end writes `finished` with `exit`: 0 when the turn completed, none when + a signal killed it, and 1 for any other failure. A failed turn's own exit code + isn't kept, so the swarm sees "failed" and the end todo has the detail. + +The status events travel on their own unbounded channel, unlike the rows' bounded +queue: a run sends two, and a lost `finished` would leave the task heartbeating a +subagent that ended. + ## Runtime A subagent runs on its parent agent's runtime