docs(subagents): clarify detached session lifecycle

This commit is contained in:
Hunter Bown
2026-05-21 00:03:17 +08:00
parent 0e222ed0bd
commit f8aa5b95e0
4 changed files with 53 additions and 44 deletions
+29 -25
View File
@@ -3,9 +3,10 @@
Sub-agents are persistent background instances of the agent loop. The parent
opens one with a focused task, gets back an `agent_id` and session name
immediately, and continues working while the sub-agent runs to completion.
Sub-agents inherit the parent's tool registry by default and run with
`CancellationToken::child_token()`, so cancelling the parent cancels every
descendant.
Sub-agents inherit the parent's tool registry by default. `agent_open`
launches them as detached background work: cancelling the parent turn stops the
parent wait/eval path, but it does not kill already-opened child sessions. Use
`agent_close` to cancel a running child explicitly.
This doc covers the role taxonomy. The active orchestration surface is
`agent_open`, `agent_eval`, and `agent_close`; see `prompts/base.md`
@@ -17,15 +18,15 @@ The `type` field on `agent_open` selects a system-prompt posture for the child
(`agent_type` is accepted as a compatibility alias). Each role is a distinct
stance toward the work — not just a different label.
| Role | Stance | Writes? | Runs shell? | Typical use |
|---------------|----------------------------------------|---------|-------------|----------------------------------------------|
| `general` | flexible; do whatever the parent says | yes | yes | the default; multi-step tasks |
| `explore` | read-only; map the relevant code fast | no | yes (read) | "find every call site of `Foo`" |
| `plan` | analyse and produce a strategy | minimal | minimal | "design the migration; don't execute" |
| `review` | read-and-grade with severity scores | no | no | "audit this PR for bugs" |
| `implementer` | land a specific change with min edit | yes | yes | "rewrite `bar.rs::Foo::bar` to do X" |
| `verifier` | run tests / validation, report outcome | no | yes (test) | "run cargo test --workspace, report" |
| `custom` | explicit narrow tool allowlist | depends | depends | locked-down dispatch with hand-picked tools |
| Role | Stance | Writes? | Shell posture | Typical use |
|---------------|----------------------------------------|---------|---------------|----------------------------------------------|
| `general` | flexible; do whatever the parent says | yes | yes | the default; multi-step tasks |
| `explore` | read-only; map the relevant code fast | no | read-only | "find every call site of `Foo`" |
| `plan` | analyse and produce a strategy | minimal | minimal | "design the migration; don't execute" |
| `review` | read-and-grade with severity scores | no | read-only | "audit this PR for bugs" |
| `implementer` | land a specific change with min edit | yes | yes | "rewrite `bar.rs::Foo::bar` to do X" |
| `verifier` | run tests / validation, report outcome | no | test-focused | "run cargo test --workspace, report" |
| `custom` | explicit narrow tool allowlist | depends | depends | locked-down dispatch with hand-picked tools |
Each role's full system prompt lives in
`crates/tui/src/tools/subagent/mod.rs` (search for
@@ -100,8 +101,9 @@ the next turn.
The dispatcher caps concurrent sub-agents at 10 by default
(configurable via `[subagents].max_concurrent` in `~/.deepseek/config.toml`,
hard ceiling 20). When the parent hits the cap, `agent_open` returns
an error with the cap value; the parent should use `agent_eval` to wait for
completion or `agent_close` to free a slot before retrying.
an error with the cap value; the parent should use `agent_eval` to wait for a
running agent to complete, or `agent_close` to cancel a running agent, before
retrying.
The cap counts only **running** agents — completed / failed /
cancelled records persist for inspection but don't occupy a slot.
@@ -116,17 +118,16 @@ Each opened session produces a record that progresses through:
Pending → Running → (Completed | Failed(reason) | Cancelled | Interrupted(reason))
```
`Interrupted` fires when the manager detects a `Running` agent
whose task handle is gone — typically after a process restart that
loaded the agent from `~/.deepseek/subagents.v1.json`. The parent
can open a replacement session with the same assignment or treat it as a
terminal state.
`Interrupted` fires when the manager detects a `Running` agent whose task
handle is gone — typically after a process restart that loaded the workspace's
persisted state from `.deepseek/state/subagents.v1.json`. The parent can open a
replacement session with the same assignment or treat it as a terminal state.
### Session boundaries (#405)
Each `SubAgentManager` instance assigns itself a fresh
`session_boot_id` on construction. Every new session stamps the agent
with that id; the persisted state file carries it across restarts.
Each `SubAgentManager` instance assigns itself a fresh `session_boot_id` on
construction. Every new session stamps the agent with that id; the workspace
state file records it for restart recovery.
`agent_eval` and the sidebar/status projections focus on current-session
agents by default. Prior-session agents that are not still running are treated
@@ -166,10 +167,13 @@ don't go through the standard write-approval flow.
## Implementation notes
- Source: `crates/tui/src/tools/subagent/mod.rs` (about 3500 LOC).
- Persisted state: `~/.deepseek/subagents.v1.json`. Schema version
`1` (forward-compatible — new optional fields use
- Source: `crates/tui/src/tools/subagent/mod.rs`.
- Persisted state: `<workspace>/.deepseek/state/subagents.v1.json`. Schema
version `1` (forward-compatible — new optional fields use
`#[serde(default)]`).
- `SubAgentRuntime::background_runtime()` starts from `child_runtime()` but
replaces the turn-scoped child token with a fresh cancellation token, so
parent turn cancellation does not stop detached background sessions.
- The `is_running` check ignores agents whose `task_handle` is
`None`; this avoids counting persisted-but-detached records
toward the concurrency cap (#509).