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, paths in
ZUT_AGENT_PROFILES, and the ~/.pi/agent/agents/*.md compatibility discovery
location. 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
budgetRatio: 0.6
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. 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, fastMode, and optional
budgetRatio or budgetTokens override are resolved before the child is
accepted. Spawn-level model, provider, reasoning, fast-mode, or budget values
take precedence where supplied. budgetRatio and budgetTokens are mutually
exclusive.
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 state plus a bounded final assistant summary
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 also
inherits a cumulative weighted-token budget equal to 75% of the active parent
model's context window. Set subagents.budget_ratio to another value greater
than zero and at most one, or override one spawn/profile with budget_ratio or
budget_tokens. Removed legacy settings, including tui_subagent_position,
are ignored.
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 child and TUI receive progressive states at
50% (notice), 70% (focused), 85% (verifying), and 90% (finalizing). At
90%, zut disables further tool use and reserves the remaining budget for the
final response. Every request's output cap is also clamped to the remaining
budget. Providers report exact input usage only after a response, so the
terminal response can make displayed usage exceed 100%; no later request is
sent. An exhausted child cannot accept follow-up turns; spawn a replacement
with an explicitly larger override when more work is required.
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. 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, isolation (shared or
worktree), and mutually exclusive budget_ratio or budget_tokens.
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.
subagent_stop stops one live child.
subagent_resume accepts an explicit follow-up prompt for an existing child.
Budget-exhausted children are terminal and reject follow-ups.
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 only returns the current
state immediately. Do not use sleep loops, repeated status calls, journal
files, or terminal UI inspection as a completion signal.
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, captures a patch and changed-file list on
successful completion, and never applies that patch automatically. Failed or
interrupted worktrees are retained for inspection; an idle successful worktree
is cleaned when its child is explicitly stopped.
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`, paths in
15`ZUT_AGENT_PROFILES`, and the `~/.pi/agent/agents/*.md` compatibility discovery
16location. They are inputs only; zut does not execute them.
17
18```markdown
19---
20name: reviewer
21description: Read-only code reviewer
22tools: [read, grep]
23model: openai-codex/gpt-5.6-luna
24thinking: high
25budgetRatio: 0.6
26systemPromptMode: replace
27inheritProjectContext: false
28inheritSkills: false
29fastMode: false
30---
31
32Inspect the requested scope and report evidence-backed findings.
33```
34
35`tools` is a capability boundary. An omitted value inherits the child-safe
36catalogue, `tools: []` grants no tools, and an explicit list replaces the
37default. Names unavailable to the child (including host-only extensions) and
38tools denied by `subagents.allowed_tools` are omitted from an explicit list;
39they never grant the child additional access. `model`, `provider`, `thinking`,
40the prompt mode, project/skill inheritance, `fastMode`, and optional
41`budgetRatio` or `budgetTokens` override are resolved before the child is
42accepted. Spawn-level model, provider, reasoning, fast-mode, or budget values
43take precedence where supplied. `budgetRatio` and `budgetTokens` are mutually
44exclusive.
45
46Children are always fresh conversations: they receive the delegated task and
47resolved host/profile context, never the parent transcript or hidden reasoning.
48They cannot recursively create more children.
49
50## Lifecycle and persistence
51
52The manager records each accepted child under its managed state root:
53
54```text
55subagents/<child-id>/
56 transcript.jsonl # authoritative accepted turns and finalized messages
57 owner.lock # host-ownership coordination; do not modify or remove
58 .transcript-backup-* # retained private pre-repair transcript, when repaired
59 metadata.json # rebuildable bounded state
60 result.json # latest state plus a bounded final assistant summary
61 patch.diff # optional worktree capture
62```
63
64Acceptance is durable before `subagent_spawn` reports success. Finalized user,
65assistant, tool-call, and tool-result messages are journaled; streamed deltas
66and hidden reasoning are not exposed as ordinary history. A tool call is stored
67before execution and reconciliation repairs incomplete call/result pairs.
68
69All children stop when the host exits. On the next start, queued or running
70turns are marked `interrupted`; zut never replays their task. Resume only with
71an explicit new prompt through `subagent_resume`, the child-session composer,
72or `/subagents resume <id> <prompt>`.
73
74Multiple zut processes may share a state root. Each resident journal has one
75host owner at a time. A process that finds a child owned elsewhere reports it
76as such and does not inspect, recover, or modify its transcript. Resume that
77child from its owning host or after the owner exits. On a subsequent safe
78reconciliation, zut repairs only the known legacy false-recovery sequence; it
79leaves any ambiguous journal corruption untouched. A repair retains the
80pre-repair transcript as `.transcript-backup-*`; it can contain private session
81data and is intentionally not removed automatically.
82
83`subagent_status` reports `owned_elsewhere: true` when another process owns a
84child. The field is omitted when false. A foreign-owned child cannot be resumed,
85or have its history or result read, from this process.
86
87The global scheduler admits the oldest eligible accepted prompt, runs at most
88one turn for each child, and defaults to six concurrent turns. Set
89`subagents.max_concurrent` to a positive limit to override it. Queueing is
90unlimited by default; a positive `subagents.queue_timeout` cancels an accepted
91prompt that has not received a slot and records its terminal failure durably.
92`subagents.allowed_tools` is an allowlist for child-visible tools and
93`subagents.allowed_roots` limits eligible child workspaces. Each child also
94inherits a cumulative weighted-token budget equal to 75% of the active parent
95model's context window. Set `subagents.budget_ratio` to another value greater
96than zero and at most one, or override one spawn/profile with `budget_ratio` or
97`budget_tokens`. Removed legacy settings, including `tui_subagent_position`,
98are ignored.
99
100Budget accounting charges uncached input, cache writes, and output fully; cache
101reads count at 25%. It is separate from active context-window usage and survives
102follow-up turns and compaction. The child and TUI receive progressive states at
10350% (`notice`), 70% (`focused`), 85% (`verifying`), and 90% (`finalizing`). At
10490%, zut disables further tool use and reserves the remaining budget for the
105final response. Every request's output cap is also clamped to the remaining
106budget. Providers report exact input usage only after a response, so the
107terminal response can make displayed usage exceed 100%; no later request is
108sent. An exhausted child cannot accept follow-up turns; spawn a replacement
109with an explicitly larger override when more work is required.
110
111`required: true` makes a delegated result an obligation of the parent turn.
112Failed, cancelled, and interrupted required work remains unresolved until an
113explicit successful follow-up. A host restart never retries it automatically.
114
115## Delegation ownership
116
117Interactive proactive delegation keeps the primary agent on the critical path.
118Before spawning, it selects useful local work and delegates only a concrete,
119bounded sidecar with a non-overlapping question, responsibility, package, or
120file set. Immediate blockers and tightly coupled work stay local. While a child
121is active, it owns that scope; the primary continues only its preselected
122independent work instead of repeating the investigation, review, testing, or
123implementation. If an explicit user request or active workflow hands blocking
124work to a child, the primary yields for the completion update. Once a worktree
125child succeeds, the primary may apply and verify its captured patch without
126redoing the delegated task.
127
128Explicit headless `--orchestrate` remains manager-only. Its primary divides the
129request into disjoint worker scopes, coordinates active children, and yields
130rather than implementing or reproducing delegated work. Children do not inherit
131the parent's strict orchestrator role. Headless orchestration waits for every
132accepted child before continuing its completion wave; `required: true`
133additionally creates a durable obligation. When proactive
134interactive delegation cannot spawn because of launch-time policy, the primary
135continues locally; a strict headless orchestrator instead reports the missing
136required capability.
137
138## Tools and slash commands
139
140The model-facing tools retain their logical names:
141
142- `subagent_spawn` accepts `task`, optional `agent`, `model` and `provider`,
143 `reasoning`, `fast_mode`, `required`, `wait`, `isolation` (`shared` or
144 `worktree`), and mutually exclusive `budget_ratio` or `budget_tokens`.
145 `wait` is an explicit whole-second value from 1 through 300; when omitted,
146 spawning returns immediately. A timed-out wait leaves the
147 accepted child active, whether it is queued or running. Do not retry that
148 task until it reaches a terminal failure, cancellation, or interruption. It
149 returns a logical `subagent://<id>` reference.
150- `subagent_status` returns bounded current state for one child or the current
151 set immediately.
152- `subagent_stop` stops one live child.
153- `subagent_resume` accepts an explicit follow-up prompt for an existing child.
154 Budget-exhausted children are terminal and reject follow-ups.
155
156Child execution started by `subagent_spawn` is asynchronous unless it receives
157an explicit `wait` value. For an unwaited spawn, completion arrives through the
158host’s typed completion update; `subagent_status` only returns the current
159state immediately. Do not use sleep loops, repeated status calls, journal
160files, or terminal UI inspection as a completion signal.
161Successful completions include the final visible assistant summary, capped at
162256 KiB; open the child session for the complete durable transcript.
163
164`/subagents` opens the resident-child list, ordered by the latest state change.
165Each row leads with the profile (or model), shows a human terminal state such
166as `completed`, a relative update time, and a short ID. Press Down from the
167main composer to open this list without clearing a draft when a child exists.
168Use arrows and Enter to open a child session; `/subagents new <task>` creates a child; `/subagents logs <id>`
169opens its history; `/subagents result <id>` shows the bounded final summary;
170`kill <id>` stops it; and `resume <id> <prompt>` continues it.
171
172The child session uses the same transcript renderer as the main session.
173Recent history is bounded and loaded asynchronously; PgUp asks for an older
174page. Up/Down preserves the current reading position, and PgDn returns to the
175live tail after unread updates accumulate. The screen combines durable finalized
176history with an immutable in-memory projection of the active turn. Enter submits
177the local composer only after the manager durably accepts it; Esc closes the session. History may contain task
178text, tool arguments, tool results, and source-derived output, so treat it as
179private local session data.
180
181## Worktrees and references
182
183Shared children use the host working directory. With `isolation: "worktree"`,
184zut prepares a detached Git worktree, captures a patch and changed-file list on
185successful completion, and never applies that patch automatically. Failed or
186interrupted worktrees are retained for inspection; an idle successful worktree
187is cleaned when its child is explicitly stopped.
188
189Logical references avoid exposing runtime paths:
190
191```text
192subagent://<id>
193subagent://<id>/history
194subagent://<id>/result
195subagent://<id>/patch
196```
197
198## Provider caching
199
200Each main and child conversation sends a stable logical session ID and a new
201turn ID per accepted prompt to the provider layer. Provider adapters decide
202whether and how to use them: OpenAI/Codex translates a supported stable session
203into its cache key and routing header, while other adapters preserve deterministic
204request input and use only cache controls their provider/model/endpoint supports.
205An OpenAI-compatible wire shape is not itself evidence that a cache extension
206or transport feature is available. Models continue through their declared
207provider transport; a transport change must not change the logical session or
208turn identity used by the provider request.