| 1 | # Fleet and sub-agents |
| 2 | |
| 3 | > 阅读简体中文版:[zh_hans/SUBAGENTS.md](zh_hans/SUBAGENTS.md) |
| 4 | |
| 5 | Fleet manages saved models and role assignments for these same sub-agents. |
| 6 | Use `agent` for an individual assignment and `workflow` for phases with |
| 7 | dependencies and completion checks. See [Workflow authoring](WORKFLOW_AUTHORING.md) |
| 8 | for plans that use the Fleet model shortlist. |
| 9 | |
| 10 | Fleet roles are the user-facing vocabulary for delegated work: a parent |
| 11 | launches a focused `general`, `explore`, `planner`, `reviewer`, `implement`, |
| 12 | `test`, or `advisor` through `agent` and gets back an `agent_id`, declared |
| 13 | deliverables, and effective limits while the worker runs. The default receipt is |
| 14 | compact; request addressed detail when you need the transcript handle or ledger. |
| 15 | The internal runtime type is `FleetRole` (formerly |
| 16 | `SubAgentType`); the older role spellings (`worker`, `scout`, `plan`, |
| 17 | `review`, `builder`, `verifier`, `consultant`, `oracle`, …) remain accepted only as a persisted/deserialize |
| 18 | compatibility adapter during v0.9.x. New prompts and config should use fleet |
| 19 | names. |
| 20 | |
| 21 | Architecturally, sub-agents should not be a second execution substrate. The |
| 22 | durable primitive is the fleet-backed worker run described in |
| 23 | [`AGENT_RUNTIME.md`](AGENT_RUNTIME.md): retries, terminal status, receipts, |
| 24 | artifact refs, inspection, and restart behavior belong there. The |
| 25 | model-facing launcher is the single `agent` tool and detached work should |
| 26 | converge on the same lifecycle as Agent fleet. |
| 27 | |
| 28 | The current `agent` implementation delegates to the durable sub-agent runtime |
| 29 | while that cutover completes. It can still be useful for short in-session |
| 30 | delegation. Transient provider header/stream/time-out failures are retried with |
| 31 | backoff inside the child runtime before the worker is marked interrupted; if the |
| 32 | retry budget is exhausted, Codewhale preserves a checkpoint and returns a |
| 33 | continuation handle instead of leaving the parent to infer what happened. For |
| 34 | work that must survive process restarts, sleep, or remote execution, prefer |
| 35 | fleet or a Workflow-backed fleet run. |
| 36 | |
| 37 | Sub-agents inherit the parent's permitted tool registry, including `agent` |
| 38 | coordination. Spawning obeys one absolute depth ceiling: the root is depth 0, |
| 39 | its child is depth 1, and a child at `max_spawn_depth` cannot spawn again. |
| 40 | The operator default is 3, with a hard ceiling of 8. A role, saved profile, or |
| 41 | compatibility request can only narrow that ceiling. Recovery and transcript |
| 42 | forking retain the source's position and bounds; they do not buy another |
| 43 | generation. The removed `agent_open`/`agent_eval`/`agent_close` lifecycle tools |
| 44 | are absent from every registry. |
| 45 | |
| 46 | Healthy children continue after an ordinary parent response. Their completion |
| 47 | returns through the existing Engine inbox and can wake the parent for another |
| 48 | normal turn. Explicit interruption or cancellation remains authoritative. |
| 49 | `detached: true` additionally opts a subtree out of parent-turn cancellation; |
| 50 | it does not remove child budgets or the headless host's deadline. |
| 51 | |
| 52 | This doc covers roles and individual worker controls. Use `workflow` to coordinate |
| 53 | multiple assignments through the same worker runtime; see the sub-agent guidance in |
| 54 | `crates/tui/src/prompts/text.rs` (`AGENT_MODE`) and the in-line |
| 55 | tool description. |
| 56 | |
| 57 | ## Role taxonomy |
| 58 | |
| 59 | The `type` field on `agent` selects a fleet posture for the child |
| 60 | (`agent_type` is accepted as a compatibility alias). Each role is a distinct |
| 61 | stance toward the work — not just a different label. |
| 62 | |
| 63 | ## Maintainer posture |
| 64 | |
| 65 | Sub-agents help Codewhale move faster, but the parent agent still owns the |
| 66 | maintainer decision. Use children to gather evidence, review patches, and run |
| 67 | verification while keeping the community posture in |
| 68 | [`AGENT_ETHOS.md`](AGENT_ETHOS.md): issues are open intake, PR gates are |
| 69 | review-load controls, and harvested work needs clear contributor credit. |
| 70 | |
| 71 | When a child reviews community work, the parent should still inspect the PR |
| 72 | diff, linked issues, tests, and CI before merging, harvesting, closing, or |
| 73 | deferring it. A sub-agent's result is a working set, not a substitute for |
| 74 | stewardship. |
| 75 | |
| 76 | | Role | Stance | Writes? | Network? | Shell posture | Typical use | |
| 77 | |---------------|----------------------------------------|---------|----------|---------------|----------------------------------------------| |
| 78 | | `general` | flexible; do whatever the parent says | yes | yes | yes | the default; multi-step tasks | |
| 79 | | `explore` | read-only; map the relevant code fast | no | yes | read-only (net + bounded verify) | "find every call site of `Foo`; check the PR with gh" | |
| 80 | | `planner` | analyse and produce a strategy | no | yes | read-only probes | "design the migration; don't execute" | |
| 81 | | `reviewer` | read-and-grade with severity scores | no | yes | read-only (net + bounded verify) | "audit this PR for bugs" | |
| 82 | | `implement` | land a specific change with min edit | yes | yes | yes | "rewrite `bar.rs::Foo::bar` to do X" | |
| 83 | | `test` | run tests / validation, report outcome | no | yes | bounded verification (no writes) | "verify the diff with the bounded test checks; report PASS/FAIL" | |
| 84 | | `advisor` | short-lived, high-reasoning counsel | no | yes | none | "what are we missing in this design?" | |
| 85 | | `custom` | explicit narrow tool allowlist | inherits | inherits | inherits | hand-picked tools on the parent's posture | |
| 86 | |
| 87 | A role's default is what the role *intends*, and the parent's effective |
| 88 | posture is always the ceiling (a child never widens beyond its parent). |
| 89 | Read-only roles withhold **workspace writes** by intent; nothing else is |
| 90 | taken away by default — every role keeps network reads, and `custom` |
| 91 | inherits the parent's write/network/shell posture and is narrowed only by |
| 92 | its explicit tool list or the spawning call. The focused worker's header |
| 93 | states the effective posture (`scout · read-only · network · read-only |
| 94 | shell`) from the runtime's own permission snapshot. |
| 95 | |
| 96 | **Delegation moves work, never authority.** A read-only parent may delegate |
| 97 | to `implement`, but the child's effective write, network, shell, and tool |
| 98 | permissions remain within the parent's live posture. Inspection roles can use |
| 99 | the classified read-only shell surface and, where native enforcement is |
| 100 | available, the explicit read-only analysis mode described below. A different |
| 101 | role name or `read_only` flag cannot grant a shell tool the caller lacks. |
| 102 | The clamp (`ChildAuthority::clamp` in `fleet/exact.rs`) intersects every field |
| 103 | with the narrower side. Deny lists are unioned, so |
| 104 | `inherit_disallowed_tools: false` cannot drop any operator or ancestor denial. |
| 105 | Resuming a saved worker intersects its saved posture with the current caller's |
| 106 | posture again. This containment is pinned by |
| 107 | `a_read_only_parents_delegation_never_widens_authority` in |
| 108 | `crates/tui/src/fleet/exact.rs` tests. |
| 109 | |
| 110 | Inside the process, the resolved authority is one object — |
| 111 | `ChildGrant` in `crates/tui/src/worker_profile.rs`: `files` |
| 112 | (none/read/write), `shell` (none/inspect/verify/full), `network`, `desktop` |
| 113 | (never granted to a child), the named tool `surface`, the caller's explicit |
| 114 | `scope`, and remaining `spawn` depth. A role is a preset over that object |
| 115 | (`ChildGrant::for_role`); `ChildGrant::resolve` intersects it with the |
| 116 | parent-derived profile. The child's tool catalog, its dispatch refusals, and |
| 117 | its capability envelope all read the same fields — a tool that is visible is |
| 118 | callable, and a tool that is denied never appears. |
| 119 | |
| 120 | The session's **permission posture** applies inside every child exactly as |
| 121 | it applies to the parent turn: under Auto-Review the same deterministic |
| 122 | floor and one-shot model guardian decide a worker's held calls (never a |
| 123 | prompt; an unavailable guardian denies, fail closed); under Ask a held call |
| 124 | the role cannot delegate is raised as an approval prompt in the parent's |
| 125 | UI and the worker waits visibly (`waiting for user`), or is denied with the |
| 126 | reason on hosts that cannot prompt; Full Access still fails closed on the |
| 127 | non-bypassable safety floor. Each decision nobody was prompted for is a |
| 128 | one-line note in that worker's transcript (visible when it is focused) and |
| 129 | an audit-log record. See `docs/MODES.md`. |
| 130 | |
| 131 | Each role's full system prompt lives in |
| 132 | `crates/tui/src/tools/subagent/mod.rs` (search for |
| 133 | `*_AGENT_INTRO`). The prompt prefix loads automatically when the |
| 134 | child agent boots; the parent's assignment prompt becomes the first |
| 135 | turn's user message. |
| 136 | |
| 137 | ## Context forking |
| 138 | |
| 139 | `agent` starts fresh by default: the child gets its role prompt plus the |
| 140 | task you pass. Use `fork_context: true` when the child should continue from |
| 141 | the parent's current request prefix instead. (`fork_context` is not in the |
| 142 | advertised schema — it stays parse-accepted for compat callers, and |
| 143 | auto-forking for read-only roles continues unchanged.) In fork mode the runtime keeps the |
| 144 | parent prefill/prompt prefix byte-identical where available, appends a |
| 145 | structured state snapshot, then adds the sub-agent role instructions and task |
| 146 | at the tail. That preserves DeepSeek prefix-cache reuse while giving the child |
| 147 | the context needed for continuation, review, summarization, or compaction work. |
| 148 | |
| 149 | Use fresh sessions for independent exploration. Use forked sessions when the |
| 150 | task depends on decisions, files, todos, or plan state already in the parent |
| 151 | transcript. |
| 152 | |
| 153 | Forked state shows the parent's To-do snapshot — the sole Work surface, written |
| 154 | by `todo_write`. The child's `<codewhale:fork_state>` block carries the bounded |
| 155 | body rendered by `crates/tui/src/todo_snapshot.rs`, so a fork continues from the |
| 156 | parent's real progress position rather than a paraphrase. That To-do section is |
| 157 | resolved when the spawn happens, so a `todo_write` earlier in the same parent |
| 158 | turn is included. |
| 159 | |
| 160 | **The list is shown once, at that spawn, and never re-sent.** No sub-agent |
| 161 | request re-states a To-do list, and neither does a parent request. Each agent |
| 162 | keeps its own private list (#4810); what it knows about that list comes from the |
| 163 | tool results its own `todo_write` calls returned, which are ordinary messages in |
| 164 | its own transcript. A worker therefore cannot read or write a parent's or a |
| 165 | sibling's list, and a forked child cannot mutate the snapshot it was handed or |
| 166 | keep reading later parent changes. |
| 167 | |
| 168 | That same private list is what the child's in-transcript card shows. A |
| 169 | delegate card renders a bounded projection of **its own** agent's To-do — the |
| 170 | settled/total count, the in-progress item always included, up to three rows, and an |
| 171 | explicit `… +N more` when the bound elides the rest — built by |
| 172 | `card_todo_projection` from the same snapshot, priority order, and sanitizer the |
| 173 | model-facing body uses. A card only ever consumes an envelope whose `agent_id` |
| 174 | matches it, so a parent's list never appears under a child and no sibling's list |
| 175 | appears under another. An agent that has stated no work shows no To-do rows at |
| 176 | all rather than a placeholder task, and a terminal card keeps the last snapshot |
| 177 | its agent actually published. Fanout cards stay a dot grid and do not show child |
| 178 | To-do: with many workers behind one card there is no truthful place to hang a |
| 179 | single list. A child To-do appears only when the runtime already represents |
| 180 | that child as its own delegate card. |
| 181 | |
| 182 | The durable Runtime ledger (projected through fleet task status) still owns |
| 183 | lifecycle state. `update_plan` is no |
| 184 | longer reachable by a model: `model_visible()` returns `false` |
| 185 | (`crates/tui/src/tools/plan.rs:408-413`), so it is filtered out of the API tool |
| 186 | list and never appears to a child. It survives only to replay older transcripts. |
| 187 | Strategy that used to go there now goes in the response body, and lifecycle |
| 188 | state goes in `todo_write`. |
| 189 | |
| 190 | ## Worktree isolation |
| 191 | |
| 192 | For parallel edit lanes, launch the child with `worktree: true`. Codewhale |
| 193 | creates a fresh git worktree and branch for that child, runs the child from the |
| 194 | isolated checkout, and reports the resulting workspace/branch in the returned |
| 195 | session projection and worker record. By default the branch is |
| 196 | `codex/agent-<name>-<id>` and the checkout lives beside the parent repo under |
| 197 | `.codewhale-worktrees/`, so the parent checkout stays clean. |
| 198 | |
| 199 | Isolation is not write authority. A prompt-only start with no role/profile or |
| 200 | write declaration remains read-only, and read-only roles need no write scope. |
| 201 | Explicitly selected write-capable roles such as `general` and `implement` |
| 202 | inherit the parent's write ceiling and default to the workspace |
| 203 | (`write_roots: ["."]`) unless narrowed. Prefer explicit, disjoint `exact_files` |
| 204 | or `write_roots` for parallel work; `coordination_contracts` can reserve named |
| 205 | shared contracts. If only `deliverables` supplies a writer's scope, those files |
| 206 | become the exact-file scope. |
| 207 | |
| 208 | `write_authority` is optional typed narrowing: `read_only` admits no write |
| 209 | scope, `workspace_write` uses the shared checkout, and `worktree_write` |
| 210 | requires actual worktree isolation. Incompatible role/scope declarations fail |
| 211 | before admission. Active overlapping shared claims fail before mutation; a |
| 212 | real isolated worktree may proceed in parallel. A `custom` role requires |
| 213 | explicit write-capable authority to claim writes; otherwise it starts |
| 214 | read-only. |
| 215 | |
| 216 | Optional fields: |
| 217 | |
| 218 | - `worktree_branch`: exact branch to create. |
| 219 | - `worktree_base`: git ref to branch from; defaults to `HEAD`. |
| 220 | - `worktree_path`: exact checkout path. Relative paths stay under the default |
| 221 | sibling `.codewhale-worktrees/` root. |
| 222 | |
| 223 | `cwd` may be combined with `worktree`: the requested directory becomes the |
| 224 | discovery anchor the repo root (and the new checkout) is resolved from |
| 225 | (`prepare_child_workspace`). Without `worktree`, `cwd` remains the manual |
| 226 | escape hatch for an already-created directory inside the parent workspace. |
| 227 | |
| 228 | ### File deliverables and edit claims |
| 229 | |
| 230 | Put required files in `deliverables`; keep the human outcome in |
| 231 | `expected_artifact`. For example, call `agent` with: |
| 232 | |
| 233 | ```json |
| 234 | { |
| 235 | "action": "start", |
| 236 | "type": "implement", |
| 237 | "prompt": "Summarize the local routing evidence in reports/routing.md.", |
| 238 | "exact_files": ["reports/routing.md"], |
| 239 | "deliverables": ["reports/routing.md"], |
| 240 | "expected_artifact": "A concise report with source references and open gaps" |
| 241 | } |
| 242 | ``` |
| 243 | |
| 244 | At most 16 repo-relative file paths are accepted. Absolute paths, traversal, |
| 245 | repository metadata paths, and symlink traversal are refused. Completion checks |
| 246 | each file against the admitted scope and reports its path, status, and byte |
| 247 | count where available. The terminal statuses are `present`, `missing`, `empty`, |
| 248 | `not_file`, `out_of_scope`, `invalid_path`, and `unreadable`. |
| 249 | `present` means a nonempty regular file exists; it does not prove the report is |
| 250 | correct or that tests passed. |
| 251 | |
| 252 | A missing or invalid required file sets `verification.status` to |
| 253 | `deliverable_missing` with the individual verdicts. Successful file checks can |
| 254 | produce `deliverables_present`; they do not turn a child self-report into an |
| 255 | independent quality gate. The completion notice includes the actual verdicts, |
| 256 | including when a worker fails or exhausts a budget. |
| 257 | |
| 258 | Edit claims are checked separately against the spawn-time git HEAD and dirty |
| 259 | file contents. Explicit changed-file declarations can produce |
| 260 | `claim_mismatch` when a claimed file did not change, or when a successful |
| 261 | bounded write receipt changed a file the child did not declare. A peer's |
| 262 | change inside a worker's broad scope is not enough to attribute that write to |
| 263 | the worker. `path:LINE` and `path:LINE-LINE` evidence citations, including |
| 264 | sentence punctuation and Markdown links, never count as edit claims. |
| 265 | |
| 266 | ### Reading beside a writer |
| 267 | |
| 268 | Read-only tools and classifier-approved shell reads can run while a peer owns |
| 269 | a shared write claim. For arbitrary analysis code, call `bash` with explicit |
| 270 | `read_only: true`: |
| 271 | |
| 272 | ```json |
| 273 | { |
| 274 | "action": "run", |
| 275 | "read_only": true, |
| 276 | "command": "python3 -c \"import sqlite3; db = sqlite3.connect('file:cache/index.db?mode=ro', uri=True); print(db.execute('SELECT name FROM sqlite_schema').fetchall())\"" |
| 277 | } |
| 278 | ``` |
| 279 | |
| 280 | This mode requires native filesystem read-only isolation and denies network |
| 281 | access. It accepts only foreground `run` with `command`, optional `cwd`, and |
| 282 | `timeout_ms`. Background or interactive modes, stdin, sandbox escalation, and |
| 283 | external execution backends are incompatible. If native enforcement is absent |
| 284 | or cannot be prepared, the call refuses before executing the command; the flag |
| 285 | never falls back to trusting a promise that the code only reads. Existing role, |
| 286 | tool, and ancestor policy restrictions still apply. |
| 287 | |
| 288 | For a write refusal outside your own scope, `agent(action="claim", ...)` can |
| 289 | add permitted paths to your claim. It cannot take a live peer's claim. Wait for |
| 290 | that peer, choose disjoint bounded writes, or use a separate worktree for code |
| 291 | that needs writes. `action="release"` only clears claims whose owners are no |
| 292 | longer live; it is not a way to unlock another running worker's files. |
| 293 | |
| 294 | ## Delegation briefs |
| 295 | |
| 296 | The parent should pass a compact brief instead of a loose paragraph. Use the |
| 297 | structured `dependencies` and `acceptance` arrays for bounded prerequisite facts |
| 298 | and observable checks; keep the focused objective in `prompt`. Do not copy raw |
| 299 | parent reasoning or an unbounded transcript. |
| 300 | |
| 301 | Default shape for the brief — a plain sentence beats the template when the |
| 302 | delegation is trivial: |
| 303 | |
| 304 | ``` |
| 305 | QUESTION: |
| 306 | SCOPE: |
| 307 | ALREADY_KNOWN: |
| 308 | EFFORT: quick | medium | thorough |
| 309 | STOP_CONDITION: |
| 310 | OUTPUT: VERDICT, EVIDENCE, GAPS, NEXT |
| 311 | ``` |
| 312 | |
| 313 | `scout` briefs default to quick, read-only investigation (no writes, but |
| 314 | network reach and the bounded verification surface are available for real |
| 315 | scouting). A quick scout usually needs only a handful of calls: orient, |
| 316 | search, read the decisive lines, and return — but stop at decisive evidence, |
| 317 | not at a number. There is no per-agent call cap to save; the runtime budgets |
| 318 | depth and concurrency, not curiosity. Do not repeat `ALREADY_KNOWN` work |
| 319 | unless evidence contradicts it. Builder and repair-style briefs should use |
| 320 | checkpoints before scope expansion or after repeated failures. |
| 321 | |
| 322 | Good delegation prompt examples: |
| 323 | |
| 324 | ```text |
| 325 | QUESTION: Does PR #3124 introduce release-risk behavior around provider routing? |
| 326 | SCOPE: PR #3124 diff, linked issue, provider routing tests, docs/PROVIDERS.md. |
| 327 | ALREADY_KNOWN: Branch is hunter/0.8.62-glm-subagents; workspace version stays 0.8.61. |
| 328 | EFFORT: medium |
| 329 | STOP_CONDITION: Return once you have either one BLOCKER/MAJOR issue or enough evidence for no MAJOR+ issues. |
| 330 | OUTPUT: VERDICT, EVIDENCE with file:line refs or PR refs, GAPS, NEXT. |
| 331 | ``` |
| 332 | |
| 333 | ```text |
| 334 | QUESTION: Where is the child-agent prompt assembled? |
| 335 | SCOPE: crates/tui/src/prompts*, crates/tui/src/tools/subagent/*. |
| 336 | ALREADY_KNOWN: The model-facing launcher is only `agent`; do not look for removed lifecycle tools. |
| 337 | EFFORT: quick |
| 338 | STOP_CONDITION: Stop after identifying the prompt source files and the function that wraps assignment text. |
| 339 | OUTPUT: VERDICT, EVIDENCE, GAPS, NEXT. |
| 340 | ``` |
| 341 | |
| 342 | ```text |
| 343 | QUESTION: Is the focused prompt/subagent test filter valid, and what fails if not? |
| 344 | SCOPE: cargo test -p codewhale-tui --bin codewhale-tui --locked prompt; subagent filter if needed. |
| 345 | ALREADY_KNOWN: Do not fix failures; capture exact command, exit code, and first relevant assertion. |
| 346 | EFFORT: medium |
| 347 | STOP_CONDITION: Stop after one clean PASS or one reproducible failing assertion with command evidence. |
| 348 | OUTPUT: VERDICT, EVIDENCE, GAPS, NEXT. |
| 349 | ``` |
| 350 | |
| 351 | ### When to pick which role |
| 352 | |
| 353 | - **`general`** — when the task is "do this whole thing", not "go |
| 354 | look", "design", or "verify". This is the right default; reach for |
| 355 | a more specific role only when the posture matters. |
| 356 | - **`explore`** — when the parent needs evidence before deciding what |
| 357 | to do next. Scouts are cheap and fast; open 2–3 in parallel |
| 358 | for independent regions. |
| 359 | They should orient first: confirm the project root, read relevant |
| 360 | `AGENTS.md`/`README.md` guidance in unfamiliar trees, search only the |
| 361 | likely scope, and return `path:line-range` evidence instead of a narrative |
| 362 | tour. The role name to use is `explore`. |
| 363 | - **`planner`** — when the parent has an objective but no executable |
| 364 | decomposition. Planners write artifacts (`todo_write` items, |
| 365 | strategy in the response body) but don't carry them out. |
| 366 | - **`reviewer`** — when there's already a change and the parent wants |
| 367 | it graded. Reviewers run under read-only posture, so the runtime |
| 368 | refuses patch attempts — describe the fix in the finding and the |
| 369 | parent dispatches a builder when the verdict is "fix it". |
| 370 | - **`implement`** — when the change is already specified and just |
| 371 | needs to land. Builders stay tightly scoped: minimum edit, no |
| 372 | drive-by refactoring, run a quick verification before handing back. |
| 373 | - **`test`** — when the parent needs an authoritative pass/fail |
| 374 | on the test suite or other validation. The verifier posture never |
| 375 | writes — the runtime refuses fix attempts — so capture the failing |
| 376 | assertion + stack and put fix candidates under RISKS for the parent |
| 377 | to dispatch. Shell is clamped to the bounded built-in verification |
| 378 | surface: Run tests/verifiers (pass `cwd` when the checks live in a |
| 379 | subdirectory), Git fetch for remote refs, Git merge_tree for merge |
| 380 | results. The write ceiling is read-only and unbounded shell forms |
| 381 | are refused (#5186). A refused probe is reported to the parent, |
| 382 | never worked around (#6298). |
| 383 | - **`advisor`** — when the operator wants a high-leverage second opinion |
| 384 | before cheaper execution continues. Consultants read enough to ground a |
| 385 | recommendation; their grant carries no writes and no shell, so the |
| 386 | runtime refuses both. `oracle` and |
| 387 | `consultant` remain accepted only when loading older requests or persisted |
| 388 | records; new prompts, receipts, and UI use `advisor`. |
| 389 | - **`custom`** — only when the parent needs to constrain the tool |
| 390 | set explicitly. Pass the allowlist via the `allowed_tools` field |
| 391 | on legacy/internal sub-agent records; the model-facing `agent` tool keeps the |
| 392 | public schema intentionally small. |
| 393 | |
| 394 | ### Aliases |
| 395 | |
| 396 | The model can spell each role multiple ways: |
| 397 | |
| 398 | | Canonical | Aliases | |
| 399 | |---------------|------------------------------------------------------------------| |
| 400 | | `general` | `worker`, `default`, `general-purpose`, `general_purpose` | |
| 401 | | `explore` | `scout`, `explorer`, `exploration` | |
| 402 | | `planner` | `plan`, `planning`, `awaiter` | |
| 403 | | `reviewer` | `review`, `code-review`, `code_review` | |
| 404 | | `implement` | `builder`, `implementer`, `implementation` | |
| 405 | | `test` | `verifier`, `verify`, `verification`, `validator`, `tester` | |
| 406 | | `advisor` | `consultant`, `oracle` (compatibility input only) | |
| 407 | | `custom` | (none; explicit `allowed_tools` array required) | |
| 408 | |
| 409 | All matching is case-insensitive. Unknown values produce a typed |
| 410 | error listing the accepted set, so the model can self-correct on |
| 411 | the next turn. |
| 412 | |
| 413 | ## Concurrency cap |
| 414 | |
| 415 | Up to **64** sub-agents run concurrently by default (`DEFAULT_MAX_SUBAGENTS`), |
| 416 | configurable via `[subagents].max_concurrent` in `~/.codewhale/config.toml` up to |
| 417 | the hard ceiling of **128** (`MAX_SUBAGENTS`). The session admits a bounded |
| 418 | queue of up to **1024** running plus queued sub-agents by default |
| 419 | (`MAX_SUBAGENT_ADMISSION`, `crates/tui/src/config/subagent_limits.rs:21`), so a turn can |
| 420 | request broad fan-out and let the manager drain it without creating an |
| 421 | unbounded population. |
| 422 | |
| 423 | By default every admitted child may start immediately — there is no artificial |
| 424 | throttle. Request the fan-out the work actually needs and let the runtime |
| 425 | queue and drain it; the caps above are enforcement, not a reason to |
| 426 | pre-refuse valid work. If you want gentler fan-out, lower `[subagents].launch_concurrency` |
| 427 | (how many direct children start at once); children beyond that limit **queue** |
| 428 | for a launch slot rather than bursting. `launch_concurrency` defaults to the |
| 429 | resolved `max_subagents` cap. (The pre-v0.8.61 `interactive_max_launch` key is |
| 430 | still accepted as a deprecated alias; the new key wins when both are set.) |
| 431 | |
| 432 | High-fanout Workflows can tune that bounded population with `[subagents] |
| 433 | max_admitted` (aliases: `max_total`, `admission_limit`). That total ceiling |
| 434 | counts both **running** and **queued** agents, while `launch_concurrency` keeps |
| 435 | instantaneous execution bounded. Completed / failed / cancelled records persist |
| 436 | for inspection but don't occupy an admission slot. Agents that lost their |
| 437 | `task_handle` (e.g. across a process restart) also don't count against the cap. |
| 438 | |
| 439 | Provider profiles let one config stay aggressive for direct API routes while |
| 440 | keeping subscription or aggregator routes gentle. Every key under |
| 441 | `[subagents.providers.<provider>]` inherits from `[subagents]` when omitted. |
| 442 | Provider keys accept canonical names such as `deepseek`, `zai`, `openrouter`, |
| 443 | and aliases such as `glm` for Z.ai: |
| 444 | |
| 445 | ```toml |
| 446 | [subagents] |
| 447 | # Global fallback for providers without a profile. |
| 448 | max_concurrent = 20 |
| 449 | launch_concurrency = 20 |
| 450 | max_admitted = 200 |
| 451 | # Operator-selected Runtime delegation depth. The default is 3; this explicit |
| 452 | # value opts in above the default but remains below the hard ceiling of 8. |
| 453 | max_depth = 6 |
| 454 | # Omitted or zero model-step budget is unbounded. Set a positive value only |
| 455 | # when an operator deliberately wants a per-child cap. |
| 456 | default_max_steps = 0 |
| 457 | default_wall_time_secs = 1800 |
| 458 | |
| 459 | [subagents.providers.deepseek] |
| 460 | # Direct API key with room to fan out. |
| 461 | max_concurrent = 20 |
| 462 | launch_concurrency = 20 |
| 463 | max_admitted = 200 |
| 464 | |
| 465 | [subagents.providers.glm] |
| 466 | # Z.ai / GLM subscription-style route: keep pressure tight. |
| 467 | max_concurrent = 4 |
| 468 | launch_concurrency = 3 |
| 469 | max_admitted = 12 |
| 470 | max_depth = 2 |
| 471 | api_timeout_secs = 180 |
| 472 | heartbeat_timeout_secs = 240 |
| 473 | |
| 474 | [subagents.providers.openrouter] |
| 475 | max_concurrent = 5 |
| 476 | launch_concurrency = 3 |
| 477 | max_admitted = 20 |
| 478 | |
| 479 | [subagents.providers.anthropic] |
| 480 | max_concurrent = 3 |
| 481 | launch_concurrency = 2 |
| 482 | max_admitted = 12 |
| 483 | ``` |
| 484 | |
| 485 | Use `/config subagents status` to see both the global values and the active |
| 486 | provider's resolved fanout, depth, and timeout profile. |
| 487 | |
| 488 | ## Advertised agent-tool fields |
| 489 | |
| 490 | The model-facing `agent` schema exposes these controls: |
| 491 | |
| 492 | | Purpose | Fields | |
| 493 | | --- | --- | |
| 494 | | Launch and route | `action`, `prompt`, `type`, `profile`, `name`, `model`, `model_strength`, `thinking` | |
| 495 | | Scope and outputs | `worktree`, `cwd`, `write_authority`, `write_roots`, `exact_files`, `coordination_contracts`, `deliverables`, `expected_artifact` | |
| 496 | | Narrow run limits | `max_steps`, `wall_time_secs` | |
| 497 | | Coordinate and recover | `agent_id`, `agent_ids`, `all_parked`, `message`, `until`, `detached`, `resume_from` | |
| 498 | | Inspect | `detail`, `offset`, `limit` | |
| 499 | |
| 500 | `start` requires `prompt`. `message` requires a target and message; |
| 501 | `followup` requires a message and exactly one target form: `agent_id`/`name`, |
| 502 | `agent_ids`, or `all_parked: true`. `peek`, `interrupt`, and `cancel` require a |
| 503 | target. `claim` requires scope entries. These action requirements are validated |
| 504 | before execution. |
| 505 | |
| 506 | `agent(action="roster")` reports each built-in role's resolved provider, model, |
| 507 | reasoning effort, known route limits and capability provenance. It uses the |
| 508 | same resolver as execution. An explicit saved profile wins first, followed by |
| 509 | a manual role pin in the current configuration, then a unique saved member |
| 510 | pinning that semantic role. Conflicting task `model` or `model_strength` choices |
| 511 | fail before admission. For an unpinned role, per-task `model` precedes |
| 512 | `model_strength`, then inherited role defaults and the session route. |
| 513 | When a Pod is selected, the `models` rows list its exact routes in saved order. |
| 514 | Use a listed `provider/model` selector for a task on an unpinned role; the session |
| 515 | model remains allowed. Off-list choices fail with the allowed routes, and a bare |
| 516 | model shared by multiple providers requires an exact selector. Without selected |
| 517 | models, current-provider overrides and `model_strength` retain their behavior; |
| 518 | foreign-provider requests fail. These choices do not change child authority. |
| 519 | |
| 520 | The `profiles` rows expose saved members from the existing selected Fleet or |
| 521 | trusted config/personal/workspace/plugin layers, with bounded identities and the |
| 522 | same route/cost evidence. `profile="bug-hunter"` loads that member's instructions, |
| 523 | role, provider/model pin and depth limit. Conflicting type or model requests are |
| 524 | refused; explicit `thinking` overrides the saved tier. Missing providers, revoked |
| 525 | plugin authority and disabled project profiles fail before child admission. |
| 526 | Discovery never creates a profile or enrolls a model. These identity choices use |
| 527 | the existing child lifecycle; a saved profile alone does not create a continuing |
| 528 | Bot conversation or a computer lease. |
| 529 | |
| 530 | Cost classes describe current uncached text input/output rates, not the total |
| 531 | price of a future task. Missing or routing-dependent prices remain unknown; |
| 532 | subscription/local routes are labelled not money metered. Discovery makes no |
| 533 | provider request and reports reachability as unverified. |
| 534 | |
| 535 | **Parse-accepted but unadvertised (compat).** Other inputs remain accepted |
| 536 | for saved transcripts, ACP/MCP clients, fleet execution data, and |
| 537 | internal/operator compatibility. Runtime validates and intersects them with |
| 538 | live policy: |
| 539 | |
| 540 | - delegation compatibility: `max_depth`, `maxDepth`, or `max_spawn_depth`; |
| 541 | values are restricted to 0 through the Runtime hard ceiling of 8 and only |
| 542 | narrow the inherited absolute ceiling. Model-facing calls inherit depth |
| 543 | from the operator and selected profile. |
| 544 | - workspace/isolation: `workspace_policy`, `fork_context`, |
| 545 | `worktree_path`, `worktree_branch`, `worktree_base` |
| 546 | - spawn contract: `deliberate`, `dependencies`, `acceptance`, `allowed_tools` |
| 547 | - lifecycle extras: `timeout_secs` (wait), `reason` (interrupt), |
| 548 | `include_archived` (status) |
| 549 | |
| 550 | Compatibility input is not a way to widen inherited authority or remove a |
| 551 | finite budget. |
| 552 | |
| 553 | ## Child budgets (steps, wall time, tokens) |
| 554 | |
| 555 | `max_steps` and `wall_time_secs` are optional per-call limits. |
| 556 | Each can only narrow the applicable role, operator, parent, and saved-run |
| 557 | limits. Omission inherits those limits; explicit zero, null, negative, or |
| 558 | out-of-range values are rejected by the tool parser. |
| 559 | |
| 560 | `max_steps` counts model turns and accepts 1 through 2000. All roles default |
| 561 | to no model-turn cap unless an operator or ancestor supplies one; the internal |
| 562 | zero representation for that default never cancels a finite inherited cap. |
| 563 | `wall_time_secs` accepts 1 through 86400, with an operator-configurable |
| 564 | 1800-second default. It includes admission queue time, model requests, and |
| 565 | tools. The effective absolute deadline is persisted. |
| 566 | |
| 567 | For example, a focused review can request: |
| 568 | |
| 569 | ```json |
| 570 | { |
| 571 | "action": "start", |
| 572 | "type": "reviewer", |
| 573 | "prompt": "Review the parser diff and report concrete regressions.", |
| 574 | "max_steps": 12, |
| 575 | "wall_time_secs": 300 |
| 576 | } |
| 577 | ``` |
| 578 | |
| 579 | The receipt's `effective_limits` is authoritative; a request for 300 seconds |
| 580 | cannot extend a parent's earlier deadline. A continuation keeps the source's |
| 581 | remaining steps, original deadline, and token history. A new ID, role, or |
| 582 | `resume_from` fork cannot reset those bounds. |
| 583 | |
| 584 | ### Token accounting and partial results |
| 585 | |
| 586 | Token budgets were retired in 0.9.14: token usage is tracked, never |
| 587 | enforced — runs are no longer stopped by token accounting. Legacy input that |
| 588 | still carries `token_budget` parses and is ignored; `max_steps` and |
| 589 | `wall_time_secs` remain the narrowable per-call limits. |
| 590 | |
| 591 | The governor uses provider-reported input plus output tokens, not a local |
| 592 | estimate presented as a bill. Request output is capped to the remaining |
| 593 | allowance. Unknown prompt usage and requests already in flight can overshoot; |
| 594 | receipts retain the full reported usage. Missing usage remains unknown. |
| 595 | Worker records distinguish the worker's own token totals from shared |
| 596 | `budget_spent_tokens` and `budget_remaining_tokens`; do not sum a shared |
| 597 | pool once for every descendant. |
| 598 | |
| 599 | The worker reserves room for one final report inside these limits: up to 10% |
| 600 | of a token allowance (at most 8192 tokens, only when at least 1024 can be |
| 601 | reserved), one turn when the step cap permits at least two, and up to 10% of |
| 602 | wall time (at most 10 seconds). Ordinary task execution stops before using |
| 603 | that reserve. Shared scopes hold back one token reserve for the scope; |
| 604 | reporting workers atomically claim remaining headroom so siblings cannot |
| 605 | independently reuse it. Continuation never refunds measured usage or resets |
| 606 | the original deadline. |
| 607 | |
| 608 | The final reporting turn uses the worker's existing resolved provider and |
| 609 | model, with tools disabled and at most 1024 output tokens. It consolidates |
| 610 | bounded assistant notes and tool results into findings, evidence, produced |
| 611 | files, unfinished work and next steps. Estimated input cost counts against |
| 612 | its allowance. Provider transport retries remain inside the one logical |
| 613 | turn and its original wall-time deadline; no worker summary retry loop is |
| 614 | added. Token estimates are not billing receipts: unknown provider input and |
| 615 | requests already in flight can still overshoot, and actual usage is recorded. |
| 616 | |
| 617 | The outcome stays `BudgetExhausted`, even when a useful report is obtained, |
| 618 | with the specific cause, checkpoint, measured usage and normal deliverable |
| 619 | verdicts. If the allowance is too small or already spent, earlier bounded |
| 620 | usage is unknown, the provider fails, or time expires, the worker returns |
| 621 | recorded partial text and says why a model report was unavailable. |
| 622 | Known missing response usage and attempts interrupted by timeout or cancellation |
| 623 | stay recorded across continuations and shared siblings; later known usage remains a |
| 624 | subtotal and cannot restore reporting headroom in that bounded scope. |
| 625 | Cancellation wins over reporting. Missing usage stays unknown. Exhausted |
| 626 | scopes reject further spawns or continuations; a partial report is not |
| 627 | successful completion. |
| 628 | |
| 629 | ## Per-role models (#3018) |
| 630 | |
| 631 | Children can run on a different model than the parent. Structured role pins, |
| 632 | the legacy model map, and convenience keys feed one override map. Structured |
| 633 | `[subagents.roles.<role>]` entries win over `[subagents.models]`, which wins over |
| 634 | the convenience keys. Keys are case-insensitive; within the structured table, |
| 635 | a canonical role key wins over its legacy alias: |
| 636 | |
| 637 | ```toml |
| 638 | [subagents] |
| 639 | default_model = "deepseek-v4-flash" # fallback for every role |
| 640 | worker_model = "deepseek-v4-pro" # worker |
| 641 | scout_model = "deepseek-v4-flash" # scout |
| 642 | planner_model = "deepseek-v4-flash" # planner |
| 643 | reviewer_model = "deepseek-v4-pro" # reviewer |
| 644 | custom_model = "deepseek-v4-pro" # custom |
| 645 | |
| 646 | [subagents.models] |
| 647 | # Free-form role → model map; any role alias accepted by agent works. |
| 648 | builder = "deepseek-v4-pro" |
| 649 | |
| 650 | [subagents.roles.reviewer] |
| 651 | model = "deepseek/deepseek-v4-pro" |
| 652 | ``` |
| 653 | |
| 654 | These are manual pins for direct and Workflow `agent` starts. A task may restate |
| 655 | the same model or exact provider/model pair, but cannot change the pin with |
| 656 | `model` or `model_strength`. An explicit saved profile takes precedence over a |
| 657 | manual role pin. A type-only start also selects a unique saved role pin when |
| 658 | there is no manual override; ambiguous saved roles fail instead of choosing one. |
| 659 | Durable Fleet runs retain their selected member's frozen route. |
| 660 | |
| 661 | Structured role pins accept `provider/model`, preserving the configured provider's |
| 662 | exact identity and the complete model suffix. Unknown providers, empty pairs, |
| 663 | and cross-provider `auto` choices fail before admission. A bare structured model |
| 664 | inherits the session provider. For a namespaced model, qualify it explicitly, |
| 665 | for example `openrouter/deepseek/deepseek-v4-pro`. Legacy scalar and |
| 666 | `[subagents.models]` values keep their full provider-owned id, including slashes; |
| 667 | they do not change providers. |
| 668 | |
| 669 | The v0.9.x convenience keys `explorer_model`, `awaiter_model`, and |
| 670 | `review_model` remain accepted as deprecated aliases so existing config files |
| 671 | do not break. |
| 672 | |
| 673 | Model ids may be **any model the active provider accepts** — validation is |
| 674 | provider-aware and happens at spawn time, not load time. On the official |
| 675 | DeepSeek API only DeepSeek ids are accepted; every other provider passes the |
| 676 | id through to the provider API, which is the authority. A non-DeepSeek |
| 677 | example: |
| 678 | |
| 679 | ```toml |
| 680 | provider = "moonshot" |
| 681 | model = "kimi-k2.7-code" |
| 682 | |
| 683 | [subagents] |
| 684 | worker_model = "kimi-k2.6" |
| 685 | ``` |
| 686 | |
| 687 | Model ids are validated the same way when applied to a child route; an invalid |
| 688 | id on the official DeepSeek API fails the spawn with the accepted-id list |
| 689 | instead of an opaque provider 400. |
| 690 | |
| 691 | With `/model auto`, sub-agent routing is provider-aware too: providers with a |
| 692 | known big/cheap pair (DeepSeek, and the hosted DeepSeek routes on NVIDIA NIM, |
| 693 | OpenRouter, Novita, SiliconFlow, SGLang, vLLM) route between that pair; |
| 694 | providers without a known cheap tier (e.g. Ollama, Moonshot) skip the |
| 695 | network router and keep children on the session model. |
| 696 | |
| 697 | ## Per-profile provider routes (#3965) |
| 698 | |
| 699 | `[subagents.models]` changes the child model within the active provider. A slash |
| 700 | in that legacy input does not grant another provider. To pin a different provider, |
| 701 | use a structured `[subagents.roles.<role>]` declaration as above, or use a |
| 702 | fleet/AgentProfile and select it with `profile` or its unique saved role. |
| 703 | The profile's explicit `provider` + |
| 704 | `model` fields win over the parent session route; omitting `provider` preserves |
| 705 | the existing inherit behavior. |
| 706 | |
| 707 | Example: keep the parent session on DeepSeek, but run a formatter child on a |
| 708 | local LM Studio OpenAI-compatible endpoint: |
| 709 | |
| 710 | ```toml |
| 711 | # ~/.codewhale/config.toml or workspace config |
| 712 | provider = "deepseek" |
| 713 | |
| 714 | [providers.deepseek] |
| 715 | api_key = "YOUR_DEEPSEEK_KEY" |
| 716 | |
| 717 | [providers.lm-studio] |
| 718 | kind = "openai-compatible" |
| 719 | base_url = "http://127.0.0.1:1234/v1" |
| 720 | api_key = "lm-studio" |
| 721 | model = "qwen-2.5-7b" |
| 722 | ``` |
| 723 | |
| 724 | ```toml |
| 725 | # .codewhale/agents/local-formatter.toml |
| 726 | id = "local-formatter" |
| 727 | role_hint = "formatter" |
| 728 | provider = "lm-studio" |
| 729 | model = "qwen-2.5-7b" |
| 730 | reasoning_effort = "off" |
| 731 | |
| 732 | [instructions] |
| 733 | text = "Use small, local edits. Keep formatting changes mechanical." |
| 734 | ``` |
| 735 | |
| 736 | Then call `agent(profile: "local-formatter", prompt: "...")`. In-process |
| 737 | children build a client for `lm-studio`; fleet workers forward |
| 738 | `--provider lm-studio` to `codewhale exec`, which resolves the same |
| 739 | `[providers.lm-studio]` table. Unknown or unconfigured provider ids fail the |
| 740 | spawn rather than silently falling back to the parent provider. |
| 741 | |
| 742 | ## Per-step API timeout (#1806, #1808) |
| 743 | |
| 744 | Each sub-agent step wraps its DeepSeek `create_message` call in a |
| 745 | per-step timeout so a single stuck request can't pin the parent's |
| 746 | completion wakeup channel indefinitely. The default is `600` seconds. |
| 747 | A timed-out attempt is retried with exponential backoff (up to 5 |
| 748 | retries) before the step interrupts with a preserved checkpoint. |
| 749 | Long-thinking children that legitimately exceed that, for example |
| 750 | heavy plan or review work behind `agent`, can extend the timeout in |
| 751 | `~/.codewhale/config.toml`: |
| 752 | |
| 753 | ```toml |
| 754 | [subagents] |
| 755 | api_timeout_secs = 900 # 15 minutes; clamped to 1..=3600 |
| 756 | ``` |
| 757 | |
| 758 | Values are clamped to `1..=3600`. `0` and `unset` keep the `600` |
| 759 | second default. |
| 760 | |
| 761 | ## Stale-agent heartbeat (#2614) |
| 762 | |
| 763 | Running agents also track manager-visible progress. If a child stops emitting |
| 764 | progress for the heartbeat window, the manager auto-cancels it, releases its |
| 765 | sub-agent slot, and keeps the cancelled record inspectable through the returned |
| 766 | transcript handle and persisted worker record. The default is 5 minutes |
| 767 | (resolved to at least 30 seconds above `api_timeout_secs`, so 630 seconds |
| 768 | with the 600-second default API timeout): |
| 769 | |
| 770 | ```toml |
| 771 | [subagents] |
| 772 | heartbeat_timeout_secs = 300 # clamped to 30..=3600 |
| 773 | ``` |
| 774 | |
| 775 | The effective heartbeat is kept at least 30 seconds above |
| 776 | `api_timeout_secs`, so a configured long model request is not cancelled before |
| 777 | its own request timeout can fire. |
| 778 | |
| 779 | ## Lifecycle |
| 780 | |
| 781 | Each opened session produces a record that progresses through: |
| 782 | |
| 783 | ``` |
| 784 | Pending → Running → (Completed | Failed(reason) | Cancelled | Interrupted(reason) | BudgetExhausted) |
| 785 | ``` |
| 786 | |
| 787 | An explicit interrupt, exhausted provider retries, or recovery of an orphaned |
| 788 | running record can leave an `Interrupted` worker with a checkpoint. Inspect |
| 789 | `needs_continuation` and the recorded reason; use `followup` for continuable |
| 790 | work. `BudgetExhausted` includes the specific token, step, or wall-time cause; |
| 791 | continuation cannot replenish an exhausted allowance. |
| 792 | |
| 793 | `wait` observes workers. A timeout returns current outcomes and never parks, |
| 794 | cancels, or resumes them. `until: "completion"` returns when one child settles; |
| 795 | `until: "all"` joins the workers running when that call starts; |
| 796 | `until: "activity"` can return on progress. A later spawn is not silently added to an |
| 797 | earlier join. |
| 798 | |
| 799 | An ordinary parent response leaves healthy children running. The same Engine |
| 800 | turn loop consumes their completion notices and can continue the parent. |
| 801 | Headless `codewhale exec` defers a successful final receipt until its existing |
| 802 | Engine reports no live children and no queued child completions. Its original |
| 803 | wall-clock deadline still bounds that settlement, including autonomous parent |
| 804 | turns. Cancellation, deadline exhaustion, a fatal event, or a lost Engine |
| 805 | channel stops settlement and returns the appropriate interrupted or failed |
| 806 | receipt with recorded partial usage. It does not report successful child |
| 807 | completion merely because the parent's first response ended. |
| 808 | |
| 809 | ### Session boundaries (#405) |
| 810 | |
| 811 | Each `SubAgentManager` instance assigns itself a fresh `session_boot_id` on |
| 812 | construction. Every new session stamps the agent with that id; the workspace |
| 813 | state file records it for restart recovery. |
| 814 | |
| 815 | Work-bar/status projections focus on current-session agents by default. |
| 816 | Prior-session agents that are not still running are treated as archived records |
| 817 | so the model does not mistake stale work for live work. This is a |
| 818 | *prior-session* rule only: agents that finished in the CURRENT session keep |
| 819 | their work-bar rows for the rest of the session (quiet completion), and their |
| 820 | details still open from those rows. |
| 821 | |
| 822 | Records that loaded from a pre-#405 persisted state file (no |
| 823 | `session_boot_id` field) classify as prior-session because the |
| 824 | manager can't match them to the current boot. |
| 825 | |
| 826 | ## Run receipts, follow-up, and takeover |
| 827 | |
| 828 | Each compatibility sub-agent has a persisted worker record in |
| 829 | `.codewhale/state/subagents.v1.json`. The record is the current run-ledger |
| 830 | slice for sub-agent lanes until those lanes are backed directly by the fleet |
| 831 | ledger: it stores `run_id`, objective, role/model, |
| 832 | workspace/branch, lifecycle events, artifact refs, follow-up target, takeover |
| 833 | target, usage provenance, and verification provenance. |
| 834 | |
| 835 | The normal parent flow is to keep working and consume the completion event. |
| 836 | Default start and status receipts are compact; full snapshots and worker |
| 837 | records are diagnostic detail, not repeated in every response. |
| 838 | |
| 839 | ### Continue an existing worker |
| 840 | |
| 841 | `message` queues a note without waking the child. `followup` wakes a running |
| 842 | child or resumes a continuable checkpoint: |
| 843 | |
| 844 | ```json |
| 845 | {"action":"followup","agent_id":"child-previous-id","message":"Continue the assignment using the recorded evidence."} |
| 846 | ``` |
| 847 | |
| 848 | Use the returned `agent_id` for subsequent waits and messages. The receipt's |
| 849 | `from` and `to` identify the original target and its current continuation. |
| 850 | The original receipt is retained. Retrying through an old ID follows the |
| 851 | persisted continuation chain and does not create a duplicate worker. If the |
| 852 | current successor is running, the follow-up is delivered there; if it has |
| 853 | already settled and cannot continue, the response says no message was |
| 854 | delivered. Duplicate workers are prevented, but repeated messages to a running |
| 855 | worker are still repeated messages. |
| 856 | |
| 857 | For a batch, choose exactly one target form: |
| 858 | |
| 859 | ```json |
| 860 | {"action":"followup","agent_ids":["child-a","child-b"],"message":"Continue the remaining checks."} |
| 861 | ``` |
| 862 | |
| 863 | ```json |
| 864 | {"action":"followup","all_parked":true,"message":"Continue the parked assignments."} |
| 865 | ``` |
| 866 | |
| 867 | Explicit batches accept up to 32 distinct IDs. `all_parked` selects parked |
| 868 | children you control and refuses more than 32 so you can choose explicit |
| 869 | batches. Bulk responses return separate `results` and `errors`; a failing |
| 870 | target does not roll back a successful continuation. Parent/descendant control |
| 871 | checks apply to both the addressed record and its current successor. |
| 872 | |
| 873 | Use `start` with `resume_from` only to create a separate worker from a settled |
| 874 | child's transcript, for example to assign a new review. Each such start is a |
| 875 | new worker. Missing, running, or cross-workspace sources are refused; the |
| 876 | source's authority and budget bounds still apply. This is distinct from |
| 877 | continuing parked work with `followup`. |
| 878 | |
| 879 | ### Compact status and full transcript retrieval |
| 880 | |
| 881 | Unscoped `agent(action="status")` returns a session-scoped page bounded to |
| 882 | 8 KiB. `offset` and `limit` page the roster; the default and maximum limit is |
| 883 | 20. Follow `next_offset`, since the byte bound can return fewer rows than |
| 884 | requested. The model-facing roster wire uses one stable `columns` header and |
| 885 | an array of values per entry in `agents`; pair each row with the header instead |
| 886 | of reading it as an object. `null` means absent or unreported, and a measured |
| 887 | zero stays numeric `0`. The header is present even on an empty page. |
| 888 | Rows include worker and parent IDs, current depth, state, |
| 889 | elapsed time, own token total, recent activity, pending input, and continuation |
| 890 | lineage (`resumed_from` / `resumed_as`). Verification includes the verdict, |
| 891 | nonempty deliverable counts and a short warning when needed. Names, steps, |
| 892 | routes, effective limits (including maximum depth), and token breakdowns remain |
| 893 | available in the unchanged object projection when addressing one `agent_id`. |
| 894 | Aggregate usage counts each worker's own reported tokens once and reports its |
| 895 | coverage. Completion receipts additionally report measured descendant usage, |
| 896 | deduplicate continuation lineage, and distinguish unknown usage from zero. A worker's |
| 897 | `has_unreported_usage` and the descendant/subtree `unreported_usage_workers` |
| 898 | counts identify missing responses even when later responses provide a measured subtotal. |
| 899 | |
| 900 | Request one worker's detail when investigating a failure: |
| 901 | |
| 902 | ```json |
| 903 | {"action":"status","agent_id":"child-a","detail":true,"offset":0,"limit":20} |
| 904 | ``` |
| 905 | |
| 906 | Addressed `peek` also accepts `detail: true`. Detail remains bounded to 32 KiB; |
| 907 | message/event archives and deliverable verdicts are paged, and omission fields |
| 908 | identify truncated detail. Use the returned typed `transcript_handle` with |
| 909 | `handle_read` for the complete retained transcript. The handle's lookup |
| 910 | coordinates are preserved even when diagnostic prose is omitted. Unscoped |
| 911 | `detail: true` does not expand the entire roster into transcripts. |
| 912 | |
| 913 | Artifacts are symbolic refs. Treat `result_summary` as a child self-report and |
| 914 | inspect the specific `verification.status` and its evidence before relying on |
| 915 | it. `usage.status` remains `unknown` until provider usage is reported, then |
| 916 | becomes `reported` or `budget_exhausted` for a spent token scope. Neither a |
| 917 | file's `present` verdict nor a completed lifecycle state proves a test gate. |
| 918 | |
| 919 | ## Output contract |
| 920 | |
| 921 | Non-scout sub-agents end with five Markdown headings, in this order: |
| 922 | |
| 923 | ``` |
| 924 | ### SUMMARY one paragraph; what you did and what happened |
| 925 | ### EVIDENCE path:line-range citations and key findings; one bullet each |
| 926 | ### CHANGES files modified, with one-line descriptions; "None." if read-only |
| 927 | ### RISKS what could go wrong / what the parent should double-check |
| 928 | ### BLOCKERS what stopped you; "None." if you finished cleanly |
| 929 | ``` |
| 930 | |
| 931 | Use `### HEADING` lines, with `EVIDENCE` before `CHANGES`. List edited |
| 932 | repo-relative file paths under `### CHANGES`; blank lines before the bullets |
| 933 | are allowed. Begin each bullet with its file path, followed by a description; |
| 934 | quote paths containing spaces or literal trailing punctuation. The verifier |
| 935 | also accepts older explicit `CHANGES:`, |
| 936 | `Changed files:`, and `Files changed:` declarations. Evidence citations and |
| 937 | paths under `RISKS` are not declarations of edits. The five-heading prompt |
| 938 | contract is `SUBAGENT_OUTPUT_FORMAT` in |
| 939 | `crates/tui/src/prompts/text.rs`. `prompt_documents_structured_subagent_briefs` |
| 940 | in `crates/tui/src/prompts.rs` asserts every heading against it. |
| 941 | |
| 942 | Scouts are the carve-out (#5189 F5): they end with `### SUMMARY` and |
| 943 | `### EVIDENCE` only (`SUBAGENT_SCOUT_OUTPUT_FORMAT` in |
| 944 | `crates/tui/src/prompts/text.rs`). `FleetRole::system_prompt` in |
| 945 | `crates/tui/src/tools/subagent/mod.rs` injects the scout contract for |
| 946 | `FleetRole::Scout` and the five-heading contract for every other role. A |
| 947 | subagent test pins that scouts contain `## Output contract (scout)` and do |
| 948 | not contain `### BLOCKERS`. |
| 949 | |
| 950 | The parent reads `EVIDENCE` as a working set for the next turn, so |
| 951 | scouts and reviewers should be precise here. |
| 952 | |
| 953 | ## Memory and the `remember` tool (#489) |
| 954 | |
| 955 | Sub-agents share the parent's native memory store when memory is enabled |
| 956 | (`[memory] enabled = true` or `DEEPSEEK_MEMORY=on`). They can |
| 957 | append durable notes via the `remember` tool — handy for a |
| 958 | scout that discovers a project convention worth carrying across |
| 959 | sessions, or a verifier that learns "this test is flaky". |
| 960 | |
| 961 | `remember` takes a `scope` of `global` or `workspace` |
| 962 | (`crates/tui/src/tools/remember.rs:79-108`) and writes through |
| 963 | `NativeMemoryStore` to `~/.codewhale/memory/global/MEMORY.md` or |
| 964 | `~/.codewhale/memory/workspace/<id>/MEMORY.md`. Writes do not go through the |
| 965 | standard write-approval flow. The legacy single-file `memory.md` path was |
| 966 | removed in v0.9.4 (remember.rs:165); see `docs/MEMORY.md` for the full layout. |
| 967 | |
| 968 | ## Implementation notes |
| 969 | |
| 970 | - Source: `crates/tui/src/tools/subagent/mod.rs`. |
| 971 | - Persisted state: `<workspace>/.codewhale/state/subagents.v1.json`. Schema |
| 972 | version `1` (forward-compatible — new optional fields use |
| 973 | `#[serde(default)]`). |
| 974 | - Settled records normally expire after `COMPLETED_AGENT_RETENTION` |
| 975 | (default 1h), with a normal retained-record target of 256. Running / |
| 976 | starting / waiting workers and the continuation identities and budget |
| 977 | lineage needed by live work are preserved. Cleanup cannot discard an old |
| 978 | ID while its continuation is still active or erase usage history needed |
| 979 | to enforce an active scope. |
| 980 | - `SubAgentRuntime::background_runtime()` starts from `child_runtime()` but |
| 981 | replaces the turn-scoped child token with a fresh cancellation token, so |
| 982 | parent turn cancellation does not stop detached background sessions. |
| 983 | - The `is_running` check ignores agents whose `task_handle` is |
| 984 | `None`; this avoids counting persisted-but-detached records |
| 985 | toward the concurrency cap (#509). |
| 986 | - `SharedSubAgentManager` is `Arc<RwLock<...>>` — read paths use |
| 987 | read locks so `/agents` and the workbar projection don't block |
| 988 | the main loop during multi-agent fan-out (#510). |
| 989 | |
| 990 | Personal profiles use the same format at |
| 991 | `$CODEWHALE_HOME/agents/<id>.toml` (normally `~/.codewhale/agents/`). For example: |
| 992 | |
| 993 | ```toml |
| 994 | # ~/.codewhale/agents/reasoner.toml |
| 995 | base_role = "explore" |
| 996 | provider = "openrouter" |
| 997 | model = "qwen/qwen3.7-plus" |
| 998 | reasoning_effort = "high" |
| 999 | |
| 1000 | [permissions] |
| 1001 | allow_shell = false |
| 1002 | trust = false |
| 1003 | ``` |
| 1004 | |
| 1005 | Select it with `agent(action: "start", profile: "reasoner", prompt: "...")`. |
| 1006 | The provider must also be configured in `config.toml`. The receipt names the |
| 1007 | resolved profile, its personal/project origin, provider/model and effective |
| 1008 | reasoning effort. Effort is normalized to the selected model's supported tiers; |
| 1009 | an explicit `thinking` request overrides the saved preference. |
| 1010 | |
| 1011 | `allow_shell` and `trust` belong under `[permissions]`, not at the top level. |
| 1012 | A profile cannot grant `allow_shell = true`, `trust = true`, or disable approval. |
| 1013 | Use the appropriate `base_role` for the task; the parent session's live policy |
| 1014 | remains the authority ceiling. These profile fields are not a way to grant |
| 1015 | additional access. |
| 1016 | |
| 1017 | A malformed, unreadable or duplicate profile now causes an explicit selection |
| 1018 | error, including when its name matches a built-in role. It never silently |
| 1019 | substitutes a lower roster layer. Repair the file and retry; profiles are reloaded |
| 1020 | for each launch. `agent(action: "roster")` reports affected profile identities and |
| 1021 | paths in `profile_load_issues` without exposing parser excerpts. Other valid |
| 1022 | profiles remain available, and a valid project override still wins over a broken |
| 1023 | personal definition. Fleet run creation performs the same check before storing |
| 1024 | a run or launching workers. |
| 1025 |