@cr1ms0n/pi-subagent 0.8.9 → 0.10.0

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,569 +1,130 @@
1
- # @cr1ms0n/pi-subagent
2
-
3
- This is an independent community fork of **@parke.dev/pi-subagent 0.8.0**, originally by Luke Parke. It is not an official upstream release. The original MIT license and copyright are preserved. Upstream source: [LukasParke/pi-extensions](https://github.com/LukasParke/pi-extensions/tree/main/packages/pi-subagent).
4
-
5
- This fork requires a user-owned `modelPolicy` in `~/.pi/subagent.json` before new subagent tasks can run, and displays actual models in the TUI.
6
-
7
- Production-grade isolated subagents for [Pi](https://github.com/badlogic/pi-mono).
8
-
9
- Delegate research, parallel exploration, and clean-context review to child Pi
10
- processes. Named agent personas, cancellable background runs with completion
11
- notifications and a live widget, mid-run steering, graceful budget wrap-ups,
12
- automatic retry with model fallback, a stall watchdog, session resume and
13
- context forking, worktree isolation with a diff/apply/discard loop, capability
14
- profiles, a root/subagent/combined cost ledger, and a TUI inspector.
15
-
16
- ## Install
17
-
18
- Pi packages install from **npm**, **git**, or a **local path**:
19
-
20
- ```bash
21
- # npm (scoped; surfaces on the pi.dev gallery via the pi-package keyword)
22
- # Note: unscoped "pi-subagent" is rejected by npm as too similar to "pi-sub-agent".
23
- pi install npm:@cr1ms0n/pi-subagent@0.8.1
24
-
25
- # latest npm
26
- pi install npm:@cr1ms0n/pi-subagent
27
-
28
- # npm is the supported package install path from the monorepo.
29
- # For local development, install this workspace directly:
30
- pi install /absolute/path/to/pi-extensions/packages/pi-subagent
31
-
32
- # local checkout
33
- pi install /absolute/path/to/pi-subagent
34
- ```
35
-
36
- Then start Pi normally. The package registers:
37
-
38
- - tool: `subagent`
39
- - command: `/subagents` (run inspector overlay)
40
- - command: `/subagent-cost` (parent / subagent / combined usage on demand)
41
-
42
- This fork is published manually after pack-content and offline checks — see
43
- [docs/RELEASING.md](./docs/RELEASING.md).
44
-
45
- ## Quick usage
46
-
47
- ```ts
48
- // Single foreground task; `model` must exactly match ~/.pi/subagent.json modelPolicy.
49
- { task: "Find all call sites of parseConfig and summarize patterns.", description: "Map parseConfig usage", model: "<provider/model-id>" }
50
-
51
- // Named agent — persona prompt from .pi/agents/reviewer.md; model still comes from modelPolicy.
52
- { task: "Review this diff for security issues", agent: "reviewer", model: "<provider/model-id>" }
53
-
54
- // Parallel read-only explorers (default profile: explore); each task needs its mapped model.
55
- {
56
- tasks: [
57
- { task: "Map auth middleware flow", description: "Auth flow map", model: "<provider/model-id>" },
58
- { task: "List all env vars used in server/", description: "Env var inventory", model: "<provider/model-id>" }
59
- ]
60
- }
61
-
62
- // Parallel research with automatic fan-in: one read-only child folds all
63
- // outputs into a single brief, delivered first.
64
- {
65
- tasks: [
66
- { task: "Audit backend error handling", description: "Backend audit", model: "<exact model from current modelPolicy route>" },
67
- { task: "Audit frontend error handling", description: "Frontend audit", model: "<exact model from current modelPolicy route>" }
68
- ],
69
- synthesis: "Merge both audits into one prioritized findings list"
70
- }
71
-
72
- // Background run — model is still required; the live widget shows the actual model
73
- // and changes when a configured fallback is selected.
74
- { task: "Audit dependency licenses", model: "<provider/model-id>", async: true }
75
- // later
76
- { action: "status", id: "abc123" }
77
- { action: "wait", id: "abc123" } // interruptible; does not cancel
78
- { action: "cancel", id: "abc123" }
79
-
80
- // Collecting a background run also has its own tool, for reflexive mid-flow
81
- // use. Identical semantics to action:"wait" (same handler underneath).
82
- // A timeout returns a still-running notice WITHOUT cancelling or consuming
83
- // the run, so it stays collectable.
84
- subagent_wait { id: "abc123" }
85
- subagent_wait { id: "abc123", timeout_ms: 30000 }
86
-
87
- // Dry-run a spawn request: full validation + preflights (git repo, fork
88
- // session, output paths), returns the resolved per-task plan (model, tools,
89
- // budgets, isolation) without spawning anything.
90
- { action: "plan", tasks: [{ task: "Implement feature A", model: "<exact model from current modelPolicy route>", isolation: "worktree" }] }
91
-
92
- // Structured output: the child must end with a fenced json:result block
93
- // matching the schema. Invalid output gets one automatic repair round;
94
- // delivery is the clean JSON and details carry the parsed object.
95
- {
96
- task: "Audit the auth module",
97
- model: "<exact model from current modelPolicy route>",
98
- output_schema: {
99
- type: "object",
100
- required: ["findings", "risk"],
101
- properties: {
102
- findings: { type: "array", items: { type: "string" } },
103
- risk: { type: "string", enum: ["low", "medium", "high"] }
104
- }
105
- }
106
- }
107
-
108
- // Fork the parent conversation into the child (needs a persisted session).
109
- // The child starts from a branched copy of everything discussed so far.
110
- { task: "Implement the plan we agreed on", model: "<exact model from current modelPolicy route>", context: "fork", profile: "general" }
111
-
112
- // Budgets with graceful wrap-up: at the limit the child is steered to produce
113
- // a final answer and given grace turns before any hard stop.
114
- { task: "Audit deps", model: "<exact model from current modelPolicy route>", max_turns: 15, grace_turns: 2 }
115
-
116
- // Automatic retry uses the configured fallback order. If fallback_models is
117
- // supplied, it must exactly match modelPolicy; otherwise omit it.
118
- { task: "Research X", model: "<provider/model-id>", max_retries: 1 }
119
-
120
- // Steer a running child mid-run instead of cancel + retry. The message is
121
- // delivered after the current assistant turn, before the next LLM call.
122
- { action: "steer", id: "abc123", message: "Skip the tests directory; focus on src/" }
123
- // Parallel runs: pass index to pick one live task.
124
- { action: "steer", id: "abc123", index: 1, message: "Wrap up now" }
125
-
126
- // Resume a child session
127
- { task: "Continue from your findings and propose a fix plan", model: "<exact model from current modelPolicy route>", resume: "<session-id>" }
128
-
129
- // Isolated writers
130
- {
131
- tasks: [
132
- { task: "Implement feature A", model: "<exact model from current modelPolicy route>", profile: "general", isolation: "worktree" },
133
- { task: "Implement feature B", model: "<exact model from current modelPolicy route>", profile: "general", isolation: "worktree" }
134
- ]
135
- }
136
-
137
- // Close the worktree loop after the run finishes:
138
- { action: "diff", id: "abc123", index: 0 } // inspect the patch
139
- { action: "apply", id: "abc123", index: 0 } // land as uncommitted changes in your checkout
140
- { action: "discard", id: "abc123", index: 1 } // drop worktree + branch
141
- ```
142
-
143
- The `/subagents` overlay mirrors the worktree loop interactively: `s` steer,
144
- `a` apply, `x` discard on the selected run.
145
-
146
- ## Side questions (`/btw`)
147
-
148
- ```
149
- /btw does this repo have a rate limiter?
150
- /btw # prompts for the question
151
- ```
152
-
153
- `/btw` runs a one-off read-only subagent for _you_, not for the model. It uses
154
- the same policy, budget, semaphore and process-lock machinery as any run, but
155
- delivers its answer as a custom session entry, which does not participate in
156
- LLM context. The main agent keeps working and never sees the question or the
157
- answer — useful for checking something mid-task without derailing the
158
- conversation or polluting the context window.
159
-
160
- ## Backends
161
-
162
- Children can run on a different agent CLI. Everything else — worktrees, process
163
- locks, depth limits, budgets, orphan reclaim — is backend-agnostic and applies
164
- unchanged.
165
-
166
- ```ts
167
- { task: "Summarize this module", model: "<exact model from current modelPolicy route>", backend: "codex", profile: "explore" }
168
- { task: "Review this diff", model: "<exact model from current modelPolicy route>", backend: "claude", max_cost: 0.50 }
169
- ```
170
-
171
- Requires the corresponding CLI on PATH (`codex`, `claude`). Capabilities differ,
172
- and **unsupported combinations are refused with an explanation rather than
173
- silently ignored** — a dropped `max_cost` or unenforced read-only profile would
174
- be a safety regression, not a minor degradation.
175
-
176
- | | `pi` (default) | `codex` | `claude` |
177
- | ------------------------------- | -------------- | ------------------------------------- | ---------------------- |
178
- | `max_cost` | yes | **refused** (reports tokens, no cost) | yes (`total_cost_usd`) |
179
- | read-only profile | tool allowlist | `--sandbox read-only` (OS-level) | `--allowedTools` |
180
- | steering / graceful wrap-up | yes | **no** (no stdin channel) | **no** (one-shot) |
181
- | `resume` | yes | yes | yes |
182
- | `context:'fork'`, `fork_resume` | yes | **refused** | yes |
183
- | `thinking` | yes | no | no |
184
- | `output_schema` | yes | yes | yes |
185
-
186
- A budget breach on a backend without steering hard-stops instead of asking the
187
- child to wrap up. Codex's read-only sandbox is enforced by the OS, which is
188
- stronger than a tool allowlist.
189
-
190
- Set a persona's backend in agent frontmatter with `backend: codex`.
191
-
192
- ## Profiles
193
-
194
- | Profile | Tools | Writes |
195
- | ---------------------------- | ------------------------------------------------------- | ------------------------------------------- |
196
- | `explore` (parallel default) | read/grep/find/ls + safe extras + Pi context tools | no project-file writes |
197
- | `review` | same as explore | no project-file writes |
198
- | `general` | inherited active tools + Pi context tools | yes if tools include bash/edit/write |
199
-
200
- For the Pi backend, the context-management tools `new_context`,
201
- `get_context_remaining`, `history`, and `notes` are retained in child tool
202
- allowlists when the parent exposes them. They are control-plane tools: they may
203
- update continuity notes or the remote context window, but cannot modify the
204
- child checkout or run a shell command. This exception also applies when a task
205
- supplies a narrower tool list, so Pi's `contextManagement` remains usable for
206
- configured gateway models.
207
-
208
- Parallel write-capable tasks sharing one checkout are rejected unless each uses
209
- `isolation: "worktree"`, distinct `cwd`, or explicit `allow_shared_writes: true`.
210
-
211
- ## Configuration
212
-
213
- Defaults can be overridden in `~/.pi/subagent.json` and per-field via env vars
214
- (env wins over file):
215
-
216
- | Setting | Env var | Default |
217
- | ----------------------- | ------------------------------------- | ------------------------------------- |
218
- | `maxTasksPerRun` | `PI_SUBAGENT_MAX_TASKS` | 8 |
219
- | `maxActiveProcesses` | `PI_SUBAGENT_MAX_ACTIVE` | 4 |
220
- | `maxQueuedTasks` | `PI_SUBAGENT_MAX_QUEUED` | 32 |
221
- | `maxGlobalActive` | `PI_SUBAGENT_MAX_GLOBAL_ACTIVE` | 16 |
222
- | `defaultTimeoutMs` | `PI_SUBAGENT_TIMEOUT_MS` | 900000 |
223
- | `maxDepth` | `PI_SUBAGENT_MAX_DEPTH` | 2 |
224
- | `killGraceMs` | `PI_SUBAGENT_KILL_GRACE_MS` | 3000 |
225
- | `sessionDir` | `PI_SUBAGENT_SESSION_DIR` | `~/.pi/subagent-sessions` |
226
- | `worktreeDir` | `PI_SUBAGENT_WORKTREE_DIR` | `~/.pi/subagent-worktrees` |
227
- | `lockDir` | `PI_SUBAGENT_LOCK_DIR` | `~/.pi/subagent-locks` |
228
- | `worktreeRetentionDays` | `PI_SUBAGENT_WORKTREE_RETENTION_DAYS` | unused (lifecycle GC) |
229
- | `sessionRetentionDays` | `PI_SUBAGENT_SESSION_RETENTION_DAYS` | unused (lifecycle GC) |
230
- | `lockRetentionDays` | `PI_SUBAGENT_LOCK_RETENTION_DAYS` | 7 |
231
- | `taskDefaults` | — | none |
232
- | `graceTurns` | `PI_SUBAGENT_GRACE_TURNS` | 2 |
233
- | `stallAfterMs` | `PI_SUBAGENT_STALL_AFTER_MS` | 90000 |
234
- | `stallKillAfterMs` | `PI_SUBAGENT_STALL_KILL_AFTER_MS` | 90000 |
235
- | `maxRetries` | `PI_SUBAGENT_MAX_RETRIES` | 1 |
236
- | `widget` | `PI_SUBAGENT_WIDGET` | `background` (`off` disables) |
237
- | `notifications` | `PI_SUBAGENT_NOTIFICATIONS` | `batched` (`off` disables) |
238
- | (bin) | `PI_SUBAGENT_BIN` | auto (`process.execPath` + CLI entry) |
239
-
240
- ### Model policy
241
-
242
- New spawns are routed only by the user-owned `modelPolicy` in `~/.pi/subagent.json`.
243
- Agent frontmatter, `taskDefaults`, parent-session model inheritance, and ad-hoc
244
- fallback lists are ignored for model selection. The minimal template is:
245
-
246
- ```json
247
- {
248
- "modelPolicy": {
249
- "default": {
250
- "model": "<provider/model-id>",
251
- "fallbackModels": [],
252
- "thinking": "medium"
253
- },
254
- "agents": {
255
- "<agent-name>": {
256
- "model": "<provider/model-id>",
257
- "fallbackModels": [],
258
- "thinking": "high"
259
- }
260
- }
261
- }
262
- }
263
- ```
264
-
265
- `thinking` is optional and is an opaque Pi thinking-level string. Common values
266
- include `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`, but the
267
- package does not remap or restrict model-specific values. Pi receives the value
268
- unchanged and decides whether the active model supports it. It is a route default,
269
- not a strict policy value. Resolution order is: explicit task `thinking` > agent
270
- frontmatter `thinking` > profile `taskDefaults.<profile>.thinking` > the selected
271
- `modelPolicy` route's `thinking` > the parent session's thinking level. Omitting it
272
- preserves the existing behavior.
273
-
274
- Every new `task`/`tasks[]` item must pass the exact mapped `model`. Omit
275
- `fallback_models` to use the configured list; when supplied it must match the
276
- configured order exactly. Management actions remain available when this file is
277
- missing, but new spawns and synthesis are rejected until it is valid. The
278
- extension re-reads the policy on each dispatch and injects the current mapping
279
- into the parent prompt. No provider catalog or credentials are read.
280
-
281
- This guarantee applies to the Extension tool dispatch and its internal synthesis
282
- handoff. The stable SDK exports (`runTasks` and `runSubagent`) are trusted
283
- low-level library APIs and intentionally do not load the Extension's
284
- `modelPolicy`; library callers must parse and validate their own policy before
285
- passing `TaskSpec` values. Do not treat direct SDK calls as policy enforcement.
286
-
287
- ### Named agent files
288
-
289
- Define reusable subagent personas as markdown files, discovered from the same
290
- conventional roots skills use (higher root wins name conflicts):
291
-
292
- | Priority | Location | Scope |
293
- | -------- | ----------------------------------------------------------------------- | --------------------------- |
294
- | 1 | `.pi/agents/<name>.md` | project (authoritative) |
295
- | 2 | `.agents/agents/<name>.md` | shared cross-tool workspace |
296
- | 3 | `$PI_CODING_AGENT_DIR/agents/<name>.md` (default `~/.pi/agent/agents/`) | global |
297
-
298
- The markdown body becomes the child's appended system prompt; frontmatter
299
- supplies defaults using the same snake_case names as the tool parameters:
300
-
301
- ```md
302
- ---
303
- description: Security-focused code reviewer
304
- # Legacy model/fallback fields are ignored; use modelPolicy instead.
305
- thinking: high
306
- profile: review
307
- max_turns: 20
308
- spawns: false # or "*", "scout", "[reviewer, scout]"
309
- ---
310
-
311
- You are a security auditor. Review code for injection flaws, auth issues,
312
- and sensitive data exposure. Report findings with file:line evidence and
313
- severity ratings.
314
-
315
- @include shared/review-checklist.md
316
- ```
317
-
318
- Agent files may also pin a structured contract with
319
- `output_schema: {"type": "object", …}` (single-line inline JSON) or
320
- `output_schema: @contract.json` (path relative to the agent file).
321
-
322
- `spawns:` controls which agents a child of this persona may spawn:
323
- `false` disables further nesting (no tool registered in that child),
324
- `"*"` (or omit) is unrestricted, and a comma/bracket list is an allowlist
325
- (agentless tasks are rejected under an allowlist). The policy is passed to
326
- the child via `PI_SUBAGENT_SPAWNS` and enforced on each subsequent spawn.
327
-
328
- Body lines that consist solely of `@include relative/path.md` expand that
329
- file one level deep (relative to the agent file, same 64KB/symlink guards
330
- as `@contract.json`). Missing or rejected includes leave the line verbatim;
331
- includes do not recurse.
332
-
333
- Invoke with `{ task: "…", agent: "reviewer", model: "<provider/model-id>" }`.
334
- The agent file supplies persona/capability defaults only; its legacy
335
- `model`/`fallback_models` fields are ignored. An explicit `system_prompt`
336
- appends after the persona body.
337
- Profiles still enforce capability: an agent declaring `profile: review` with
338
- write tools fails closed. The agent catalog is advertised in the tool's
339
- system-prompt guidelines (session start) and in bare `status` output (live),
340
- and file changes are picked up within seconds — no restart needed.
341
-
342
- ### Per-profile task defaults
343
-
344
- `taskDefaults` in `~/.pi/subagent.json` remains available for non-model
345
- fields such as thinking, budgets, and retry counts. Its legacy `model` and
346
- `fallbackModels` fields are ignored; model routing belongs only to
347
- `modelPolicy`. A profile `thinking` value overrides the selected route default.
348
- Invalid fields are dropped field-by-field.
349
-
350
- Notes on behavior:
351
-
352
- - `timeout_ms` covers queue time plus runtime, but timed-out tasks report
353
- `state: "timeout"` with `timeoutPhase: "queued"|"starting"|"running"` so
354
- agents can retry capacity issues without confusing them for task failures.
355
- - Budget stops (`max_turns`, `max_cost`) trigger a **graceful wrap-up**: the
356
- child is steered to produce its final answer NOW and allowed `graceTurns`
357
- more turns before SIGTERM. Results end as `partial` with `wrappedUp: true`
358
- when the child concluded in time. `graceTurns: 0` restores immediate stops.
359
- - A **stall watchdog** flags children with no protocol activity for
360
- `stallAfterMs` (a liveness probe distinguishes quiet-but-thinking from dead),
361
- then kills after `stallKillAfterMs` more silence — feeding automatic retry
362
- instead of burning the whole timeout.
363
- - **Transient failures retry automatically** (queue timeouts, stalls, spawn
364
- errors, provider errors) up to `maxRetries` extra attempts, escalating
365
- through `fallback_models` when provided. Usage accumulates across attempts;
366
- results record `attempts` and `attemptedModels`. Task-quality failures
367
- (nonzero exit with complete protocol, cancellations, budget stops, running
368
- timeouts) never retry.
369
- - `context: "fork"` starts a single child from a real branched copy of the
370
- parent conversation (`--fork` on the parent's session file). It requires a
371
- persisted parent session, cannot combine with `resume`, and is rejected for
372
- parallel fanout (context duplication × N is a cost bug, not a feature).
373
- - **Structured output** (`output_schema`): the contract is appended to the
374
- child's system prompt; the final message must end with a fenced
375
- `json:result` block. Validation runs parent-side against a dependency-free
376
- JSON-Schema subset (type/properties/required/items/enum/const — unknown
377
- keywords are ignored, never rejected). Invalid output triggers **one
378
- steer-based repair round**; still-invalid results end `partial` with
379
- `structuredError` set and the raw text delivered — paid work is never
380
- discarded. Validated parallel results feed the `synthesis` child as clean
381
- JSON instead of prose.
382
- - **Arg repair**: double-encoded task text (literal `\n` / `\"` escapes from
383
- LLM re-encoding) is conservatively de-mangled once at validation time.
384
- Identifier fields and paths are never touched.
385
- Protocol streams truncated after useful assistant output also end as `partial`.
386
- - Aborting a `wait` returns immediately without cancelling the background run.
387
- - Child processes are launched via the same Node runtime + CLI entry as the
388
- parent when possible (`PI_SUBAGENT_BIN` overrides). Bare `pi` on PATH is only
389
- a logged last resort.
390
- - Direct resume is exclusive **across processes** via durable locks under
391
- `lockDir`. Lost runs block resume until startup orphan reconciliation kills
392
- (or confirms dead) the recorded child process group.
393
- - `maxGlobalActive` bounds concurrent children across every Pi parent process
394
- on the machine (in addition to the per-session semaphore).
395
- - Nested children at the depth ceiling do not re-register the subagent tool;
396
- only top-level parents run maintenance/orphan reclaim/worktree GC.
397
- - Preserved worktrees live under `worktreeDir` (durable, not `/tmp`) and are
398
- garbage-collected on startup by **lifecycle**, not wall-clock retention: once
399
- a run is over (not live, past a 1h concurrency race guard), the worktree's
400
- unique work is archived as one applyable patch under
401
- `<repo-container>/_patches/` and the directory is reclaimed immediately.
402
- Branches holding commits that exist on no other ref are never deleted.
403
- `diff`/`apply`/`discard` transparently fall back to the archived patch when
404
- the directory is already gone. Live runs are never swept: the current
405
- session's live worktrees plus any worktree recorded on a running run record
406
- (concurrent Pi processes) are shielded machine-wide.
407
- - Startup GC sweeps **every** repo container under `worktreeDir`, not just the
408
- current checkout's, so repos you stop visiting are still reclaimed. A
409
- container whose base repo no longer exists is kept and reported, never
410
- deleted — its worktrees' object stores lived inside the deleted repo, so
411
- unique work cannot be distinguished from a pristine checkout, let alone
412
- archived. Empty containers (no worktrees, no archived patches) are removed.
413
- - Child session transcripts are likewise distilled on lifecycle: when a run is
414
- over and nothing on the parent branch references its session, the transcript
415
- is reduced to a small `.digest.json` (task, final output, model, usage,
416
- turn/tool/error counts) and the raw `.jsonl` is deleted. Resume needs the
417
- transcript, so anything referenced or busy machine-wide is kept.
418
- - `keep_background: true` on a task keeps processes the child intentionally
419
- backgrounded (e.g. dev servers) alive after a clean exit.
420
- - `include_wip: true` (with `isolation: "worktree"`) seeds the worktree with the
421
- parent checkout's uncommitted changes so the child sees your dirty baseline.
422
- `diff`/`apply` subtract that baseline when clean, else report the combined
423
- delta with an explicit `[includes parent WIP]` warning.
424
-
425
- ## Using the runner as a library
426
-
427
- Import the stable public SDK from the package root or the explicit `/sdk`
428
- subpath — do not reach into `src/*` internals (those paths are not part of the
429
- supported contract):
430
-
431
- ```ts
432
- import {
433
- runTasks,
434
- runSubagent,
435
- ChildRunner,
436
- WorktreeManager,
437
- Semaphore,
438
- ProcessLockManager,
439
- addUsage,
440
- normalizeUsage,
441
- emptyUsage,
442
- type TaskSpec,
443
- type TaskResult,
444
- type RunState,
445
- type UsageStats,
446
- } from "@parke.dev/pi-subagent/sdk";
447
- ```
448
-
449
- The package root is an alias for the same SDK:
450
- `import { runTasks } from "@cr1ms0n/pi-subagent"`.
451
-
452
- The Extension dispatch path requires `model` to be the exact value from the
453
- current `modelPolicy` route. This placeholder is illustrative only and does not
454
- write or select a real configuration:
455
-
456
- ```ts
457
- const routeModel = "<exact model from current modelPolicy route>";
458
- const task: TaskSpec = {
459
- task: "Audit src/ for unsafe parsing",
460
- profile: "explore",
461
- model: routeModel,
462
- timeoutMs: 10 * 60_000,
463
- };
464
- ```
465
-
466
- Prefer `runTasks()` for multi-task / worktree orchestration (same path the
467
- extension and pi-workflows use). `runSubagent()` runs a single child process
468
- directly without the extension host, but durable coordination is **opt-in**.
469
- Pass both `locks` (a `ProcessLockManager`) and a stable `runId` if you want
470
- global concurrency slots and orphan reclaim to see the child. Without those
471
- options no durable run record is written, so a parent restart cannot reclassify
472
- the process and nested children vanish from reconcile. There is intentionally
473
- no implicit default lock manager — embedding code that needs durability must
474
- construct and share one.
475
-
476
- The Pi extension entry is unchanged: package `pi.extensions` still points at
477
- `./extensions/subagent.ts`.
478
-
479
- ## Design invariants
480
-
481
- 1. A run belongs to one parent session and cannot update another session.
482
- 2. Per-session + machine-wide process caps and nesting depth limits prevent process storms.
483
- 3. Cancellation prevents queued tasks from spawning.
484
- 4. Direct resume of a child session is exclusive **across processes** via durable locks.
485
- 5. Tool responses are capped to ~50KB/2000 lines; full output lives in
486
- artifacts and `~/.pi/subagent-sessions`.
487
- 6. Status is compact; wait is the one-shot deliverable.
488
- 7. On parent session shutdown, live children are aborted and awaited briefly.
489
- 8. On parent (re)start, orphan process groups recorded under `lockDir` are reaped
490
- before any resume is allowed for the matching child session.
491
- 9. Provider-reported usage is counted once per root message and terminal child run.
492
- 10. Protocol completion prefers `agent_settled` (falls back to non-retrying `agent_end`).
493
-
494
- ## Layout
495
-
496
- ```
497
- src/
498
- index.ts # stable public SDK entry (@parke.dev/pi-subagent)
499
- extension.ts # Pi wiring only
500
- schema.ts # request schemas (subagent + subagent_wait)
501
- btw.ts # /btw side questions (model-hidden entries)
502
- backend.ts # backend adapter seam + capability gate
503
- backends/ # pi | codex | claude adapters (invocation + parser)
504
- policy.ts # profiles, normalization, write guards, agent resolution
505
- agents.ts # named agent files (.pi/agents/, .agents/agents/, global)
506
- launch.ts # resolve child pi via execPath / PI_SUBAGENT_BIN
507
- process-lock.ts # durable session locks, global slots, orphan records
508
- worktree.ts # git worktree isolation + diff/apply/discard
509
- orchestrator.ts # multi-task execution, transient retry + model fallback
510
- runner.ts # child process lifecycle, RPC channel, steering,
511
- # graceful budget wrap-up, stall watchdog
512
- protocol.ts # Pi RPC/JSON event parser (agent_settled-aware)
513
- semaphore.ts # per-session concurrency limit
514
- registry.ts # session-scoped run state + durable resume locks
515
- persistence.ts # parent-session event folding
516
- usage.ts # root/subagent/combined usage ledger
517
- output.ts # exact global output caps
518
- notifications.ts # batched background-run completion notifications
519
- format.ts / ui.ts# renderers, ambient widget, /subagents overlay
520
- ```
521
-
522
- ## Develop
523
-
524
- ```bash
525
- npm install
526
- npm run typecheck
527
- npm test
528
- npm run pack:check
529
- ```
530
-
531
- Tests use a deterministic `fake-pi` child. No live model calls are required.
532
-
533
- ## Cost accounting
534
-
535
- `status`, `/subagent-cost`, and the `/subagents` overlay header show separate
536
- **root**, **subagent**, and **combined** totals based on provider-reported
537
- usage. On Pi builds after v0.80.10, delivered runs also report their total
538
- usage natively on the tool result
539
- ([pi#6671](https://github.com/earendil-works/pi/pull/6671)), so Pi's own
540
- footer, `/session`, and RPC totals include subagent spend — exactly once per
541
- run; older Pi hosts ignore the field. Nested usage reported by a child's tool
542
- results (e.g. grandchild subagents) folds into the run's totals and budgets.
543
- The extension footer stays terse (running/ready counts only). Delivery and
544
- replay do not double count runs. See
545
- [docs/COST-ACCOUNTING.md](./docs/COST-ACCOUNTING.md).
546
-
547
- ## Roadmap
548
-
549
- Planned work — agent spawn policies, dry-run validation, engine hardening,
550
- live transcripts — lives in [docs/ROADMAP.md](./docs/ROADMAP.md) (rationale
551
- and design sketches) and [docs/PLAN.md](./docs/PLAN.md) (execution contract:
552
- work breakdown, acceptance criteria, test plans, and release gates per phase).
553
-
554
- ## Security
555
-
556
- See [docs/SECURITY.md](./docs/SECURITY.md). Pi packages run with full system
557
- access—review source before installing third-party packages.
558
-
559
- ## Status
560
-
561
- v0.1 focuses on the correct lifecycle engine:
562
-
563
- - process + session ownership
564
- - budgets, caps, profiles
565
- - persistence + inspector
566
- - worktree isolation helpers
567
-
568
- Named agent catalogs and automatic chain workflows are intentionally deferred
569
- until the core is battle-tested.
1
+ [English](README.md) | [简体中文](README.zh-CN.md)
2
+
3
+ # Pi Smart Subagents
4
+
5
+ Run isolated child agents in [Pi](https://pi.dev/), with Jev selecting an execution model and individual tools for each task.
6
+
7
+ Published on npm as `@cr1ms0n/pi-subagent`. This is an independent community fork of Luke Parke's `@parke.dev/pi-subagent` 0.8.0 from [LukasParke/pi-extensions](https://github.com/LukasParke/pi-extensions/tree/main/packages/pi-subagent), not an official upstream release. The original MIT license and copyright are preserved.
8
+
9
+ The upstream extension provides the child-process engine, named agents, background tasks, worktrees and usage accounting. This fork adds mandatory Jev model/tool selection and verifies the child's selected capabilities before sending it the task.
10
+
11
+ ---
12
+
13
+ <a id="quick-start"></a>
14
+ ### Install and quick start
15
+
16
+ Use Node.js 22.19.0 or newer and an installed Pi CLI with a working model provider. Pi 0.86.0 is the verified host baseline for enforcing built-in, extension and late-registered tool allowlists. A host that cannot verify the selected capabilities is refused rather than granted more tools.
17
+
18
+ **1. Install the published package.**
19
+
20
+ Install the exact `0.10.0` release. The earlier `0.9.0` release uses the older `apiKeyEnv` configuration contract and does not accept `apiKey`:
21
+
22
+ ```bash
23
+ pi install npm:@cr1ms0n/pi-subagent@0.10.0
24
+ ```
25
+
26
+ Pi loads the package directly. Do not enable another copy of this extension or `@parke.dev/pi-subagent` together with it: they register the same tools. The package provides `subagent`, `subagent_wait`, `/subagents`, `/subagent-cost` and `/btw`.
27
+
28
+ **2. Store your TypeSafe credential in private configuration.**
29
+
30
+ Set `jevRouting.apiKey` in your user-level `~/.pi/subagent.json`, as shown below. If you are upgrading from `0.9.0`, move the existing value from `jevRouting.apiKeyEnv` to `jevRouting.apiKey` and remove the old field before starting new dispatches. Do not paste the key into chat or repository files. The file stores the key in plaintext: restrict file access and protect backups. See [credential setup](docs/REFERENCE.md#credential-setup) for migration and security details. Provider authentication for the child models is configured separately in Pi.
31
+
32
+ **3. Configure your candidate models.**
33
+
34
+ Add this block to `~/.pi/subagent.json`, preserving unrelated settings. Replace the example model ID with an exact `provider/model-id` available in your Pi installation and write your own model characteristics. Remove any legacy `modelPolicy` block; it is not migrated automatically.
35
+
36
+ ```json
37
+ {
38
+ "jevRouting": {
39
+ "selectorModel": "jev-latest",
40
+ "apiKey": "<your-typesafe-api-key>",
41
+ "timeoutMs": 15000,
42
+ "models": [
43
+ {
44
+ "model": "<provider/model-id>",
45
+ "description": "Describe this model's strengths and the tasks you want it to handle."
46
+ }
47
+ ]
48
+ }
49
+ }
50
+ ```
51
+
52
+ Replace the `apiKey` placeholder with your TypeSafe key and remove any old `apiKeyEnv` field. There is no environment fallback or automatic migration. Candidate descriptions may be written in Chinese. See the [configuration reference](docs/REFERENCE.md#configuration) for optional thinking defaults, profile defaults and limits.
53
+
54
+ Jev selection can incur TypeSafe charges. It receives the delegated task text, model IDs/descriptions, candidate tool names/descriptions and required constraints. It does not automatically upload repository files or conversation history; text you include in the task can still disclose sensitive information. `action: "plan"` also calls Jev, and a later execution selects again.
55
+
56
+ **4. Start Pi and delegate a read-only task.**
57
+
58
+ Start Pi, or reload/restart it after switching extension code. Once `0.10.0` is loaded, each new dispatch re-reads the configuration; changing `apiKey` does not require a shell environment update.
59
+
60
+ ```bash
61
+ pi
62
+ ```
63
+
64
+ Ask the parent agent to use `subagent` with a request such as:
65
+
66
+ ```json
67
+ {
68
+ "task": "Read README.md and summarize what this package does.",
69
+ "description": "Summarize the README",
70
+ "profile": "explore",
71
+ "tools": ["read"],
72
+ "max_turns": 4,
73
+ "timeout_ms": 120000,
74
+ "max_retries": 0
75
+ }
76
+ ```
77
+
78
+ Omit `model` and `fallback_models`. Jev chooses from your configured model list and permitted tools; a routing failure stops the new dispatch without a fallback. Existing-run management remains available without a routing credential.
79
+
80
+ ---
81
+
82
+ <a id="delegation"></a>
83
+ ### Delegation
84
+
85
+ - **Model and tool routing:** this fork asks Jev to match each task to your model descriptions and select tools individually. Local permission checks and child startup verification enforce the result.
86
+ - **Named agents and parallel work:** the upstream engine supports reusable personas and concurrent child processes. This fork routes each new child through Jev; agent files do not pin its model or tool selection.
87
+ - **Background tasks:** the upstream engine supports status, interruptible waiting, cancellation and steering. The fork's display includes the selected model, with tool details in expanded results.
88
+ - **Isolated edits:** the upstream worktree flow lets you inspect, apply or discard changes without sharing one writable checkout between parallel agents.
89
+ - **Structured results and budgets:** the upstream engine validates structured output parent-side and preserves partial work. This fork keeps retries on the selected model/tool set and accounts for selector tokens separately.
90
+
91
+ For a background task, set `async: true`, then collect it using `subagent_wait` or `action: "wait"`. Aborting or timing out a wait does not cancel the child. Use `action: "cancel"` to stop it. Open `/subagents` to inspect runs and `/subagent-cost` to see usage.
92
+
93
+ The [reference](docs/REFERENCE.md#quick-usage) includes parallel work, synthesis, resume/fork, structured output, budgets and the worktree diff/apply/discard loop. The [TUI guide](docs/UX.md) describes the inspector and keyboard controls.
94
+
95
+ ---
96
+
97
+ <a id="permissions-and-costs"></a>
98
+ ### Permissions and costs
99
+
100
+ | Profile | Tool selection | Project-file writes |
101
+ | --- | --- | --- |
102
+ | `explore` | Jev-selected locally permitted read-only tools plus available Pi context controls | No |
103
+ | `review` | Same read-only policy | No |
104
+ | `general` | Jev-selected locally permitted tools plus available Pi context controls | Possible with selected write-capable tools |
105
+
106
+ Single tasks default to `general`; parallel tasks default to `explore`. An explicit `tools` list is a ceiling. Available Pi context-management controls are added locally even with `tools: []`. An empty tool selection never means all tools.
107
+
108
+ Profiles are tool-selection policy, not an OS sandbox. Children inherit the parent environment and can read files accessible to the same user, including the private config. Worktrees isolate the checkout only. Review the [security model](docs/SECURITY.md) before delegating untrusted work.
109
+
110
+ The ledger separates root, subagent, routing and combined usage. TypeSafe reports routing tokens, not currency, so selector cost is **unreported**, not free. `max_cost` limits provider-reported child execution cost; it does not cap TypeSafe fees. See [cost accounting](docs/COST-ACCOUNTING.md) for delivery, retry and branch semantics.
111
+
112
+ New extension-managed dispatch supports the Pi backend only. Native Codex/Claude requests are rejected. The [low-level SDK](docs/REFERENCE.md#using-the-runner-as-a-library) is a separate explicit-spec API: it does not automatically call Jev, and embedding code owns its model/tool choices.
113
+
114
+ ---
115
+
116
+ <a id="development"></a>
117
+ ### Development
118
+
119
+ The source is a standalone TypeScript package with peer dependencies, no build step and no bundled test runner or typecheck script. Follow [development and verification](docs/DEVELOPMENT.md) for the checks this checkout supports. A syntax transform is not a semantic typecheck, and `npm pack --dry-run --ignore-scripts --json` verifies package contents without publishing.
120
+
121
+ The [architecture contract](docs/ARCHITECTURE.md) documents ownership and invariants. [Release maintenance](docs/RELEASING.md) covers selective source updates and the separate, explicitly authorized npm publication process.
122
+
123
+ ---
124
+
125
+ <a id="license"></a>
126
+ ### License
127
+
128
+ [MIT](LICENSE). Copyright (c) 2026 Luke Parke. Fork maintained by cr1ms0n (awoaCrim). Preserve the original copyright and license when redistributing this work.
129
+
130
+ Thanks to [Linux.do](https://linux.do/).