@ferris1225/pi-subagents 4.2.5 → 4.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,346 +1,375 @@
1
- # pi-subagents
2
-
3
- [![npm version](https://img.shields.io/npm/v/@ferris1225/pi-subagents?color=blue)](https://www.npmjs.com/package/@ferris1225/pi-subagents)
4
- [![downloads](https://img.shields.io/npm/dm/@ferris1225/pi-subagents)](https://www.npmjs.com/package/@ferris1225/pi-subagents)
5
- [![license](https://img.shields.io/npm/l/@ferris1225/pi-subagents)](./LICENSE)
6
- ![platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey)
7
- ![pi](https://img.shields.io/badge/pi-extension-orange)
8
-
9
- A managed engineering team for [pi](https://github.com/earendil-works/pi): two
10
- focused sub-agents, durable threads, and Git worktree
11
- isolation. You install it once and your main agent delegates on its own.
12
-
13
- ## Why
14
-
15
- Delegation is supposed to remove coordination work. Most sub-agent launchers stop
16
- at "spawn a child with a prompt" and leave the hard parts — when to delegate, how
17
- wide to fan out, what happens when a model dies, how results come
18
- back — with you. This extension owns them:
19
-
20
- - The main model delegates without being asked, because a delegation directive is
21
- always in its system prompt.
22
- - Dispatching never blocks or ends the main turn, so it can start several runs and
23
- keep working while they execute.
24
- - Results deliver themselves. There is no status tool to poll and no lookup step.
25
- - Parallel writers get their own Git worktrees, so concurrent edits do not collide
26
- and your index is never touched.
27
- - Threads keep their context across resume, stop, reload, and crash; a dead model
28
- hands its session to the current main model instead of losing progress.
29
- - Crashes, partial starts, and integration failures come back as results with
30
- recovery records — never as silent hangs.
31
-
32
- ## Install
33
-
34
- Requires **pi >= 0.84.4** and **Node.js >= 22.19.0**.
35
-
36
- ```bash
37
- pi install npm:@ferris1225/pi-subagents
38
- ```
39
-
40
- Open pi and run `/subagents-setup` to choose agents, models, and thinking
41
- strengths. A fresh install enables every agent on the current main model, and each
42
- session start points you at the wizard until a config file exists. Then just ask
43
- for work:
44
-
45
- ```text
46
- Map how authentication works, fix the refresh race, run the tests, and review the diff.
47
- ```
48
-
49
- The main agent decides when delegation pays off. You can also call the tools
50
- directly when you want exact control.
51
-
52
- ## The team
53
-
54
- | Agent | Access | Best for |
55
- | ------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
56
- | `explorer` | Read-only | Broad search, unfamiliar-area mapping, symbol and dependency tracing. Returns a retrieval index — never proof. A single artifact the main agent must fully absorb (one issue, one spec) stays an inline read. |
57
- | `executor` | Full | The default route for any non-trivial, self-contained task: implementation, fixes, refactors, tests, evidence-first cleanup, docs/comment sync, or merging a fan-out's results into one brief — carried through verification and a result-only handoff. |
58
-
59
- Custom roles join them with a Markdown file (see [Custom agents](#custom-agents)).
60
-
61
- Every child is an isolated leaf pi process with its own context window and no
62
- memory of your conversation, so the brief is its only input. A good brief carries
63
- the goal, exact paths, constraints, and expected output — which is what the
64
- injected delegation guidance produces when the main agent dispatches for you.
65
-
66
- ```text
67
- You
68
- └─ pi main agent
69
- ├─ explorer ─── parallel recon, retrieval leads only
70
- └─ executor ─── one deliverable per child: implement, fix, clean up,
71
- sync docs, or merge fan-out results → verify → deliver
72
- ```
73
-
74
- ## Dispatching work
75
-
76
- ```ts
77
- // One task
78
- subagent({
79
- agent: "executor",
80
- task: "Fix the cache invalidation bug in src/cache, add regression tests, run the checks.",
81
- });
82
-
83
- // Parallel: as many genuinely independent units as the work has
84
- subagent({
85
- tasks: [
86
- { agent: "explorer", task: "Trace model fallback from dispatch to completion." },
87
- { agent: "executor", task: "Add edge-case tests for config migration." },
88
- ],
89
- });
90
- ```
91
-
92
- Breadth is the main agent's call, not a configured limit. There is no per-call
93
- task cap: put every genuinely independent unit in one `tasks` array. The runtime
94
- paces execution instead, running a pool of child processes that scales with the
95
- machine (half its cores, bounded to 4–16) and starting queued runs automatically
96
- as slots free.
97
-
98
- Because queueing is pacing rather than refusal, it is always reported as such.
99
- Dispatch confirmations name each waiting run's real reason — waiting for a free
100
- process slot, serialized behind the shared-checkout write lane, or already
101
- starting its child alongside the slot capacity. A run that waits for the write
102
- lane releases its slot first, so serialized writers never starve new dispatches.
103
-
104
- One child owns one coherent deliverable and its files. Dependent work starts only
105
- after its prerequisite delivers. Verification belongs to whoever did the work:
106
- every child runs the checks it can and reports exactly which ones ran, and the
107
- main agent inspects the actual changes before calling anything done.
108
-
109
- ## Parallel edits
110
-
111
- - Single tasks use your checkout. Every parallel write-capable agent (`executor`
112
- and custom writers) defaults to a detached Git worktree, so
113
- parallel writers run at the same time. Worktree mode needs a committed `HEAD`,
114
- and read-only agents reject it.
115
- - A role file can pin its own default with `isolation: worktree` or
116
- `isolation: shared` in the frontmatter. Precedence is an explicit per-dispatch
117
- `isolation`, then the role's declaration, then the parallel write default.
118
- - An isolated run's tracked, deleted, untracked, and binary changes integrate
119
- back exactly once, after the child settles. Nothing is staged and your index is
120
- untouched.
121
- - Integration is a three-way merge, so parallel workers that touched disjoint
122
- files or regions land cleanly even when earlier patches moved the checkout
123
- underneath them. A genuine overlap leaves conflict markers in the checkout and
124
- keeps the worktree and patch for you to resolve.
125
- - Shared-checkout writers serialize through
126
- one repository lane, so two of them never race. A run waiting there is reported
127
- as a lane wait, not as slot queueing, and its process slot is already released.
128
- - Setup and integration failures keep the useful patch and worktree, and record
129
- where they are in `~/.pi/agent/pi-subagents-recovery.json`. Every later session
130
- start repeats that notice until you remove the artifacts. When the changes had
131
- already been applied and only the cleanup failed, the next session start
132
- removes the retained copy itself and clears the notice.
133
-
134
- ## Threads: resume, stop
135
-
136
- Every dispatch returns a stable `#id`, which is the handle for the thread tools:
137
-
138
- | Tool | What it does |
139
- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
140
- | `subagent_control` | `resume` a parked or settled thread with its full retained context, optionally appending a new `objective`. |
141
- | `subagent_stop` | Destructively cancel, deliver the partial output, and retire the thread. Steering and follow-up messages still queued in the child are dropped so nothing can revive it later. |
142
-
143
- ```ts
144
- subagent_control({ action: "resume", id: 7, objective: "Finish the tests." });
145
- ```
146
-
147
- There is deliberately no status, polling, or wait tool. Every result delivers
148
- itself as a completion that wakes the main model, so a turn never blocks on a
149
- running subagent keep working or end the turn, and the completion continues
150
- it. The one in-turn block is `wait: true` on a dispatch, which holds that call
151
- until the runs it started settle: the escape hatch for one-shot `pi -p`
152
- parents, which exit at end of turn and would otherwise never see them. That
153
- wait runs on no timer and no timeout the model picks: it resolves the instant
154
- its run settles, a parked run answers immediately with its resume handle, and
155
- aborting the turn is the escape hatch. Control operations are all bounded, so
156
- they never hang on a generation that is still settling.
157
-
158
- A thread stays durable while its work is unfinished. Parked sessions, worktree
159
- checkpoints, and result excerpts are recorded in a manifest beside your config, so
160
- a pi reload, restart, or crash interrupts a run into a resumable checkpoint
161
- instead of losing it, and an isolated thread resumes in the worktree it was
162
- already working in. Restore happens at load, and everything that answers for a run
163
- waits for it `subagent_control`, `subagent_stop`, and a new dispatch before it
164
- takes an id so the first call after a reload can never report parked work as
165
- missing or hand its id to something else.
166
-
167
- Only interrupted work needs a record, so a thread that completes or fails cleanly
168
- drops its own. That also means a reload keeps interrupted threads resumable, while
169
- threads that had already finished keep only their delivered result.
170
-
171
- ## Live status and results
172
-
173
- The TUI widget renders one line per active run in fixed identity columns —
174
- status icon, right-aligned `#id`, padded agent name, then the task label — so
175
- every label starts at the same column, with the live activity dimmed after
176
- `↳` on its own line and the rest of the telemetry flowing inline after ` · `: the
177
- worktree badge, the token flow in the footer vocabulary (`↑` input, `↓` output,
178
- `R`/`W` cache read/write), cost, the full `provider/model` ref, the
179
- wait state, and an elapsed time that always carries seconds. A live run renders
180
- two lines: what it is — agent, task, token flow, cost, provider/model, elapsed —
181
- and, dim under the label column, what it is doing right now:
182
-
183
- ```text
184
- ● #12 executor src/cache.ts · wt:a91f3c · ↑5.2k ↓41.0k R210.0k W6.1k $1.9400 · 12m06s
185
- ↳ edit src/auth.ts
186
- ● #15 explorer src/models.ts · ↑1.2k ↓8.4k R31.0k W1.1k $0.0900 · openai/gpt-5-mini · 3m07s
187
- grep fallback
188
- #23 executor src/config.ts · repo lane
189
- #24 executor ↻ tests/config.test.ts · queued · 5m02s
190
- ```
191
-
192
- Telemetry drops leftmost-first when a row runs out of width (badge, wait
193
- state, usage, model) while the elapsed survives every width. Queued rows state
194
- what they actually wait for `queued` for a free process slot, `repo lane`
195
- for shared-checkout write serialization, or `starting` and a resumed thread
196
- carries a dim `↻` in its agent column with its cumulative time. The widget is
197
- capped at ten lines: when many runs are live, extra runs collapse into a
198
- `… +N more` marker so the editor keeps its space.
199
-
200
- Completions resume the main agent on their own, with a compact block of at most 40
201
- lines by default; longer output lands unchanged in a Markdown artifact whose path
202
- comes with the message. Roles write result-only handoffs outcome, paths,
203
- verification, unresolved blockers and the main agent is told to add its
204
- conclusion rather than restate what you already read. A failed run adds its
205
- failed-tool diagnostics.
206
-
207
- ## Models, thinking, and tools
208
-
209
- Each agent runs on the current main model or on one you pick in
210
- `/subagents-setup`, which labels vision and text-only models. If a selected model
211
- is missing, rate-limited, or fails at the provider level, the **same retained
212
- session** continues on the main model, so finished searches, reads, and edits
213
- survive. Ordinary task failures do not trigger a handoff.
214
-
215
- Thinking defaults to **Auto**: the role's own preference, clamped to what the
216
- effective model supports. `/subagents-setup` _Configure an agent_ also offers a
217
- manual strength, listing only the levels that model supports. There is no separate
218
- vision modeassign a multimodal model and name the image paths in the task.
219
-
220
- Every dispatch, resume, retry, and fallback snapshots the parent's
221
- currently active tools. A role with no explicit list inherits the full set. An
222
- explicit list keeps its pi built-in boundary and gains active extension tools,
223
- while its shell slot follows the parent: a role file naming `bash` runs
224
- `powershell` when that is the shell you enabled. When you run both, the child gets
225
- the one that fits the host — PowerShell on Windows, Bash elsewhere — rather than
226
- two terminals to choose between. A child never receives a shell you disabled,
227
- since pi's `--tools` allowlist overrides its own `defaultTools`. Read-only roles
228
- never gain `edit` or `write`, and all `subagent*` tools are stripped so children
229
- stay leaves. An empty snapshot starts the child with `--no-tools`.
230
-
231
- Shell guidance in the shipped roles is portable for the same reason: they reach
232
- for pi's own `read`/`grep`/`find`/`ls` tools, which behave identically everywhere,
233
- and keep shell examples to `git` queries instead of POSIX binaries a PowerShell
234
- child cannot run.
235
-
236
- ## Configuration
237
-
238
- `/subagents-setup` stays one level deep: enabled agents, plus a model and thinking
239
- strength per agent. Everything else is config-file only, stored at
240
- `~/.pi/agent/pi-subagents.json` (following `PI_CODING_AGENT_DIR`):
241
-
242
- ```json
243
- {
244
- "enabledAgents": ["explorer", "executor"],
245
- "knownAgents": ["explorer", "executor"],
246
- "agentModels": { "explorer": "anthropic/claude-haiku-4-5" },
247
- "agentThinkingLevels": { "executor": "high" },
248
- "maxResultLines": 40,
249
- "agentScope": "user",
250
- "idleTimeoutSec": 90
251
- }
252
- ```
253
-
254
- | Field | Meaning |
255
- | --------------------- | --------------------------------------------------------------------------------- |
256
- | `enabledAgents` | Agents available for discovery and delegation. `[]` disables all. |
257
- | `knownAgents` | Built-ins this config has seen; automatic bookkeeping never edit it. |
258
- | `agentModels` | Optional `provider/model-id` per agent; missing = current main model. |
259
- | `agentThinkingLevels` | Optional manual level per agent; missing = Auto. |
260
- | `maxResultLines` | Lines kept in a completion message before the artifact takes over. Default `40`. |
261
- | `agentScope` | Discover `user`, `project`, or `both` agent directories. Default `user`. |
262
- | `idleTimeoutSec` | Seconds without child RPC output before termination; `0` disables. Default `90`. |
263
-
264
- The delegation directive is always injected; there is no toggle. Invalid values
265
- fall back safely, and stale keys — including the former `proactiveInjection`,
266
- `maxConcurrency`, `maxFixRounds`, and `notifyOnReviewPass` knobs — are dropped
267
- automatically. Built-in roles a newer package no longer ships (such as the
268
- retired `worker`/`cleaner`/`documenter`/`synthesizer`/`reviewer` set) are pruned
269
- from `enabledAgents`, `knownAgents`, and the model/thinking tables at first
270
- load, so the setup wizard never mixes old and new roles; custom agents are
271
- untouched. At session
272
- start, model overrides pi no longer reports are removed with a one-time notice. If
273
- pi's own session compaction fails mid-thread, a notice surfaces the error and the
274
- automatic retry instead of failing quietly.
275
-
276
- Agents shipped by a newer package version turn themselves on at the next
277
- session: a built-in the config has never seen is adopted into `enabledAgents`
278
- and follows explorer's configured model and thinking level — the fast lane
279
- these light roles need — while an agent you disabled stays disabled
280
- (`knownAgents` is what tells the two cases apart). Enabling a role in
281
- `/subagents-setup` adopts the same explorer route.
282
-
283
- ## Custom agents
284
-
285
- Built-ins ship with the package. Add or replace them with Markdown files:
286
-
287
- - User agents: `~/.pi/agent/agents/`
288
- - Project agents: the nearest `.pi/agents/` in a trusted project
289
- - Precedence: project > user > built-in, where the same `name` wins
290
-
291
- ```yaml
292
- ---
293
- name: explorer
294
- description: Fast read-only codebase reconnaissance
295
- thinking: low
296
- isolation: shared
297
- tools: read, bash
298
- ---
299
- …additional system prompt…
300
- ```
301
-
302
- `description` is the routing line the main model reads, and `thinking` is the
303
- role's Auto preference, which a wizard choice overrides. `isolation` pins the
304
- role's default boundary as described under [Parallel edits](#parallel-edits).
305
- Models come only from `/subagents-setup`; an agent file cannot pin one. An
306
- explicit `tools` list is the capability boundary, and omitting it inherits the
307
- parent's complete active set.
308
-
309
- ## Storage and cleanup
310
-
311
- Everything lives under your pi agent directory, grouped per project. Nothing
312
- long-lived is written to the OS temp directory, and every class of file has a rule
313
- that removes it, so this directory does not grow without bound:
314
-
315
- | Path | Holds | Removed |
316
- | ------------------------------------------ | ---------------------------------------------------- | ---------------------------------------------------------------- |
317
- | `pi-subagents.json` | Your configuration | Never it is yours |
318
- | `pi-subagents-recovery.json` | Worktree integration and cleanup failures | When the retained patch or worktree it points at is gone |
319
- | `ferris-pi-subagents/<project>/pi-subagents-threads.json` | One record per interrupted thread | When the thread settles, or after 30 days |
320
- | `ferris-pi-subagents/<project>/sessions/` | Retained child sessions that a resume continues from | When the pi session that produced it ends, or its owner is gone |
321
- | `ferris-pi-subagents/<project>/worktrees/` | Isolated checkouts for parallel writers | On integration, or when its owning process is gone |
322
- | `ferris-pi-subagents/<project>/results/` | Full text of truncated results | After 7 days, or beyond 50 per project |
323
- | `ferris-pi-subagents/<project>/tmp/` | Child prompt copies and the no-retry policy shim | When its owning process exits |
324
- | `ferris-pi-subagents/<project>/` | All of the above for one checkout | When the whole directory has been idle for 3 days |
325
-
326
- Cleanup runs at extension load and is deliberately conservative. A directory goes
327
- away only when the process that created it is gone and no manifest record still
328
- claims it, so a live sibling pi instance never loses state and parked work
329
- outlives its own process by design — a reference from the threads manifest always
330
- beats an age rule.
331
-
332
- ## Development
333
-
334
- ```bash
335
- npm install
336
- npm run check
337
- npm test
338
- ```
339
-
340
- There are no bundled runtime dependencies; pi and TypeBox are peers. The source is
341
- split by responsibility: dispatch policy, thread lifecycle, RPC
342
- transport, worktree integration, completion delivery, tools, and TUI status.
343
-
344
- ## License
345
-
346
- MIT
1
+ # pi-subagents
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@ferris1225/pi-subagents?color=blue)](https://www.npmjs.com/package/@ferris1225/pi-subagents)
4
+ [![downloads](https://img.shields.io/npm/dm/@ferris1225/pi-subagents)](https://www.npmjs.com/package/@ferris1225/pi-subagents)
5
+ [![license](https://img.shields.io/npm/l/@ferris1225/pi-subagents)](./LICENSE)
6
+ ![platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey)
7
+ ![pi](https://img.shields.io/badge/pi-extension-orange)
8
+
9
+ A managed engineering team for [pi](https://github.com/earendil-works/pi): two
10
+ focused sub-agents, durable threads, and Git worktree
11
+ isolation. You install it once and your main agent delegates on its own.
12
+
13
+ ## Why
14
+
15
+ Delegation is supposed to remove coordination work. Most sub-agent launchers stop
16
+ at "spawn a child with a prompt" and leave the hard parts — when to delegate, how
17
+ wide to fan out, what happens when a model dies, how results come
18
+ back — with you. This extension owns them:
19
+
20
+ - The main model delegates without being asked, because a delegation directive is
21
+ always in its system prompt.
22
+ - Dispatching never blocks or ends the main turn, so it can start several runs and
23
+ keep working while they execute.
24
+ - Results deliver themselves. There is no status tool to poll and no lookup step.
25
+ - Parallel writers get their own Git worktrees, so concurrent edits do not collide
26
+ and your index is never touched.
27
+ - Threads keep their context across resume, stop, reload, and crash; a dead model
28
+ hands its session to the current main model instead of losing progress.
29
+ - Crashes, partial starts, and integration failures come back as results with
30
+ recovery records — never as silent hangs.
31
+
32
+ ## Install
33
+
34
+ Requires **pi >= 0.84.4** and **Node.js >= 22.19.0**.
35
+
36
+ ```bash
37
+ pi install npm:@ferris1225/pi-subagents
38
+ ```
39
+
40
+ Open pi and run `/subagents-setup` to choose agents, models, and thinking
41
+ strengths. A fresh install enables every agent on the current main model, and each
42
+ session start points you at the wizard until a config file exists. Then just ask
43
+ for work:
44
+
45
+ ```text
46
+ Map how authentication works, fix the refresh race, run the tests, and review the diff.
47
+ ```
48
+
49
+ The main agent decides when delegation pays off. You can also call the tools
50
+ directly when you want exact control.
51
+
52
+ ## The team
53
+
54
+ | Agent | Access | Best for |
55
+ | ------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
56
+ | `explorer` | Read-only | Broad search, unfamiliar-area mapping, symbol and dependency tracing. Returns a retrieval index — never proof. A single artifact the main agent must fully absorb (one issue, one spec) stays an inline read. |
57
+ | `executor` | Full | A self-contained unit that changes the repository or condenses inputs: implementation, fixes, refactors, tests, evidence-first cleanup, docs/comment sync, or merging a fan-out's results into one brief — carried through verification and a result-only handoff. |
58
+
59
+ Custom roles join them with a Markdown file (see [Custom agents](#custom-agents)).
60
+
61
+ Every child is an isolated leaf pi process with its own context window and no
62
+ memory of your conversation, so the brief is its only input. A good brief carries
63
+ the goal, exact paths, constraints, and expected output — which is what the
64
+ injected delegation guidance produces when the main agent dispatches for you.
65
+
66
+ ```text
67
+ You
68
+ └─ pi main agent
69
+ ├─ explorer ─── parallel recon, retrieval leads only
70
+ └─ executor ─── one deliverable per child: implement, fix, clean up,
71
+ sync docs, or merge fan-out results → verify → deliver
72
+ ```
73
+
74
+ ## Dispatching work
75
+
76
+ ```ts
77
+ // One task
78
+ subagent({
79
+ agent: "executor",
80
+ task: "Fix the cache invalidation bug in src/cache, add regression tests, run the checks.",
81
+ });
82
+
83
+ // Parallel: as many genuinely independent units as the work has
84
+ subagent({
85
+ tasks: [
86
+ { agent: "explorer", task: "Trace model fallback from dispatch to completion." },
87
+ { agent: "executor", task: "Add edge-case tests for config migration." },
88
+ // Optional per-call reasoning strength for a task that needs less of it
89
+ { agent: "executor", task: "Run the suite and report failures.", thinking: "low" },
90
+ ],
91
+ });
92
+ ```
93
+
94
+ Breadth is the main agent's call, not a configured limit. There is no per-call
95
+ task cap: put every genuinely independent unit in one `tasks` array. The runtime
96
+ paces execution instead, running a pool of child processes that scales with the
97
+ machine (half its cores, bounded to 4–16) and starting queued runs automatically
98
+ as slots free.
99
+
100
+ Because queueing is pacing rather than refusal, it is always reported as such.
101
+ Dispatch confirmations name each waiting run's real reason waiting for a free
102
+ process slot, serialized behind the shared-checkout write lane, or already
103
+ starting its child — alongside the slot capacity. A run that waits for the write
104
+ lane releases its slot first, so serialized writers never starve new dispatches.
105
+
106
+ One child owns one coherent deliverable and its files. Dependent work starts only
107
+ after its prerequisite delivers. Verification belongs to whoever did the work:
108
+ every child runs the checks it can and reports exactly which ones ran, and the
109
+ main agent inspects the actual changes before calling anything done.
110
+
111
+ ## Parallel edits
112
+
113
+ - Single tasks use your checkout. Every parallel write-capable agent (`executor`
114
+ and custom writers) defaults to a detached Git worktree, so
115
+ parallel writers run at the same time. Worktree mode needs a committed `HEAD`,
116
+ and read-only agents reject it.
117
+ - A role file can pin its own default with `isolation: worktree` or
118
+ `isolation: shared` in the frontmatter. Precedence is an explicit per-dispatch
119
+ `isolation`, then the role's declaration, then the parallel write default.
120
+ - An isolated run's tracked, deleted, untracked, and binary changes integrate
121
+ back exactly once, after the child settles. Nothing is staged and your index is
122
+ untouched.
123
+ - Integration is a three-way merge, so parallel workers that touched disjoint
124
+ files or regions land cleanly even when earlier patches moved the checkout
125
+ underneath them. A genuine overlap leaves conflict markers in the checkout and
126
+ keeps the worktree and patch for you to resolve.
127
+ - Shared-checkout writers serialize through
128
+ one repository lane, so two of them never race. A run waiting there is reported
129
+ as a lane wait, not as slot queueing, and its process slot is already released.
130
+ - Setup and integration failures keep the useful patch and worktree, and record
131
+ where they are in `~/.pi/agent/pi-subagents-recovery.json`. Every later session
132
+ start repeats that notice until you remove the artifacts. When the changes had
133
+ already been applied and only the cleanup failed, the next session start
134
+ removes the retained copy itself and clears the notice.
135
+
136
+ ## Threads: resume, stop
137
+
138
+ Every dispatch returns a stable `#id`, which is the handle for the thread tools:
139
+
140
+ | Tool | What it does |
141
+ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
142
+ | `subagent_control` | `resume` a parked or settled thread with its full retained context, optionally appending a new `objective`. |
143
+ | `subagent_stop` | Destructively cancel, deliver the partial output, and retire the thread. Steering and follow-up messages still queued in the child are dropped so nothing can revive it later. |
144
+
145
+ ```ts
146
+ subagent_control({ action: "resume", id: 7, objective: "Finish the tests." });
147
+ ```
148
+
149
+ There is deliberately no status, polling, or wait tool. Every result delivers
150
+ itself as a completion that wakes the main model, so a turn never blocks on a
151
+ running subagent keep working or end the turn, and the completion continues
152
+ it. The one in-turn block is `wait: true` on a dispatch, which holds that call
153
+ until the runs it started settle: the escape hatch for one-shot `pi -p`
154
+ parents, which exit at end of turn and would otherwise never see them. That
155
+ wait runs on no timer and no timeout the model picks: it resolves the instant
156
+ its run settles, a parked run answers immediately with its resume handle, and
157
+ aborting the turn is the escape hatch. Control operations are all bounded, so
158
+ they never hang on a generation that is still settling.
159
+
160
+ A thread stays durable while its work is unfinished. Parked sessions, worktree
161
+ checkpoints, and result excerpts are recorded in a manifest beside your config, so
162
+ a pi reload, restart, or crash interrupts a run into a resumable checkpoint
163
+ instead of losing it, and an isolated thread resumes in the worktree it was
164
+ already working in. Restore happens at load, and everything that answers for a run
165
+ waits for it `subagent_control`, `subagent_stop`, and a new dispatch before it
166
+ takes an id — so the first call after a reload can never report parked work as
167
+ missing or hand its id to something else.
168
+
169
+ Only interrupted work needs a record, so a thread that completes or fails cleanly
170
+ drops its own. That also means a reload keeps interrupted threads resumable, while
171
+ threads that had already finished keep only their delivered result.
172
+
173
+ ## Live status and results
174
+
175
+ The TUI widget renders one line per active run in fixed identity columns —
176
+ status icon, right-aligned `#id`, padded agent name, then the task label so
177
+ every label starts at the same column, with the live activity dimmed after
178
+ `↳` on its own line and the rest of the telemetry flowing inline after ` · `: the
179
+ worktree badge, the token flow in the footer vocabulary (`↑` input, `↓` output,
180
+ `R`/`W` cache read/write), cost, the full `provider/model` ref, the
181
+ wait state, and an elapsed time that always carries seconds. A live run renders
182
+ two lines: what it is — agent, task, token flow, cost, provider/model, elapsed —
183
+ and, dim under the label column, what it is doing right now:
184
+
185
+ ```text
186
+ ● #12 executor src/cache.ts · worktree:a91f3c · 5.2k ↓41.0k R210.0k W6.1k $1.9400 · 12m06s
187
+ edit src/auth.ts
188
+ #15 explorer src/models.ts · ↑1.2k ↓8.4k R31.0k W1.1k $0.0900 · openai/gpt-5-mini · 3m07s
189
+ grep fallback
190
+ ○ #23 executor src/config.ts · repo lane
191
+ ○ #24 executor ↻ tests/config.test.ts · queued · 5m02s
192
+ ```
193
+
194
+ Telemetry drops leftmost-first when a row runs out of width (badge, wait
195
+ state, usage, model) while the elapsed survives every width. Queued rows state
196
+ what they actually wait for `queued` for a free process slot, `repo lane`
197
+ for shared-checkout write serialization, or `starting` and a resumed thread
198
+ carries a dim `↻` in its agent column with its cumulative time. The widget is
199
+ capped at ten lines: when many runs are live, extra runs collapse into a
200
+ `… +N more` marker so the editor keeps its space.
201
+
202
+ The widget is the detailed surface, but it only pays off while you are looking
203
+ at it. A one-line roll-up in the always-visible footer answers "is anything
204
+ still working?" without opening the widget or asking:
205
+
206
+ ```text
207
+ subagents 2 running · 1 repo lane · 3 done
208
+ ```
209
+
210
+ It is count-only, keeps the same wait vocabulary as the widget, disappears when
211
+ nothing is active, and works in RPC hosts as well as the TUI.
212
+
213
+ Completions resume the main agent on their own, with a compact block of at most 40
214
+ lines by default; longer output lands unchanged in a Markdown artifact whose path
215
+ comes with the message, stated as how much was actually cut (`40 of 137 lines
216
+ shown`) and conditioned on the shown lines being insufficient, so the same
217
+ content does not enter the main context twice. Roles write result-only handoffs — outcome, paths,
218
+ verification, unresolved blockers and the main agent is told to add its
219
+ conclusion rather than restate what you already read. A failed run adds its
220
+ failed-tool diagnostics.
221
+
222
+ Delivery is held while a context compaction is in flight and released once it
223
+ settles on failure and abort too — so a result a child spent minutes producing
224
+ is never swallowed by the summary that replaces the history.
225
+
226
+ A `wait: true` dispatch streams its progress onto the tool card while it waits,
227
+ and reports the awaited children's token spend as the tool call's own usage, so
228
+ sub-agent cost lands in the footer, `/session`, and RPC session totals. A
229
+ background dispatch returns before its children finish, so it reports no usage
230
+ rather than a fabricated number.
231
+
232
+ ## Models, thinking, and tools
233
+
234
+ Each agent runs on the current main model or on one you pick in
235
+ `/subagents-setup`, which labels vision and text-only models. If a selected model
236
+ is missing, rate-limited, or fails at the provider level, the **same retained
237
+ session** continues on the main model, so finished searches, reads, and edits
238
+ survive. Ordinary task failures do not trigger a handoff.
239
+
240
+ Thinking defaults to **Auto**: the role's own preference, clamped to what the
241
+ effective model supports. `/subagents-setup` → _Configure an agent_ also offers a
242
+ manual strength, listing only the levels that model supports. A dispatch can also
243
+ ask for a strength per call with `thinking`, so a quick check and a deep refactor
244
+ do not have to share one static level. Precedence: your manual
245
+ `/subagents-setup` choice > the per-call `thinking` > the role's frontmatter >
246
+ the default, and the winner is still clamped to the effective model. There is no separate
247
+ vision mode assign a multimodal model and name the image paths in the task.
248
+
249
+ Every dispatch, resume, retry, and fallback snapshots the parent's
250
+ currently active tools. A role with no explicit list inherits the full set. An
251
+ explicit list keeps its pi built-in boundary and gains active extension tools,
252
+ while its shell slot follows the parent: a role file naming `bash` runs
253
+ `powershell` when that is the shell you enabled. When you run both, the child gets
254
+ the one that fits the host — PowerShell on Windows, Bash elsewhere — rather than
255
+ two terminals to choose between. A child never receives a shell you disabled,
256
+ since pi's `--tools` allowlist overrides its own `defaultTools`. Read-only roles
257
+ never gain `edit` or `write`, and all `subagent*` tools are stripped so children
258
+ stay leaves. An empty snapshot starts the child with `--no-tools`.
259
+
260
+ Shell guidance in the shipped roles is portable for the same reason: they reach
261
+ for pi's own `read`/`grep`/`find`/`ls` tools, which behave identically everywhere,
262
+ and keep shell examples to `git` queries instead of POSIX binaries a PowerShell
263
+ child cannot run.
264
+
265
+ ## Configuration
266
+
267
+ `/subagents-setup` stays one level deep: enabled agents, plus a model and thinking
268
+ strength per agent. Everything else is config-file only, stored at
269
+ `~/.pi/agent/pi-subagents.json` (following `PI_CODING_AGENT_DIR`):
270
+
271
+ ```json
272
+ {
273
+ "enabledAgents": ["explorer", "executor"],
274
+ "knownAgents": ["explorer", "executor"],
275
+ "agentModels": { "explorer": "anthropic/claude-haiku-4-5" },
276
+ "agentThinkingLevels": { "executor": "high" },
277
+ "maxResultLines": 40,
278
+ "agentScope": "user",
279
+ "idleTimeoutSec": 90
280
+ }
281
+ ```
282
+
283
+ | Field | Meaning |
284
+ | --------------------- | --------------------------------------------------------------------------------- |
285
+ | `enabledAgents` | Agents available for discovery and delegation. `[]` disables all. |
286
+ | `knownAgents` | Built-ins this config has seen; automatic bookkeeping — never edit it. |
287
+ | `agentModels` | Optional `provider/model-id` per agent; missing = current main model. |
288
+ | `agentThinkingLevels` | Optional manual level per agent; missing = Auto. |
289
+ | `maxResultLines` | Lines kept in a completion message before the artifact takes over. Default `40`. |
290
+ | `agentScope` | Discover `user`, `project`, or `both` agent directories. Default `user`. |
291
+ | `idleTimeoutSec` | Seconds without child RPC output before termination; `0` disables. Default `90`. |
292
+
293
+ The delegation directive is always injected; there is no toggle. Invalid values
294
+ fall back safely, and stale keys — including the former `proactiveInjection`,
295
+ `maxConcurrency`, `maxFixRounds`, and `notifyOnReviewPass` knobs — are dropped
296
+ automatically. Built-in roles a newer package no longer ships (such as the
297
+ retired `worker`/`cleaner`/`documenter`/`synthesizer`/`reviewer` set) are pruned
298
+ from `enabledAgents`, `knownAgents`, and the model/thinking tables at first
299
+ load, so the setup wizard never mixes old and new roles; custom agents are
300
+ untouched. At session
301
+ start, model overrides pi no longer reports are removed with a one-time notice. If
302
+ pi's own session compaction fails mid-thread, a notice surfaces the error and the
303
+ automatic retry instead of failing quietly.
304
+
305
+ Agents shipped by a newer package version turn themselves on at the next
306
+ session: a built-in the config has never seen is adopted into `enabledAgents`
307
+ and follows explorer's configured model and thinking level — the fast lane
308
+ these light roles need — while an agent you disabled stays disabled
309
+ (`knownAgents` is what tells the two cases apart). Enabling a role in
310
+ `/subagents-setup` adopts the same explorer route.
311
+
312
+ ## Custom agents
313
+
314
+ Built-ins ship with the package. Add or replace them with Markdown files:
315
+
316
+ - User agents: `~/.pi/agent/agents/`
317
+ - Project agents: the nearest `.pi/agents/` in a trusted project
318
+ - Precedence: project > user > built-in, where the same `name` wins
319
+
320
+ ```yaml
321
+ ---
322
+ name: explorer
323
+ description: Fast read-only codebase reconnaissance
324
+ thinking: low
325
+ isolation: shared
326
+ tools: read, bash
327
+ ---
328
+ …additional system prompt…
329
+ ```
330
+
331
+ `description` is the routing line the main model reads, and `thinking` is the
332
+ role's Auto preference, which a wizard choice overrides. `isolation` pins the
333
+ role's default boundary as described under [Parallel edits](#parallel-edits).
334
+ Models come only from `/subagents-setup`; an agent file cannot pin one. An
335
+ explicit `tools` list is the capability boundary, and omitting it inherits the
336
+ parent's complete active set.
337
+
338
+ ## Storage and cleanup
339
+
340
+ Everything lives under your pi agent directory, grouped per project. Nothing
341
+ long-lived is written to the OS temp directory, and every class of file has a rule
342
+ that removes it, so this directory does not grow without bound:
343
+
344
+ | Path | Holds | Removed |
345
+ | ------------------------------------------ | ---------------------------------------------------- | ---------------------------------------------------------------- |
346
+ | `pi-subagents.json` | Your configuration | Never — it is yours |
347
+ | `pi-subagents-recovery.json` | Worktree integration and cleanup failures | When the retained patch or worktree it points at is gone |
348
+ | `ferris-pi-subagents/<project>/pi-subagents-threads.json` | One record per interrupted thread | When the thread settles, or after 30 days |
349
+ | `ferris-pi-subagents/<project>/sessions/` | Retained child sessions that a resume continues from | When the pi session that produced it ends, or its owner is gone |
350
+ | `ferris-pi-subagents/<project>/worktrees/` | Isolated checkouts for parallel writers | On integration, or when its owning process is gone |
351
+ | `ferris-pi-subagents/<project>/results/` | Full text of truncated results | After 7 days, or beyond 50 per project |
352
+ | `ferris-pi-subagents/<project>/tmp/` | Child prompt copies and the no-retry policy shim | When its owning process exits |
353
+ | `ferris-pi-subagents/<project>/` | All of the above for one checkout | When the whole directory has been idle for 3 days |
354
+
355
+ Cleanup runs at extension load and is deliberately conservative. A directory goes
356
+ away only when the process that created it is gone and no manifest record still
357
+ claims it, so a live sibling pi instance never loses state and parked work
358
+ outlives its own process by design — a reference from the threads manifest always
359
+ beats an age rule.
360
+
361
+ ## Development
362
+
363
+ ```bash
364
+ npm install
365
+ npm run check
366
+ npm test
367
+ ```
368
+
369
+ There are no bundled runtime dependencies; pi and TypeBox are peers. The source is
370
+ split by responsibility: dispatch policy, thread lifecycle, RPC
371
+ transport, worktree integration, completion delivery, tools, and TUI status.
372
+
373
+ ## License
374
+
375
+ MIT