@ferris1225/pi-subagents 4.3.8 → 4.3.10

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/CHANGELOG.md CHANGED
@@ -4,6 +4,47 @@ Published versions of `@ferris1225/pi-subagents`. Unpublished numbers
4
4
  (`4.2.3`, `4.2.6`, `4.2.9`–`4.2.11`) never shipped on npm; their changes
5
5
  landed in the next published release.
6
6
 
7
+ ## 4.3.10
8
+
9
+ - Add read-only `subagent_status`: list current-session runs or inspect an exact id,
10
+ including progress, elapsed time, terminal diagnostics, and retained artifact paths.
11
+ Runtime facts use Pi's existing `details.runs`; children do not need strict JSON reports.
12
+ - Remove `subagent_control` and all steer/park/resume entry points, continuation
13
+ admission, session forking, and resume widget markers. Main handles failed or
14
+ incomplete phases; new deliverables get new briefs. Dispatch, automatic completion
15
+ delivery, destructive stop, RPC cancellation, and manual worktree recovery remain.
16
+ - Keep interrupted worktree edits even when the retained Pi session file is missing.
17
+ Manual recovery no longer depends on model context; managed-path validation and
18
+ index-preserving Git integration stay intact.
19
+ - Record child exits before RPC settlement with their exit code or signal instead
20
+ of returning only partial output. Clear stale provider errors after successful Pi
21
+ retries, expose missing diagnostic evidence explicitly, and keep individual failed
22
+ tool calls separate from a terminal run failure. Preserve safe pre-prompt startup
23
+ retries and keep the last recorded cause when that retry budget is exhausted.
24
+ - Require Pi 0.85.0 and reuse its exported RPC command/response types. Include its
25
+ official server package as a peer and development dependency: the unbundled SDK
26
+ and CLI require it at runtime, not only in tests.
27
+ - Discover actual built-in/custom role definitions in setup, respecting project
28
+ trust. Saving selection removes unavailable names and overrides without retired-role
29
+ aliases or config migration; real disabled and never-configured custom roles remain selectable.
30
+
31
+ ## 4.3.9
32
+
33
+ - Add optional bounded stable `phaseId` and exact declarative write `scope` claims to single
34
+ and parallel dispatches. Phase identity is immutable across task rewrites and resume; scope
35
+ is monotonic across retained generations and survives durable v1 restore. Exact task+cwd
36
+ remains the compatibility fallback.
37
+ - Reject deterministic duplicates and declared writer-scope conflicts before parallel batch
38
+ allocation. Fresh single and resumed writers also reject normalized absolute scope overlap
39
+ with active leases, without requiring equal caller cwd. Parallel calls that omit scope remain
40
+ compatible and explicitly report `independence not verified`; declared claims do not prove
41
+ natural-language task independence. Scope is conflict metadata, not permissions or a sandbox.
42
+ - Add the advisory-only `subagent_risk` tool. Without a model call it resolves the repository
43
+ root, reads root-relative tracked and untracked changes from `HEAD`, and applies fixed
44
+ explainable rules for concurrency, trust-boundary, persistence-compatibility, and
45
+ failure-cancellation risk. It propagates cancellation, and suggests but never dispatches or
46
+ requires a Sentinel review.
47
+
7
48
  ## 4.3.8
8
49
 
9
50
  - Restore `sentinel` as an optional fresh-context reviewer instead of the mandatory
package/README.md CHANGED
@@ -12,12 +12,12 @@ once and your main agent delegates on its own.
12
12
 
13
13
  ## What's new
14
14
 
15
- **4.3.8** — `sentinel` is back as an optional fresh-context reviewer. It reads a
16
- completed diff with no memory of how it was written and returns only evidence-backed
17
- defects and test gaps; the main agent dispatches it for diffs that touch concurrency,
18
- trust boundaries, persistence, or failure paths, never as a fixed pre-commit ritual, and
19
- routes a finding back to the thread that owns the change. Configs written by
20
- 4.3.5–4.3.7 adopt it once; a deliberate disable sticks.
15
+ **4.3.10** — adds read-only `subagent_status` and removes `subagent_control`
16
+ (`steer`, `park`, and `resume`). Runs are one-shot; main handles unfinished work.
17
+ Failure reporting preserves real RPC exit/provider diagnostics and distinguishes
18
+ a failed run from an intentionally failing tool call. Requires Pi **0.85.0** and
19
+ reuses its official RPC types. Setup discovers actual role definitions instead
20
+ of inventing roles from saved names; there are no retired-role aliases or migrations.
21
21
 
22
22
  See [CHANGELOG.md](./CHANGELOG.md).
23
23
 
@@ -28,7 +28,7 @@ See [CHANGELOG.md](./CHANGELOG.md).
28
28
  - [The team](#the-team)
29
29
  - [Dispatching work](#dispatching-work)
30
30
  - [Parallel edits](#parallel-edits)
31
- - [Threads: steer, resume, park, stop](#threads-steer-resume-park-stop)
31
+ - [Runs: status and stop](#runs-status-and-stop)
32
32
  - [Live status and results](#live-status-and-results)
33
33
  - [Models, thinking, and tools](#models-thinking-and-tools)
34
34
  - [Configuration](#configuration)
@@ -50,24 +50,24 @@ back — with you. This extension owns them:
50
50
  handoff costs. Every brief carries the objective and done condition, exact paths,
51
51
  facts already established with citations, boundaries, and the expected output, so a
52
52
  child starts from evidence instead of re-deriving it.
53
- - One normalized task and working directory owns its phase: an exact duplicate of an
54
- active run is rejected, and an exact re-run of a finished brief with retained
55
- context is rejected in favor of resuming it, so the same work is never bought twice.
56
- - Follow-up work stays on the same thread: `steer` a running phase, `resume` or
57
- `park` a thread with its retained context, `stop` a phase the evidence made moot.
53
+ - A stable `phaseId` owns a logical phase in one resolved working directory even if
54
+ its task wording changes. IDs are 1–80 ASCII letters, numbers, or `._:-`, starting
55
+ with a letter or number, so lease output stays single-line. Exact normalized task+cwd
56
+ remains the backward-compatible fallback for old calls.
57
+ - Runs are one-shot. Use `subagent_status` to inspect them and `subagent_stop` to
58
+ cancel them; main handles unfinished work instead of continuing a failed child.
58
59
  - Background completions and stop results arrive at the next parent model boundary;
59
60
  `wait: true` returns the same result in-turn instead. A run uses exactly one route.
60
61
  - Parallel writers use detached Git worktrees without touching your index.
61
- Worktree setup obeys the bounded queue; final integration releases its process
62
- slot.
63
- - Interrupted threads retain their session for resume after reload or crash; a
64
- configured child-model failure continues the same session on the main model.
65
- - Start, restore, and integration failures surface with retained recovery paths
66
- instead of becoming silent hangs.
62
+ Worktree setup obeys the bounded queue; final integration releases its process slot.
63
+ - Interrupted work retains artifacts for manual recovery after reload or crash.
64
+ Within a run, a configured child-model failure can still hand off to the main model.
65
+ - Failure notifications include available reasons, and status keeps terminal facts
66
+ and retained recovery paths queryable for the current parent session.
67
67
 
68
68
  ## Install
69
69
 
70
- Requires **pi >= 0.84.4** and **Node.js >= 22.19.0**.
70
+ Requires **pi >= 0.85.0** and **Node.js >= 22.19.0**.
71
71
 
72
72
  ```bash
73
73
  pi install npm:@ferris1225/pi-subagents
@@ -117,20 +117,41 @@ the objective and its done condition, exact paths and symbols, facts already
117
117
  established (with citations), boundaries, and the expected output shape — which is
118
118
  what the injected delegation guidance produces when the main agent dispatches for you.
119
119
 
120
+ Children run the official `pi --mode rpc` server, using Pi's exported command/response
121
+ types and its own session persistence. There is no separate subagent protocol. The
122
+ host transport remains local because Pi 0.85.0's `RpcClient` cannot attach to our
123
+ child process or provide process-tree shutdown, bounded abort coordination, and
124
+ cancellation of child extension dialogs.
125
+
120
126
  ## Dispatching work
121
127
 
122
128
  ```ts
123
129
  // One task
124
130
  subagent({
131
+ phaseId: "cache-invalidation-fix",
125
132
  agent: "artisan",
126
133
  task: "Fix the cache invalidation bug in src/cache, add regression tests, run the checks.",
134
+ scope: {
135
+ paths: ["src/cache"],
136
+ symbols: [{ path: "test/cache.test.ts", name: "invalidates stale entries" }],
137
+ },
127
138
  });
128
139
 
129
140
  // Parallel only when each scope independently justifies a child
130
141
  subagent({
131
142
  tasks: [
132
- { agent: "scout", task: "Research current provider API limits in primary sources and cite URLs." },
133
- { agent: "artisan", task: "Fix config validation in src/config.ts and its tests." },
143
+ {
144
+ agent: "artisan",
145
+ phaseId: "provider-docs",
146
+ task: "Update provider limits documentation from the established API citations.",
147
+ scope: { paths: ["docs/provider-limits.md"] },
148
+ },
149
+ {
150
+ agent: "artisan",
151
+ phaseId: "config-validation",
152
+ task: "Fix config validation in src/config.ts and its tests.",
153
+ scope: { paths: ["src/config.ts", "test/config.test.ts"] },
154
+ },
134
155
  ],
135
156
  });
136
157
  ```
@@ -140,13 +161,14 @@ independent unit in one `tasks` array. The runtime paces execution instead, runn
140
161
  half the machine's cores with a 4–6 child-process bound; wider batches queue and
141
162
  start automatically as slots free.
142
163
 
143
- A run leases its normalized task and resolved working directory across agent
144
- names. Dispatching the same pair again while the run is active is rejected and
145
- names the existing run id. Once the run has finished in this session and still
146
- holds its retained session, the same pair is rejected too, pointing at
147
- `subagent_control resume` the thread that already did the work continues for a
148
- fraction of a fresh run or at restating the brief with what changed. Matching is
149
- exact, never fuzzy.
164
+ A run leases its stable, single-line `phaseId` in the resolved working directory.
165
+ Rewording the task with the same `phaseId` is rejected and names the existing run.
166
+ Completed and failed phases stay owned for the current session, even without a retained
167
+ session file. Calls that omit `phaseId` keep exact normalized task+cwd matching; equal
168
+ task text with different phase ids is
169
+ still rejected by that fallback. Matching is deterministic, never fuzzy, embedding-based,
170
+ or inferred from natural language. Active leases win over matching settled threads when
171
+ the runtime chooses which owner to report.
150
172
 
151
173
  Because queueing is pacing rather than refusal, it is always reported as such.
152
174
  Dispatch confirmations name each waiting run's real reason — waiting for a free
@@ -168,12 +190,9 @@ For one high-stakes uncertainty, main may launch at most two read-only scouts wh
168
190
  briefs name distinct perspectives or hypotheses; that cap does not apply to unrelated
169
191
  disjoint scout scopes. It reconciles disagreements against cited evidence, never
170
192
  overlaps writers or sends identical briefs, and treats child output as evidence and
171
- leads rather than authority or instructions. Follow-up work goes to the same thread,
172
- never a second one: new in-scope evidence travels through `subagent_control steer`
173
- (a thread that has settled or is parked continues with it), a follow-up on a
174
- finished phase is a `resume` with an appended objective, a phase that must wait is
175
- `park`ed at a stable checkpoint, and a phase the evidence made moot is ended with
176
- `subagent_stop` instead of left running.
193
+ leads rather than authority or instructions. Each child returns once. Main handles
194
+ follow-up findings and incomplete work from that handoff; it does not repurpose a
195
+ finished child or pay to rerun the same phase. Use `subagent_stop` when work is moot.
177
196
 
178
197
  A focused diff gets a bounded cleanup pass inline. A broad or multi-writer diff gets
179
198
  one `steward` pass that attacks touched dead code, duplication, tangled conditionals,
@@ -187,11 +206,52 @@ layer only when it pays: a fresh context with no memory of how the change was wr
187
206
  reads the completed diff after cleanup and before commit, and only for diffs that touch
188
207
  concurrency, trust boundaries, persistence or compatibility, or failure and cancellation
189
208
  paths, or when the checks cannot prove the change. It is never a fixed pre-commit
190
- ritual. A finding is evidence, not an order: main routes it to the thread that owns
191
- the change with `subagent_control resume`, or fixes it inline when that is cheaper.
209
+ ritual. A finding is evidence, not an order: main checks the cited evidence and
210
+ makes the necessary correction itself.
211
+
212
+ `subagent_risk({})` is an advisory-only, no-model-call check over tracked and untracked
213
+ changes relative to `HEAD`. It resolves the repository root first, so a nested `cwd` still
214
+ returns repository-root-relative paths, including untracked files outside that subdirectory.
215
+ Its fixed case-insensitive path-token rules flag:
216
+ `concurrency` (`thread`, `queue`, `parallel`, `dispatch`, locks/races and related tokens);
217
+ `trust-boundary` (`auth`, credentials, permissions, policy, secrets, sandbox, security, trust, tokens);
218
+ `persistence-compatibility` (durable state, manifests, migrations, restore, schemas,
219
+ serialization/storage); and `failure-cancellation` (abort, cancel, errors/failures, recovery,
220
+ retry, stop, timeout). It returns the changed paths, matched categories, and whether those
221
+ rules suggest Sentinel. If Git or `HEAD` is unavailable, it reports advisory unavailable; an
222
+ aborted tool call propagates cancellation instead of converting it to an advisory result. It
223
+ never blocks, starts a child, or automatically dispatches Sentinel.
224
+
225
+ This classifier is intentionally conservative and explainable: it only sees path names, so
226
+ it can produce false positives and miss risky behavior hidden behind neutral names. Main
227
+ still decides whether review pays from the actual diff, test evidence, handoff cost, and the
228
+ complete conversation. The runtime can enforce explicit phase/scope admission, but cannot
229
+ safely force the natural-language judgment of whether work is worth delegating.
192
230
 
193
231
  ## Parallel edits
194
232
 
233
+ `scope` is declarative admission metadata for expected writes, not access control. `paths`
234
+ contains exact file or directory paths; `symbols` contains exact `{ path, name }` claims.
235
+ Paths resolve from each task's caller-facing cwd and use case-insensitive comparison on
236
+ Windows. Wildcard `*` and `?` inputs are rejected; other punctuation is treated literally,
237
+ so paths such as `app/[id]/page.tsx` are valid exact claims. A path claim overlaps the same
238
+ path, an ancestor/descendant path, or a symbol under that path; identical path+symbol
239
+ claims overlap, while two different symbols in the same file may run together.
240
+
241
+ Fresh dispatches check declared writer scope against active, interrupted, or settling
242
+ writer leases before allocating a run. Scope
243
+ comparison uses normalized absolute claims rather than requiring equal caller cwd, so a
244
+ repo-root claim still conflicts with the same path claimed from a nested cwd. Settled
245
+ threads do not block a later phase solely because it edits the same scope.
246
+
247
+ Before allocating any run in a parallel call, the runtime also rejects deterministic phase
248
+ duplicates within the batch or against existing active/retained threads, then compares
249
+ declared writer scopes across the whole batch. A definite conflict rejects the whole batch
250
+ with zero starts. Parallel calls without `scope` remain valid, but their tool result and
251
+ launch receipt say `independence not verified`; that means the contract lacked enough
252
+ metadata, not that overlap was proved safe. Single calls never make a batch-independence
253
+ claim. The existing shared-checkout writer lane remains the final serialization boundary.
254
+
195
255
  - Single tasks use your checkout. Every parallel write-capable agent (`artisan`,
196
256
  `steward`, and custom writers) defaults to a detached Git worktree, so
197
257
  parallel writers run at the same time. Worktree mode needs a committed `HEAD`;
@@ -224,81 +284,60 @@ Third-party Pi packages execute as trusted code and must be reviewed accordingly
224
284
  already been applied and only the cleanup failed, the next session start
225
285
  removes the retained copy itself and clears the notice.
226
286
 
227
- ## Threads: steer, resume, park, stop
287
+ ## Runs: status and stop
228
288
 
229
- Every dispatch returns a stable `#id`, which is the handle for the thread tools:
289
+ Every dispatch returns a stable `#id`. Runs are one-shot: there is no
290
+ `subagent_control`, `steer`, `park`, or `resume` interface. Main takes over failed
291
+ or incomplete work using the child's partial edits and artifacts. A different
292
+ deliverable needs a new phase and brief, not a recycled thread.
230
293
 
231
- | Tool | What it does |
232
- | ------------------ | ------------ |
233
- | `subagent_control` | `steer` a running RPC attempt with additional evidence/guidance, continuing the same thread with it when the thread has settled or is parked; `resume` a parked/settled thread with an optional appended `objective`; `park` a running thread at a stable checkpoint, keeping its session and worktree for a later resume. |
234
- | `subagent_stop` | Destructively cancel, deliver partial output, and retire the thread. Steering and follow-up messages still queued in the child are dropped so nothing can revive it later. |
294
+ | Tool | What it does |
295
+ | ---- | ------------ |
296
+ | `subagent_status` | Read-only inspection. Omit `id` to list this parent session's runs, or pass an exact numeric `id` to inspect one. |
297
+ | `subagent_stop` | Destructively cancel/retire a run by id/prefix, or all active runs with `all: true`. Delivers partial output and finalizes isolated changes. |
235
298
 
236
299
  ```ts
237
- subagent_control({ action: "steer", id: 7, objective: "The failing request used an expired token; account for that evidence." });
238
- subagent_control({ action: "park", id: 7 });
239
- subagent_control({ action: "resume", id: 7, objective: "Finish the tests." });
300
+ subagent_status({});
301
+ subagent_status({ id: 7 });
302
+ subagent_stop({ id: "7" });
240
303
  ```
241
304
 
242
- `steer` requires a nonblank `objective`. While the child RPC is running, it adds
243
- guidance to the current phase without replacing the original task. If the thread has
244
- already reached `completed`, `failed`, or `parked` including a generation that settles
245
- between the state check and RPC acceptance — the control call resumes the same stable
246
- id, reuses retained context when available, and supplies the guidance as its appended
247
- objective, so evidence is never re-bought by a second dispatch. Queued, starting,
248
- retrying, resuming, interrupting, stopped, retired, and missing threads are rejected
249
- without changing them. Steering ACKs are bounded, and steering/stop are serialized so
250
- stop can clear queued child messages and abort without a stale steer landing afterward.
251
-
252
- `park` pauses a running thread at its next safe point: the child is interrupted the
253
- same way a session shutdown interrupts it, but the thread returns as `parked` rather
254
- than failed, its retained session and any active worktree are kept, and its durable
255
- record is written immediately so the checkpoint survives a reload. Nothing is
256
- integrated or delivered on park; the tool result carries the usage so far and the
257
- resume handle. Only an active running attempt with a retained session can be parked;
258
- a run that has not started has nothing worth keeping, so `subagent_stop` discards it.
259
-
260
- A resumed child is told that its earlier work is preserved and must not be redone, and
261
- that the workspace may have changed while the thread was inactive — main may have
262
- integrated sibling worktrees or edited the tree so it re-reads a file before editing
263
- it unless it read it during the continuation. A resume with an appended objective is
264
- framed as the same thread continuing on top of finished work, never as a restart.
265
-
266
- There is no status, polling, or separate wait tool. A background dispatch returns
267
- a launch receipt, then its completion is steered at the next safe parent boundary—after
268
- the current tool calls and before the next model call. This wakes the main model without
269
- waiting for its whole run to end.
270
-
271
- `wait: true` instead holds that tool call until its new runs settle,
272
- which is useful for one-shot `pi -p` sessions. It claims the delivery route before
273
- launch, so the same result cannot also arrive as a background completion; if the
274
- parent turn is aborted, delivery falls back to the completion path.
275
- Use `wait: true` only when the result is the immediate dependency. Otherwise
276
- leave it in the background and continue real disjoint work — never burn main
277
- context on `sleep` or polling while a child keeps running.
278
-
279
- The wait has no timer chosen by the model: it resolves when its run settles, and
280
- a parked run returns its resume handle. Control operations are bounded so they do
281
- not hang on a generation that is still settling.
282
-
283
- A thread stays durable while its work is unfinished. Parked sessions, worktree
284
- checkpoints, and result excerpts are recorded under the per-project storage root,
285
- so reload, restart, or crash produces a resumable checkpoint. An isolated thread
286
- continues in its original worktree.
287
-
288
- Restore runs at session start. `subagent_control`, `subagent_stop`, prompt
289
- injection, and new dispatches wait for it, so a parked id cannot be reported
290
- missing or reused. If a recorded worktree is gone, the run is surfaced as failed
291
- and non-resumable while its retained session and recovery record remain available
292
- for inspection or destructive stop.
293
-
294
- Persisted sessions and worktrees are resumed or removed only when their canonical paths
295
- match the current project's managed storage layout and repository. Invalid records are
296
- dropped without following or deleting their targets. Recovery-owned worktrees and patches
297
- remain protected from startup sweeps and project-root retention until recovery is announced.
298
-
299
- Only interrupted work needs a record, so a thread that completes or fails cleanly
300
- drops its own. That also means a reload keeps interrupted threads resumable, while
301
- threads that had already finished keep only their delivered result.
305
+ Status reads runtime state without starting, stopping, continuing, or waiting for a
306
+ child to finish. It includes the phase/task summary, activity, elapsed time, model,
307
+ usage, terminal diagnostics, and available result/session/recovery paths. States
308
+ distinguish `queued`, `running`, `interrupting`, `settling` (Git finalization),
309
+ `completed`, `failed`, `stopped`, and `interrupted` (recovered unfinished work).
310
+ Queued runs report their actual wait reason. Settled runs remain queryable in the
311
+ current parent session even after their transient widget rows disappear.
312
+
313
+ The tool returns these facts in Pi's existing structured `details.runs` field.
314
+ An individual run's failure does not make a successful status lookup a tool error;
315
+ an unknown `id` does. This is runtime-authored data, not a requirement for children
316
+ to generate strict JSON. Agent-written reports remain evidence to verify.
317
+
318
+ A background dispatch returns a launch receipt, then its completion arrives at
319
+ the next safe parent boundary—after current tool calls and before the next model
320
+ call. `wait: true` instead holds the dispatch until its new runs settle, which is
321
+ useful for one-shot `pi -p` sessions or an immediate dependency. Each run has one
322
+ delivery route; aborting the waiting parent turn transfers delivery to the
323
+ background path. Use status for on-demand inspection, not a polling or sleep loop.
324
+
325
+ Stop drops messages still queued inside Pi, performs a bounded RPC abort, and
326
+ terminates the child process tree. It retires the session; it never starts another
327
+ attempt. Worktree integration failures keep their recovery artifacts.
328
+
329
+ Interrupted work retains a durable record and any session/worktree artifacts for
330
+ manual recovery after reload or crash. Missing session files no longer discard
331
+ isolated edits. Restore runs at session start; lookup tools, prompt injection, and
332
+ fresh dispatch wait for that pass so an existing id cannot be reported missing or
333
+ reused. Missing recorded worktrees surface as failures without discarding the
334
+ remaining recovery evidence.
335
+
336
+ Canonical managed-path and repository validation remains in place. Invalid records
337
+ are dropped without following or deleting their targets. Recovery-owned worktrees
338
+ and patches stay protected from startup sweeps and project-root retention.
339
+ Completed/failed runs drop their durable thread record; after reload, inspect their
340
+ delivered result instead of expecting them in the current-session status list.
302
341
 
303
342
  ## Live status and results
304
343
 
@@ -318,14 +357,13 @@ and, dim under the label column, what it is doing right now:
318
357
  ● #15 scout src/models.ts · ↑1.2k ↓8.4k R31.0k W1.1k $0.0900 · openai/gpt-5-mini · think:low · 3m07s
319
358
  ↳ grep fallback
320
359
  ○ #23 artisan src/config.ts · repo lane
321
- ○ #24 artisan tests/config.test.ts · queued · 5m02s
360
+ ○ #24 artisan tests/config.test.ts · queued · 5m02s
322
361
  ```
323
362
 
324
363
  Telemetry drops leftmost-first when a row runs out of width (badge, wait state,
325
364
  usage, model, thinking) while elapsed survives every width. Queued rows state
326
365
  what they actually wait for — `queued` for a free process slot, `repo lane`
327
- for shared-checkout write serialization, or `starting` and a resumed thread
328
- carries a dim `↻` in its agent column with its cumulative time. The widget is
366
+ for shared-checkout write serialization, or `starting`. The widget is
329
367
  capped at ten lines: when many runs are live, extra runs collapse into a
330
368
  `… +N more` marker so the editor keeps its space.
331
369
 
@@ -379,7 +417,7 @@ no `thinking` field in agent Markdown. Precedence: your setup override > the
379
417
  role default, then the model clamp. There is no separate vision mode — assign
380
418
  a multimodal model and name the image paths in the task.
381
419
 
382
- Every dispatch, resume, retry, and fallback snapshots the parent's active tools,
420
+ Every dispatch, startup retry, and model fallback snapshots the parent's active tools,
383
421
  and all `subagent*` tools are removed so children remain leaves. A role without
384
422
  an explicit list inherits that snapshot; an explicit list is a strict
385
423
  intersection, so active extension tools are available only when named. A declared
@@ -406,8 +444,12 @@ search snippets, records material dates/versions, and marks uncertainty.
406
444
  `/subagents-setup` opens the original settings menu: enable or disable roles,
407
445
  configure one enabled role's model and thinking level, or walk through a full
408
446
  re-setup. `Esc` moves back through the menu stack, and model lists support fuzzy
409
- search. Built-in and previously configured custom roles remain available in the
410
- enable menu. Other settings live in
447
+ search. The enable menu discovers built-ins and actual custom role files in the configured
448
+ scope, including roles never configured before. Project files require Pi's project trust.
449
+ Config-only names are not role definitions and never appear in the enable or configure
450
+ picker. Saving an enable selection or full setup discards unavailable role names and their
451
+ model/thinking settings; no retired-name aliases or configuration migration are applied.
452
+ To start over, remove `pi-subagents.json` and run `/subagents-setup` again. Other settings live in
411
453
  `~/.pi/agent/pi-subagents.json` (following `PI_CODING_AGENT_DIR`):
412
454
 
413
455
  ```json
@@ -425,7 +467,7 @@ enable menu. Other settings live in
425
467
  | Field | Meaning |
426
468
  | --------------------- | ------- |
427
469
  | `enabledAgents` | Agents available for discovery and delegation. `[]` disables all. |
428
- | `knownAgents` | Roles already surfaced by setup; retains disabled custom roles and tracks built-in adoption. |
470
+ | `knownAgents` | Catalog shown by setup; tracks built-in adoption, but cannot define a custom role without a file. |
429
471
  | `agentModels` | Optional model per agent; missing means the current main model. |
430
472
  | `agentThinkingLevels` | Optional setup override per agent; missing means the role default. |
431
473
  | `maxResultLines` | Lines kept in a completion message before the artifact takes over. Default `40`. |
@@ -437,9 +479,9 @@ automatically. `enabledAgents` is authoritative after catalog adoption: a newly
437
479
  shipped built-in is appended once, then `knownAgents` records that it was surfaced
438
480
  so a deliberate later disable remains disabled. `sentinel` returns through that
439
481
  rule: a config written by 4.3.5–4.3.7, which removed it, enables it once on the next
440
- load; turn it off in `/subagents-setup` and it stays off. Custom roles and other
441
- known-agent entries remain intact. Invalid known fields fall back safely, and unknown
442
- fields are dropped when canonical config is persisted.
482
+ load; turn it off in `/subagents-setup` and it stays off. Available custom roles remain
483
+ selectable even when disabled. Invalid known fields fall back safely, and unknown fields
484
+ are dropped when canonical config is persisted.
443
485
 
444
486
  At session start, model overrides that pi no longer reports are removed with a
445
487
  one-time notice. If pi's own session compaction fails mid-thread, a notice surfaces
@@ -481,7 +523,7 @@ that removes it, so this directory does not grow without bound:
481
523
  | `pi-subagents.json` | Your configuration | Never — it is yours |
482
524
  | `ferris-pi-subagents/pi-subagents-recovery.json` | Worktree integration and cleanup failures | When the retained patch or worktree it points at is gone |
483
525
  | `ferris-pi-subagents/<project>/pi-subagents-threads.json` | One record per interrupted thread | When the thread settles, or after 30 days |
484
- | `ferris-pi-subagents/<project>/sessions/` | Retained child sessions that a resume continues from | When the thread settles or its retained record is removed |
526
+ | `ferris-pi-subagents/<project>/sessions/` | Child sessions for in-run fallback and manual recovery | When the owning session ends and no recovery record claims them |
485
527
  | `ferris-pi-subagents/<project>/worktrees/` | Isolated checkouts for parallel writers | On integration, or when no thread/recovery record claims them |
486
528
  | `ferris-pi-subagents/<project>/results/` | Full text of truncated results | After 7 days, or beyond 50 per project |
487
529
  | `ferris-pi-subagents/<project>/tmp/` | Child prompt copies and the no-retry policy shim | When its owning process exits |
@@ -489,7 +531,7 @@ that removes it, so this directory does not grow without bound:
489
531
 
490
532
  Cleanup runs at session start and is deliberately conservative. A directory goes
491
533
  away only when the process that created it is gone and no valid manifest record still
492
- claims it, so a live sibling pi instance never loses state and parked or recovery-owned
534
+ claims it, so a live sibling pi instance never loses state and interrupted or recovery-owned
493
535
  work outlives its own process by design. Thread and recovery references always beat an
494
536
  age rule.
495
537
 
@@ -506,6 +548,12 @@ grouped by responsibility under `src/`: configuration, delegation, execution, is
506
548
  lifecycle, and presentation. Thread restoration, shared lifecycle coordination, RPC control,
507
549
  and Git command execution live in focused modules rather than oversized catch-all files.
508
550
 
551
+ The test runner uses Node 22 or 24; Node 26 removed `--experimental-transform-types`.
552
+ Pi 0.85.0's unbundled SDK and CLI import `@earendil-works/pi-server` without declaring
553
+ it. This project declares the official server package as a peer (and a development
554
+ dependency), so npm can resolve it alongside the SDK in consumer installations.
555
+ It is not bundled into the extension, and no replacement RPC server is introduced.
556
+
509
557
  ## Changelog
510
558
 
511
559
  See [CHANGELOG.md](./CHANGELOG.md) for published release notes.
package/index.ts CHANGED
@@ -24,6 +24,7 @@ import { getConfigPath, loadConfig } from "./src/configuration/config.ts";
24
24
  import { runSetup } from "./src/configuration/setup.ts";
25
25
  import { discoverAgents } from "./src/delegation/agents.ts";
26
26
  import { registerSubagentTool } from "./src/delegation/dispatch.ts";
27
+ import { registerSubagentRiskTool } from "./src/delegation/risk.ts";
27
28
  import { buildDelegationDirective } from "./src/delegation/prompt.ts";
28
29
  import { currentSubagentDepth } from "./src/execution/spawn.ts";
29
30
  import { createRuntime } from "./src/lifecycle/runtime.ts";
@@ -69,6 +70,7 @@ export default function (pi: ExtensionAPI): void {
69
70
  });
70
71
 
71
72
  registerSubagentTool(pi, runtime);
73
+ registerSubagentRiskTool(pi);
72
74
  registerLookupTools(pi, runtime);
73
75
 
74
76
  pi.registerCommand("subagents-setup", {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ferris1225/pi-subagents",
3
- "version": "4.3.8",
4
- "description": "A managed sub-agent team for pi: scout, artisan, steward, and sentinel roles, durable threads, model fallback, and Git worktree isolation.",
3
+ "version": "4.3.10",
4
+ "description": "A managed sub-agent team for pi: scout, artisan, steward, and sentinel roles, one-shot runs, read-only status, and Git worktree isolation.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -43,17 +43,19 @@
43
43
  "prepack": "npm run check"
44
44
  },
45
45
  "peerDependencies": {
46
- "@earendil-works/pi-agent-core": ">=0.84.4",
47
- "@earendil-works/pi-ai": ">=0.84.4",
48
- "@earendil-works/pi-coding-agent": ">=0.84.4",
49
- "@earendil-works/pi-tui": ">=0.84.4",
46
+ "@earendil-works/pi-agent-core": ">=0.85.0",
47
+ "@earendil-works/pi-ai": ">=0.85.0",
48
+ "@earendil-works/pi-coding-agent": ">=0.85.0",
49
+ "@earendil-works/pi-server": ">=0.85.0",
50
+ "@earendil-works/pi-tui": ">=0.85.0",
50
51
  "typebox": "*"
51
52
  },
52
53
  "devDependencies": {
53
- "@earendil-works/pi-agent-core": "^0.84.4",
54
- "@earendil-works/pi-ai": "^0.84.4",
55
- "@earendil-works/pi-coding-agent": "^0.84.4",
56
- "@earendil-works/pi-tui": "^0.84.4",
54
+ "@earendil-works/pi-agent-core": "^0.85.0",
55
+ "@earendil-works/pi-ai": "^0.85.0",
56
+ "@earendil-works/pi-coding-agent": "^0.85.0",
57
+ "@earendil-works/pi-server": "^0.85.0",
58
+ "@earendil-works/pi-tui": "^0.85.0",
57
59
  "@types/node": "^22.10.0",
58
60
  "typebox": "^1.3.9",
59
61
  "typescript": "^5.9.0"
@@ -9,6 +9,7 @@
9
9
  import { stat } from "node:fs/promises";
10
10
  import type { Api, Model } from "@earendil-works/pi-ai";
11
11
  import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
12
+ import { discoverAgents } from "../delegation/agents.ts";
12
13
  import {
13
14
  AGENT_PROFILES,
14
15
  BUILTIN_AGENT_NAMES,
@@ -46,16 +47,13 @@ const THINKING_LEVEL_HINTS: Record<ThinkingLevel, string> = {
46
47
  max: "strongest reasoning",
47
48
  };
48
49
 
49
- function setupAgentNames(config: SubagentsConfig): string[] {
50
- return [
51
- ...new Set([
52
- ...BUILTIN_AGENT_NAMES,
53
- ...config.knownAgents,
54
- ...config.enabledAgents,
55
- ...Object.keys(config.agentModels),
56
- ...Object.keys(config.agentThinkingLevels),
57
- ]),
58
- ];
50
+ function setupAgentNames(ctx: ExtensionCommandContext, config: SubagentsConfig): string[] {
51
+ const { agents } = discoverAgents(ctx.cwd, {
52
+ scope: config.agentScope,
53
+ projectTrusted: ctx.isProjectTrusted?.() === true,
54
+ });
55
+ const available = new Set(agents.map((agent) => agent.name));
56
+ return [...new Set([...BUILTIN_AGENT_NAMES, ...available])].filter((name) => available.has(name));
59
57
  }
60
58
 
61
59
  function agentPickerItems(names: readonly string[]): Array<{ value: string; label: string; description: string }> {
@@ -87,12 +85,13 @@ async function pickEnabledAgents(
87
85
  ctx: ExtensionCommandContext,
88
86
  config: SubagentsConfig,
89
87
  ): Promise<string[] | undefined> {
88
+ const names = setupAgentNames(ctx, config);
90
89
  return promptSelectMany(
91
90
  ctx,
92
91
  "Which agents should run?",
93
92
  "Each line is a role and its job. Space toggles • Enter confirms • Esc back",
94
- agentPickerItems(setupAgentNames(config)),
95
- config.enabledAgents,
93
+ agentPickerItems(names),
94
+ config.enabledAgents.filter((name) => names.includes(name)),
96
95
  );
97
96
  }
98
97
 
@@ -210,8 +209,10 @@ async function configureOneAgent(
210
209
  ctx: ExtensionCommandContext,
211
210
  config: SubagentsConfig,
212
211
  ): Promise<ConfiguredAgentChoice | undefined> {
212
+ const available = setupAgentNames(ctx, config);
213
+ const enabled = config.enabledAgents.filter((name) => available.includes(name));
213
214
  while (true) {
214
- const name = await pickAgentToConfigure(ctx, config.enabledAgents);
215
+ const name = await pickAgentToConfigure(ctx, enabled);
215
216
  if (name === undefined) return undefined;
216
217
  const profile = agentProfile(name);
217
218
  if (profile) ctx.ui.notify(`${name}: ${profile.remark}`, "info");
@@ -289,7 +290,7 @@ async function runFullSetup(ctx: ExtensionCommandContext, configPath: string, ba
289
290
 
290
291
  const next: SubagentsConfig = {
291
292
  enabledAgents: enabled,
292
- knownAgents: setupAgentNames(base),
293
+ knownAgents: setupAgentNames(ctx, base),
293
294
  agentModels,
294
295
  agentThinkingLevels: keepAgentEntries(base.agentThinkingLevels, enabled),
295
296
  maxResultLines: base.maxResultLines,
@@ -326,6 +327,7 @@ async function runMenu(ctx: ExtensionCommandContext, configPath: string, config:
326
327
  const enabled = await pickEnabledAgents(ctx, config);
327
328
  if (enabled === undefined) continue;
328
329
  next.enabledAgents = enabled;
330
+ next.knownAgents = setupAgentNames(ctx, config);
329
331
  next.agentModels = keepAgentEntries(next.agentModels, enabled);
330
332
  next.agentThinkingLevels = keepAgentEntries(next.agentThinkingLevels, enabled);
331
333
  } else {