# Resident subagents > **V0.x breaking change:** zut no longer supports subprocess subagent state. > Previous state is ignored rather than read or migrated; remove it when you > no longer need it. Existing child jobs are never resumed. > **V0.x breaking change:** the separate `subagent_spawn`, `subagent_status`, > `subagent_stop`, and `subagent_resume` tools are replaced by one `subagent` > tool whose actions are selected by an `action` argument. > `--tools subagent_spawn` no longer selects anything; use > `--tools subagent:spawn`, or bare `--tools subagent` for every action. > Under `--no-yolo`, "always allow" is remembered per action > (`subagent:status` and `subagent:spawn` are separate grants), so approving a > read-only `status` call never pre-approves any other action. Subagents are independent `core.Agent` conversations resident in the same zut process as their parent. A child has a stable, private session identity, its own provider client and tool registry, a cancellation boundary, and an authoritative journal. Starting a child never launches another zut executable. ## Profiles Profiles are Markdown files discovered from `~/.agents/agents/*.md` and paths in `ZUT_AGENT_PROFILES`. They are inputs only; zut does not execute them. ```markdown --- name: reviewer description: Read-only code reviewer tools: [read, grep] model: openai-codex/gpt-5.6-luna thinking: high systemPromptMode: replace inheritProjectContext: false inheritSkills: false fastMode: false --- Inspect the requested scope and report evidence-backed findings. ``` `tools` is a capability boundary. An omitted value inherits the child-safe catalogue, `tools: []` grants no tools, and an explicit list replaces the default. The child-safe catalogue contains only tools offered by the parent that are also available in the resolved child runtime; parent-only scheduling and extension tools are never inherited. Names unavailable to the child (including host-only extensions) and tools denied by `subagents.allowed_tools` are omitted from an explicit list; they never grant the child additional access. `mcp` in `tools` or `subagents.allowed_tools` selects every MCP tool; under headless `--orchestrate`, workers receive MCP tools the manager-only parent does not hold (see [mcp.md](mcp.md)). `model`, `provider`, `thinking`, the prompt mode, project/skill inheritance, and `fastMode` are resolved before the child is accepted. Spawn-level model, provider, reasoning, and fast-mode values take precedence where supplied. Children are always fresh conversations: they receive the delegated task and resolved host/profile context, never the parent transcript or hidden reasoning. They cannot recursively create more children. ## Lifecycle and persistence The manager records each accepted child under its managed state root: ```text subagents// transcript.jsonl # accepted turns, finalized messages, and compaction checkpoints owner.lock # host-ownership coordination; do not modify or remove .transcript-backup-* # retained private pre-repair transcript, when repaired metadata.json # rebuildable bounded state result.json # latest outcome, final summary, artifacts patch.diff # optional worktree capture ``` Acceptance is durable before the `subagent` tool's `spawn` action reports success. Finalized user, assistant, tool-call, and tool-result messages are journaled; streamed deltas and hidden reasoning are not exposed as ordinary history. A tool call is stored before execution and reconciliation repairs incomplete call/result pairs. A context-overflow recovery appends one `child.compacted` checkpoint carrying the replacement transcript; resume starts from the newest checkpoint, while the paged history view still shows everything appended before it. All children stop when the host exits. On the next start, queued or running turns are marked `interrupted`; zut never replays their task. Resume only with an explicit new prompt through the `subagent` tool's `resume` action, the child-session composer, or `/subagents resume `. Multiple zut processes may share a state root. Each resident journal has one host owner at a time. A process that finds a child owned elsewhere reports it as such and does not inspect, recover, or modify its transcript. Resume that child from its owning host or after the owner exits. On a subsequent safe reconciliation, zut repairs only the known legacy false-recovery sequence; it leaves any ambiguous journal corruption untouched. A repair retains the pre-repair transcript as `.transcript-backup-*`; it can contain private session data and is intentionally not removed automatically. The `subagent` tool's `status` action reports `owned_elsewhere: true` when another process owns a child. The field is omitted when false. A foreign-owned child cannot be resumed, or have its history or result read, from this process. The global scheduler admits the oldest eligible accepted prompt, runs at most one turn for each child, and defaults to six concurrent turns. Set `subagents.max_concurrent` to a positive limit to override it. Queueing is unlimited by default; a positive `subagents.queue_timeout` cancels an accepted prompt that has not received a slot and records its terminal failure durably. `subagents.allowed_tools` is an allowlist for child-visible tools and `subagents.allowed_roots` limits eligible child workspaces. Resident children have no cumulative token budget and no child-only step cap: a child inherits the parent's resolved limit, including the unlimited default. A turn is bounded by the provider context window, with one compaction recovery, and by an explicit parent `--max-steps`. Removed legacy settings, including `tui_subagent_position` and `subagents.budget_ratio`, are ignored. Resident journals written by older versions are read with explicit translation (an old `budget_exhausted` outcome reconciles as failure) or, for unknown versions, left untouched on disk and excluded from automatic discovery. Incompatible journals cannot be resumed; start a new child instead. Other journal recovery errors are still reported. Compatibility notes: new journals write version 3 and carry no budget fields, baselines, or exhaustion states. Version 2 archives are read with explicit translation and their transcripts are never rewritten by translation. `result.json` keeps a deprecated `handoff` field that stays empty for new results and remains readable for archives. The `subagent` tool's `status` action no longer exposes `budget` or `budget_source` fields. ### Context reminders When a child's last-turn prompt reaches 85%, 90%, or 95% of its model context window, the runner injects a non-blocking reminder into the next provider request: ```text Context usage is at NN% of the model window. Finish the current task, report results, limits, and remaining verifications, and do not start broad new work. ``` Each band fires at most once per child runner and reminders never block a turn. A jump straight to a higher band delivers one reminder for that band, and usage that falls does not re-deliver a band. Reminders travel as developer context in the live request; the durable transcript keeps only finalized history. Usage is the last-turn prompt size (input plus cache read/write tokens) against the resolved context window; an unknown window never triggers a reminder. The per-turn gauge resets when the host restarts and when a recovery compacts the context, so neither event reports a stale percentage. The band ladder itself advances at most once per child runner: a restart starts a fresh ladder, while a compacted child is reminded again only when usage reaches a higher band. ### Context-overflow recovery A provider request that exceeds the model context window ends the turn. When the transcript holds at least four messages, the child recovers once per turn: it compacts the transcript, keeps the two most recent messages verbatim, journals the replacement as a `child.compacted` record, and continues the same turn. A compaction or persistence failure restores the pre-compaction transcript and fails the turn, and a second overflow stays terminal. Recovery never replenishes an explicit parent `--max-steps`: when the allowance is already spent, the checkpoint is still written for a later resume, but the turn is not continued. Explicit resume replays the newest checkpoint instead of the full append-only log. The history views (`/subagents logs`, `HistoryPage`, `subagent://` refs) still show the append-only log, including pre-compaction records. A build that predates checkpoint support cannot resume such a child correctly; downgrading is unsupported. The summarization request itself can be rejected when a single oversized message leaves nothing to summarize away. The turn then fails with the original provider error and no checkpoint is written, so the child keeps failing on resume until its transcript shrinks. Recovery is a best effort, not a guarantee. Retrieve the saved result without executing a model: ```json {"action":"status","agent_id":"","include_result":true} ``` Pass this to the `subagent` tool's `status` action, then reconcile the history and artifacts before repeating any side effects. The `subagent` tool's explicit `resume` action after a terminal failure continues the retained session, workspace, and cumulative cost. It does not automatically retry the task. Ordinary follow-ups continue the existing session. `required: true` makes a delegated result an obligation of the parent turn. Failed, cancelled, and interrupted required work remains unresolved until an explicit successful follow-up. Reading a saved result or accepting a recovery prompt does not satisfy the obligation. A host restart never retries it automatically. ## Delegation ownership Interactive proactive delegation makes the primary agent an orchestrator that delegates by default. Implementation, debugging, testing, review, and broad research go to workers; answers, quick lookups, edits of a few lines, and integration of worker results stay local. Blocking work is delegated too: the primary spawns it with `required: true` and yields for the completion update. Routing happens for each new request, not at process start: the reply that starts the work names each part, its owner, and the profile for each delegated part. The routing guidance is part of the prompt only when launch-time policy exposes the spawn action. Before spawning, the primary maps dependencies, runs independent parts in parallel, and gives every worker a self-contained task with a disjoint scope. For a multi-part feature it creates a feature worktree branch, then one sub-worktree per worker with `worktree` `create` and `base` set to the feature branch. It reviews each result, commits on the sub-branch, and merges it into the feature branch. It then removes merged sub-worktrees and their branches with `worktree` `cleanup`, `into` set to the feature branch and `delete_branch: true`, and checks with `list` that none are left behind. While a child is active, it owns that scope; the primary does not repeat its investigation, review, testing, or implementation. Once an `isolation: "worktree"` child succeeds, the primary may apply and verify its captured patch without redoing the task, then stop the idle child so its temporary worktree is removed. Explicit headless `--orchestrate` remains manager-only. Its primary divides the request into disjoint worker scopes, coordinates active children, and yields rather than implementing or reproducing delegated work. Children do not inherit the parent's strict orchestrator role. Headless orchestration waits for every accepted child before continuing its completion wave; `required: true` additionally creates a durable obligation. When proactive interactive delegation cannot spawn because of launch-time policy, the primary continues locally; a strict headless orchestrator instead reports the missing required capability. ## Tools and slash commands The model-facing tool keeps one logical name and five actions, selected by its `action` argument. Launch-time gating is per action: `--tools subagent` grants every action, `--tools subagent:` grants one, and `--no-tools` disables all of them. - `subagent` accepts an `action` of `spawn`, `status`, `stop`, `resume`, or `interrupt`. - `spawn` accepts `task`, optional `agent`, `model` and `provider`, `reasoning`, `fast_mode`, `required`, `wait`, and `isolation` (`shared` or `worktree`). `wait` is an explicit whole-second value from 1 through 300; when omitted, spawning returns immediately. A timed-out wait leaves the accepted child active, whether it is queued or running. Do not retry that task until it reaches a terminal failure, cancellation, or interruption. It returns a logical `subagent://` reference. - `status` accepts optional `agent_id` and `include_result`, and returns bounded current state for one child or the current set immediately. With `agent_id` and `include_result: true`, it also reads the saved terminal result without model execution. Without this option it remains metadata-only; foreign-owned results cannot be read. Result-read errors distinguish foreign ownership, a missing saved result, and permission denial without exposing filesystem paths or saved content. With `agent_id`, a running child also includes `activity`: a `phase` (`waiting_for_model`, `writing`, `tools`, or `working`), the tail of its visible text, and its active tools with truncated arguments. Hidden reasoning is never exposed. `watch` (1–60 seconds, requires `agent_id`) observes that activity and returns a `timeline` of phase and tool changes. It returns early when the child stops running. Use it to inspect progress, not to wait for completion. `pending_followups` counts follow-ups the child has accepted but not read yet. - `stop` accepts `agent_id` and stops one live child. - `resume` accepts `agent_id`, `prompt`, optional `mode`, and optional `wait`. `prompt` is an explicit follow-up prompt for an existing child. Do not use `resume` solely to wait for existing work or ask for its result again: yield for the automatic completion update. `queue` schedules another turn and produces another completion. After a terminal failure, inspect the saved result before resuming; resume continues the retained session. `mode` controls how a follow-up reaches a running child: - `steer` (default) injects it into the current turn at the next tool or model boundary, like typing while the main agent works. It adds no new turn and no extra completion. - `queue` runs it as a separate turn after the current one. An idle or stopped child starts a new turn in either mode, and a steer never jumps ahead of already queued turns. The response's `delivery` is `steered` or `queued`. `wait` uses the same 1–300 second bound as `spawn` and waits for the turn that answers the follow-up, returning its outcome or reporting the timeout while the child stays active. A returned outcome can be `dropped` when a queued follow-up never started. When the host accepts the completion report, the summary and `undelivered` steers arrive in the completion update; otherwise the outcome includes them. - `interrupt` accepts `agent_id` and cancels only the child's running turn. Unlike `stop`, the child stays live with its transcript, so a later `resume` such as "report what you have" continues with full context. Queued follow-ups are dropped. It fails when no turn is running. When a turn ends before the child reads a steered follow-up, for example after `stop`, `interrupt`, or a failure, the completion update lists it as `undelivered`. Queued follow-ups that never started are reported as `dropped`. Neither appears as an interrupted task. Child execution started by the `subagent` tool's `spawn` or `resume` action is asynchronous unless it receives an explicit `wait` value. For an unwaited call, completion arrives through the host’s typed completion update; the `subagent` tool's `status` action returns immediately and does not wait for completion. Do not use sleep loops, repeated status calls, journal files, or terminal UI inspection as a completion signal. When the host accepts a completion report, a completed `spawn` or `resume` wait returns terminal status and `report_delivery: host_update`; the full report arrives only through the completion update, not again in the tool result. If no host observer accepts the report, the tool result includes it inline. An expired wait does not suppress later completion delivery. Cancelling the parent turn discards pending completion updates; cancelling only a tool wait leaves host tracking intact. In interactive mode, a result received while the primary is busy enters the visible **sliding in** queue and reaches the model at its next safe boundary, without interrupting a model request or tool call and without waiting for other children. Results already queued are not repeated when the parent turn ends. Undelivered results remain queued after a provider or compaction error for the next turn; they do not automatically retry the failed request. Explicit cancellation clears pending queue entries. The queue is in memory, while the child’s saved result remains available through the `subagent` tool's `status` action. When the primary is idle, each child completion immediately starts the next parent turn instead of waiting for its siblings. The host update names the finished child and lists sibling agent IDs that are still running. A completion that arrives while that parent turn is active slides into its queue. Headless orchestration still waits for every accepted child before its next parent turn. Successful completions include the final visible assistant summary, capped at 256 KiB; open the child session for the complete durable transcript. `/subagents` opens the resident-child list, ordered by the latest state change. Each row leads with the profile (or model), shows a human terminal state such as `completed`, a relative update time, and a short ID. Press Down from the main composer to open this list without clearing a draft when a child exists. Use arrows and Enter to open a child session; `/subagents new ` creates a child; `/subagents logs ` opens its history; `/subagents result ` shows the bounded final summary; `kill ` stops it; and `resume ` continues it. A prompt sent from the child composer or `resume` to a running child steers its current turn. The child session uses the same transcript renderer as the main session. Recent history is bounded and loaded asynchronously; PgUp asks for an older page. Up/Down preserves the current reading position, and PgDn returns to the live tail after unread updates accumulate. The screen combines durable finalized history with an immutable in-memory projection of the active turn. Enter submits the local composer only after the manager durably accepts it; Esc closes the session. History may contain task text, tool arguments, tool results, and source-derived output, so treat it as private local session data. ## Worktrees and references Shared children use the host working directory. With `isolation: "worktree"`, zut prepares a detached Git worktree and captures a patch and changed-file list on successful completion or non-cancelled failure. Failed turns retain partial work in the worktree, along with any captured patch and changed-file list. zut never applies a captured patch automatically. Failed or interrupted worktrees are retained for inspection; an idle successful worktree is cleaned when its child is explicitly stopped. Use the `subagent` tool's `status` action with `agent_id` and `include_result: true` to retrieve the saved result, `changed_files`, and `patch_ref` when a patch was captured. The patch bytes are stored in `subagents//patch.diff` under the managed state root shown above; logical references are identifiers, not filesystem paths. Inspect the retained worktree and patch before resuming or applying partial changes. New terminal journal records also save the capture metadata, so reconciliation can restore it if `result.json` is missing or corrupt. Older records remain readable, but cannot reconstruct artifact metadata they never recorded. Logical references avoid exposing runtime paths: ```text subagent:// subagent:///history subagent:///result subagent:///patch ``` ## Provider caching Each main and child conversation sends a stable logical session ID and a new turn ID per accepted prompt to the provider layer. Provider adapters decide whether and how to use them: OpenAI/Codex translates a supported stable session into its cache key and routing header, while other adapters preserve deterministic request input and use only cache controls their provider/model/endpoint supports. An OpenAI-compatible wire shape is not itself evidence that a cache extension or transport feature is available. Models continue through their declared provider transport; a transport change must not change the logical session or turn identity used by the provider request.