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.
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.
---
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. 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:
subagents/<child-id>/
transcript.jsonl # authoritative accepted turns and finalized messages
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 or partial handoff, artifacts
patch.diff # optional worktree capture
Acceptance is durable before subagent_spawn 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.
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 subagent_resume, the child-session composer,
or /subagents resume <id> <prompt>.
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.
subagent_status 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. Each child receives
a cumulative weighted-token budget equal to its resolved model's context window.
Removed legacy settings, including tui_subagent_position and
subagents.budget_ratio, are ignored. Resident journals created before this
budget policy are incompatible: zut leaves them untouched on disk and excludes
them from automatic discovery without reporting an error at every startup.
They cannot be resumed; start a new child instead. Other journal recovery
errors are still reported.
Budget accounting charges uncached input, cache writes, and output fully; cache
reads count at 25%. It is separate from active context-window usage and survives
follow-up turns and compaction. The TUI shows the child's current budget usage,
but does not alter its instructions, tools, or output cap before exhaustion.
Providers report exact input usage only after a response, so the terminal
response can make displayed usage exceed 100%; no later request is sent in that
turn. If the worker still needs another model step, it reports
budget_exhausted, not successful idle work. A final answer that completes on
the response crossing the limit remains successful and reports completed.
Budget exhaustion publishes a terminal completion with a host-generated partial
handoff, capped at 16 KiB. It includes recent visible progress messages, tool
names and error flags, workspace and history references, and recovery guidance.
Raw tool arguments and output are omitted from this automatic handoff because
they may contain secrets; they remain retrievable in the child's durable history.
These are observations, not a claim that the task or verification finished;
older or truncated details also remain in the durable transcript. Worktree
changes are captured even on budget exhaustion, and the worktree is retained
for recovery. Shared-workspace changes are not rolled back.
Retrieve the saved result without executing a model:
{"agent_id":"<child-id>","include_result":true}
Pass this to subagent_status, then reconcile the history and artifacts before
repeating any side effects. Explicit subagent_resume after terminal exhaustion
opens a fresh allowance of the same size, retaining the session, workspace,
and cumulative cost. This permits additional spending; it does not enlarge the
configured limit or automatically retry the task. The allowance baseline is
journaled and survives restart. Ordinary non-exhausted follow-ups continue using
the existing allowance.
required: true makes a delegated result an obligation of the parent turn.
Failed, cancelled, interrupted, and budget-exhausted required work remains
unresolved until an explicit successful follow-up. Reading a partial handoff or
accepting a recovery prompt does not satisfy the obligation. A host restart never
retries it automatically.
Delegation ownership
Interactive proactive delegation keeps the primary agent on the critical path.
Before spawning, it selects useful local work and delegates only a concrete,
bounded sidecar with a non-overlapping question, responsibility, package, or
file set. Immediate blockers and tightly coupled work stay local. While a child
is active, it owns that scope; the primary continues only its preselected
independent work instead of repeating the investigation, review, testing, or
implementation. If an explicit user request or active workflow hands blocking
work to a child, the primary yields for the completion update. Once a worktree
child succeeds, the primary may apply and verify its captured patch without
redoing the delegated task.
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.
The model-facing tools retain their logical names:
subagent_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://<id> reference.
subagent_status 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 or partial handoff 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.
subagent_stop stops one live child.
subagent_resume accepts an explicit follow-up prompt for an existing child.
After terminal budget exhaustion, this grants a fresh same-size allowance
while preserving progress. Inspect the saved handoff before resuming.
Child execution started by subagent_spawn is asynchronous unless it receives
an explicit wait value. For an unwaited spawn, completion arrives through the
host’s typed completion update; subagent_status 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.
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 subagent_status.
When the primary is idle, remaining children are collected into one completion
wave. 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 <task> creates a child; /subagents logs <id>
opens its history; /subagents result <id> shows the bounded final summary;
kill <id> stops it; and resume <id> <prompt> continues it.
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 budget exhaustion. Budget-exhausted 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 subagent_status with agent_id and include_result: true to retrieve the
saved handoff, changed_files, and patch_ref when a patch was captured. The
patch bytes are stored in subagents/<child-id>/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:
subagent://<id>
subagent://<id>/history
subagent://<id>/result
subagent://<id>/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.
source
1# Resident subagents
2
3> **V0.x breaking change:** zut no longer supports subprocess subagent state.
4> Previous state is ignored rather than read or migrated; remove it when you
5> no longer need it. Existing child jobs are never resumed.
6
7Subagents are independent `core.Agent` conversations resident in the same zut
8process as their parent. A child has a stable, private session identity, its own
9provider client and tool registry, a cancellation boundary, and an authoritative
10journal. Starting a child never launches another zut executable.
11
12## Profiles
13
14Profiles are Markdown files discovered from `~/.agents/agents/*.md` and paths
15in `ZUT_AGENT_PROFILES`. They are inputs only; zut does not execute them.
16
17```markdown
18---
19name: reviewer
20description: Read-only code reviewer
21tools: [read, grep]
22model: openai-codex/gpt-5.6-luna
23thinking: high
24systemPromptMode: replace
25inheritProjectContext: false
26inheritSkills: false
27fastMode: false
28---
29
30Inspect the requested scope and report evidence-backed findings.
31```
32
33`tools` is a capability boundary. An omitted value inherits the child-safe
34catalogue, `tools: []` grants no tools, and an explicit list replaces the
35default. The child-safe catalogue contains only tools offered by the parent
36that are also available in the resolved child runtime; parent-only scheduling
37and extension tools are never inherited. Names unavailable to the child
38(including host-only extensions) and tools denied by `subagents.allowed_tools`
39are omitted from an explicit list; they never grant the child additional
40access. `model`, `provider`, `thinking`,
41the prompt mode, project/skill inheritance, and `fastMode` are resolved before
42the child is accepted. Spawn-level model, provider, reasoning, and fast-mode
43values take precedence where supplied.
44
45Children are always fresh conversations: they receive the delegated task and
46resolved host/profile context, never the parent transcript or hidden reasoning.
47They cannot recursively create more children.
48
49## Lifecycle and persistence
50
51The manager records each accepted child under its managed state root:
52
53```text
54subagents/<child-id>/
55 transcript.jsonl # authoritative accepted turns and finalized messages
56 owner.lock # host-ownership coordination; do not modify or remove
57 .transcript-backup-* # retained private pre-repair transcript, when repaired
58 metadata.json # rebuildable bounded state
59 result.json # latest outcome, final summary or partial handoff, artifacts
60 patch.diff # optional worktree capture
61```
62
63Acceptance is durable before `subagent_spawn` reports success. Finalized user,
64assistant, tool-call, and tool-result messages are journaled; streamed deltas
65and hidden reasoning are not exposed as ordinary history. A tool call is stored
66before execution and reconciliation repairs incomplete call/result pairs.
67
68All children stop when the host exits. On the next start, queued or running
69turns are marked `interrupted`; zut never replays their task. Resume only with
70an explicit new prompt through `subagent_resume`, the child-session composer,
71or `/subagents resume <id> <prompt>`.
72
73Multiple zut processes may share a state root. Each resident journal has one
74host owner at a time. A process that finds a child owned elsewhere reports it
75as such and does not inspect, recover, or modify its transcript. Resume that
76child from its owning host or after the owner exits. On a subsequent safe
77reconciliation, zut repairs only the known legacy false-recovery sequence; it
78leaves any ambiguous journal corruption untouched. A repair retains the
79pre-repair transcript as `.transcript-backup-*`; it can contain private session
80data and is intentionally not removed automatically.
81
82`subagent_status` reports `owned_elsewhere: true` when another process owns a
83child. The field is omitted when false. A foreign-owned child cannot be resumed,
84or have its history or result read, from this process.
85
86The global scheduler admits the oldest eligible accepted prompt, runs at most
87one turn for each child, and defaults to six concurrent turns. Set
88`subagents.max_concurrent` to a positive limit to override it. Queueing is
89unlimited by default; a positive `subagents.queue_timeout` cancels an accepted
90prompt that has not received a slot and records its terminal failure durably.
91`subagents.allowed_tools` is an allowlist for child-visible tools and
92`subagents.allowed_roots` limits eligible child workspaces. Each child receives
93a cumulative weighted-token budget equal to its resolved model's context window.
94Removed legacy settings, including `tui_subagent_position` and
95`subagents.budget_ratio`, are ignored. Resident journals created before this
96budget policy are incompatible: zut leaves them untouched on disk and excludes
97them from automatic discovery without reporting an error at every startup.
98They cannot be resumed; start a new child instead. Other journal recovery
99errors are still reported.
100
101Budget accounting charges uncached input, cache writes, and output fully; cache
102reads count at 25%. It is separate from active context-window usage and survives
103follow-up turns and compaction. The TUI shows the child's current budget usage,
104but does not alter its instructions, tools, or output cap before exhaustion.
105Providers report exact input usage only after a response, so the terminal
106response can make displayed usage exceed 100%; no later request is sent in that
107turn. If the worker still needs another model step, it reports
108`budget_exhausted`, not successful idle work. A final answer that completes on
109the response crossing the limit remains successful and reports `completed`.
110
111Budget exhaustion publishes a terminal completion with a host-generated partial
112handoff, capped at 16 KiB. It includes recent visible progress messages, tool
113names and error flags, workspace and history references, and recovery guidance.
114Raw tool arguments and output are omitted from this automatic handoff because
115they may contain secrets; they remain retrievable in the child's durable history.
116These are observations, not a claim that the task or verification finished;
117older or truncated details also remain in the durable transcript. Worktree
118changes are captured even on budget exhaustion, and the worktree is retained
119for recovery. Shared-workspace changes are not rolled back.
120
121Retrieve the saved result without executing a model:
122
123```json
124{"agent_id":"<child-id>","include_result":true}
125```
126
127Pass this to `subagent_status`, then reconcile the history and artifacts before
128repeating any side effects. Explicit `subagent_resume` after terminal exhaustion
129opens a fresh allowance of the **same size**, retaining the session, workspace,
130and cumulative cost. This permits additional spending; it does not enlarge the
131configured limit or automatically retry the task. The allowance baseline is
132journaled and survives restart. Ordinary non-exhausted follow-ups continue using
133the existing allowance.
134
135`required: true` makes a delegated result an obligation of the parent turn.
136Failed, cancelled, interrupted, and budget-exhausted required work remains
137unresolved until an explicit successful follow-up. Reading a partial handoff or
138accepting a recovery prompt does not satisfy the obligation. A host restart never
139retries it automatically.
140
141## Delegation ownership
142
143Interactive proactive delegation keeps the primary agent on the critical path.
144Before spawning, it selects useful local work and delegates only a concrete,
145bounded sidecar with a non-overlapping question, responsibility, package, or
146file set. Immediate blockers and tightly coupled work stay local. While a child
147is active, it owns that scope; the primary continues only its preselected
148independent work instead of repeating the investigation, review, testing, or
149implementation. If an explicit user request or active workflow hands blocking
150work to a child, the primary yields for the completion update. Once a worktree
151child succeeds, the primary may apply and verify its captured patch without
152redoing the delegated task.
153
154Explicit headless `--orchestrate` remains manager-only. Its primary divides the
155request into disjoint worker scopes, coordinates active children, and yields
156rather than implementing or reproducing delegated work. Children do not inherit
157the parent's strict orchestrator role. Headless orchestration waits for every
158accepted child before continuing its completion wave; `required: true`
159additionally creates a durable obligation. When proactive
160interactive delegation cannot spawn because of launch-time policy, the primary
161continues locally; a strict headless orchestrator instead reports the missing
162required capability.
163
164## Tools and slash commands
165
166The model-facing tools retain their logical names:
167
168- `subagent_spawn` accepts `task`, optional `agent`, `model` and `provider`,
169 `reasoning`, `fast_mode`, `required`, `wait`, and `isolation` (`shared` or
170 `worktree`).
171 `wait` is an explicit whole-second value from 1 through 300; when omitted,
172 spawning returns immediately. A timed-out wait leaves the
173 accepted child active, whether it is queued or running. Do not retry that
174 task until it reaches a terminal failure, cancellation, or interruption. It
175 returns a logical `subagent://<id>` reference.
176- `subagent_status` returns bounded current state for one child or the current
177 set immediately. With `agent_id` and `include_result: true`, it also reads the
178 saved terminal result or partial handoff without model execution. Without this
179 option it remains metadata-only; foreign-owned results cannot be read.
180 Result-read errors distinguish foreign ownership, a missing saved result,
181 and permission denial without exposing filesystem paths or saved content.
182- `subagent_stop` stops one live child.
183- `subagent_resume` accepts an explicit follow-up prompt for an existing child.
184 After terminal budget exhaustion, this grants a fresh same-size allowance
185 while preserving progress. Inspect the saved handoff before resuming.
186
187Child execution started by `subagent_spawn` is asynchronous unless it receives
188an explicit `wait` value. For an unwaited spawn, completion arrives through the
189host’s typed completion update; `subagent_status` returns immediately and does
190not wait for completion. Do not use sleep loops, repeated status calls, journal
191files, or terminal UI inspection as a completion signal.
192In interactive mode, a result received while the primary is busy enters the
193visible **sliding in** queue and reaches the model at its next safe boundary,
194without interrupting a model request or tool call and without waiting for other
195children. Results already queued are not repeated when the parent turn ends.
196Undelivered results remain queued after a provider or compaction error for the
197next turn; they do not automatically retry the failed request. Explicit
198cancellation clears pending queue entries. The queue is in memory, while the
199child’s saved result remains available through `subagent_status`.
200When the primary is idle, remaining children are collected into one completion
201wave. Headless orchestration still waits for every accepted child before its
202next parent turn.
203Successful completions include the final visible assistant summary, capped at
204256 KiB; open the child session for the complete durable transcript.
205
206`/subagents` opens the resident-child list, ordered by the latest state change.
207Each row leads with the profile (or model), shows a human terminal state such
208as `completed`, a relative update time, and a short ID. Press Down from the
209main composer to open this list without clearing a draft when a child exists.
210Use arrows and Enter to open a child session; `/subagents new <task>` creates a child; `/subagents logs <id>`
211opens its history; `/subagents result <id>` shows the bounded final summary;
212`kill <id>` stops it; and `resume <id> <prompt>` continues it.
213
214The child session uses the same transcript renderer as the main session.
215Recent history is bounded and loaded asynchronously; PgUp asks for an older
216page. Up/Down preserves the current reading position, and PgDn returns to the
217live tail after unread updates accumulate. The screen combines durable finalized
218history with an immutable in-memory projection of the active turn. Enter submits
219the local composer only after the manager durably accepts it; Esc closes the session. History may contain task
220text, tool arguments, tool results, and source-derived output, so treat it as
221private local session data.
222
223## Worktrees and references
224
225Shared children use the host working directory. With `isolation: "worktree"`,
226zut prepares a detached Git worktree and captures a patch and changed-file list
227on successful completion or budget exhaustion. Budget-exhausted turns retain
228partial work in the worktree, along with any captured patch and changed-file
229list. zut never applies a captured patch automatically. Failed or interrupted
230worktrees are retained for inspection; an idle successful worktree is cleaned
231when its child is explicitly stopped.
232
233Use `subagent_status` with `agent_id` and `include_result: true` to retrieve the
234saved handoff, `changed_files`, and `patch_ref` when a patch was captured. The
235patch bytes are stored in `subagents/<child-id>/patch.diff` under the managed
236state root shown above; logical references are identifiers, not filesystem
237paths. Inspect the retained worktree and patch before resuming or applying
238partial changes.
239
240New terminal journal records also save the capture metadata, so reconciliation
241can restore it if `result.json` is missing or corrupt. Older records remain
242readable, but cannot reconstruct artifact metadata they never recorded.
243
244Logical references avoid exposing runtime paths:
245
246```text
247subagent://<id>
248subagent://<id>/history
249subagent://<id>/result
250subagent://<id>/patch
251```
252
253## Provider caching
254
255Each main and child conversation sends a stable logical session ID and a new
256turn ID per accepted prompt to the provider layer. Provider adapters decide
257whether and how to use them: OpenAI/Codex translates a supported stable session
258into its cache key and routing header, while other adapters preserve deterministic
259request input and use only cache controls their provider/model/endpoint supports.
260An OpenAI-compatible wire shape is not itself evidence that a cache extension
261or transport feature is available. Models continue through their declared
262provider transport; a transport change must not change the logical session or
263turn identity used by the provider request.