@henryqw/pi-subagent 18.0.0 → 22.0.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.
Files changed (47) hide show
  1. package/CONTEXT.md +76 -24
  2. package/README.md +116 -142
  3. package/dist/composition.d.ts +11 -0
  4. package/dist/composition.js +107 -0
  5. package/dist/direct-herdr.d.ts +17 -0
  6. package/dist/direct-herdr.js +219 -0
  7. package/dist/ephemeral.d.ts +2 -2
  8. package/dist/ephemeral.js +7 -7
  9. package/dist/git-runtime.d.ts +110 -0
  10. package/dist/git-runtime.js +472 -0
  11. package/dist/herdr-runtime.d.ts +137 -0
  12. package/dist/herdr-runtime.js +1228 -0
  13. package/dist/index.d.ts +11 -5
  14. package/dist/index.js +20 -16
  15. package/dist/integration-git.d.ts +59 -0
  16. package/dist/integration-git.js +484 -0
  17. package/dist/launch-runtime.d.ts +54 -0
  18. package/dist/launch-runtime.js +195 -0
  19. package/dist/process.d.ts +9 -0
  20. package/dist/process.js +11 -0
  21. package/dist/runner.d.ts +356 -0
  22. package/dist/runner.js +2710 -0
  23. package/dist/schema.d.ts +2234 -0
  24. package/dist/schema.js +1236 -0
  25. package/dist/store.d.ts +50 -0
  26. package/dist/store.js +288 -0
  27. package/dist/working-evidence.d.ts +25 -0
  28. package/dist/working-evidence.js +261 -0
  29. package/docs/adr/001-composable-ephemeral-execution.md +3 -3
  30. package/docs/delegation-routing.html +89 -0
  31. package/docs/delegation-routing.svg +78 -0
  32. package/docs/orchestration-architecture.html +238 -0
  33. package/docs/orchestration-architecture.svg +189 -0
  34. package/docs/orchestration.md +122 -331
  35. package/example.png +0 -0
  36. package/examples/roles/implementer.md +2 -3
  37. package/extensions/admission.ts +66 -0
  38. package/extensions/config.ts +51 -37
  39. package/extensions/isolated.ts +638 -0
  40. package/extensions/result-transport.ts +13 -103
  41. package/extensions/role-tools.ts +11 -11
  42. package/extensions/subagent-command.ts +167 -0
  43. package/extensions/subagent.ts +264 -540
  44. package/extensions/task-name.ts +1 -2
  45. package/extensions/workflow.ts +60 -96
  46. package/package.json +19 -11
  47. package/skills/pi-subagent/SKILL.md +35 -0
package/CONTEXT.md CHANGED
@@ -2,33 +2,85 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- Provide validated built-in and user Roles, shared task-model Pi launch policy, generic `delegate_task` delegation, and public executor, worktree, and exact-review-evidence APIs. Main or a consuming package owns any checked implementation protocol.
5
+ Own Role-configured Pi delegation through one `delegate_task` surface:
6
+
7
+ - **direct mode** runs read-only compact work in Herdr tabs in Main's workspace and returns a handle before completion;
8
+ - **isolated mode** runs durable checked graphs with Herdr worktrees, exact Git identities, guarded integration, and recovery; and
9
+ - the package root exposes the Role, executor, worktree, evidence, schema, state, and runtime mechanisms used by the extension.
6
10
 
7
11
  ## Domain glossary
8
12
 
9
- - **Main**: Pi session that plans and orchestrates delegated work.
10
- - **Subagent**: isolated Pi child process handling one task.
11
- - **Role**: package-shipped built-in or user-owned Markdown definition of a reusable responsibility, with a name, description, system instructions, required `tools`, `extensions`, and `skills` arrays, and optional `modelClass` default and isolation.
12
- - **Model Class**: `fast`, `balanced`, `frontier`, or `fav`, resolved through shared task-model settings.
13
- - **Route**: configured model and exact thinking-level pair selected from a shared Model Class profile; the primary route precedes its optional fallback. A direct model can replace only its model.
14
- - **Delegated Task**: one bounded work request sent from Main to one Role.
15
- - **Workflow**: generic orchestration of one or more Delegated Tasks; `delegate_task` owns its selected mode, while library callers compose executor runs in JavaScript.
16
- - **Workflow Mode**: `delegate_task` tool policy selected per call for `single`, `parallel`, or `chain` execution; not a Role property or executor API.
17
- - **Exact Review Evidence**: caller-requested private base-to-tip patch plus exact `{base, tip, patchPath}` identity prepared by `prepareExactReviewEvidence`.
18
- - **Resource Policy**: Role ownership of base tools, extensions, Skill names, and an exact MCP server allowlist, plus explicit caller additions of tools, extensions, and environment through `createRoleLaunch`.
19
- - **Pi Launch**: reusable `{env,args}` policy for one Role, resolved model route, explicit caller resources, and project trust.
20
- - **Ephemeral Executor**: mechanism that receives a prepared Pi Launch, runs one bounded Delegated Task in one no-session child process, and returns its result without discovering resources or composing a Workflow.
13
+ - **Main**: the Pi session and checkout coordinating delegated work.
14
+ - **Role**: package or user Markdown defining responsibility, tools, trusted extension sources, Skills, optional MCP names, instructions, and an optional model-class default. Roles do not select isolation.
15
+ - **Model Class**: `fast`, `balanced`, `frontier`, or `fav`, resolved through `pi-task-models`.
16
+ - **Direct task**: one read-only assignment in a Herdr tab in Main's current workspace.
17
+ - **Isolated request**: one durable ID, goal, and checked task graph.
18
+ - **Candidate**: exact clean committed task-worktree identity produced by an isolated changeset worker.
19
+ - **Readiness**: durable proof that one exact live-worker candidate passed its preliminary checks and is sealed for Main's explicit selection; it is not a separate user approval.
20
+ - **Integration generation**: Main's owned, ordered assembly of selected candidates in a separate checkout; each replaced assembly remains an inspectable superseded generation.
21
+ - **Release**: Main's explicit, exact-identity cleanup of a rejected worker or superseded generation after proving it is no longer selected; it never discards dirty or conflicted work.
22
+ - **Productive lease**: repository-scoped process lease that excludes concurrent execute/resume while permitting status and abort.
23
+ - **Safety budget**: finite timeout for child work, process I/O, status, termination, or cleanup. It is not a whole-run deadline.
21
24
 
22
25
  ## Invariants
23
26
 
24
- - One Delegated Task creates one ephemeral child process and no saved session. Execution ends at the first hard budget: attempted turn 51 by default (`maxTurns` is a safe integer >= 1; default 50), attempted continuation after the optional token handoff, or `deadline = min(last recognized Pi JSON event + idle timeout, child start + maximum runtime)` (recognized Pi events renew; raw bytes do not; max always terminates). A terminal turn 50 succeeds; attempted continuation rejects with `turn_limit`, accumulated usage, and bounded output. Optional `maxTokens` is a safe integer >= 1 with an unlimited default. It is one global executor default applied independently to every `delegate_task` child; it is neither a shared pool nor a per-call or environment option. Token accounting sums each completed assistant response's `Usage.totalTokens` once, matching the executor's aggregate `Usage`. A terminal response crossing the limit succeeds. A continuing crossing turn and its tools finish, then exactly one extra response turn is permitted. Further continuation rejects with typed `token_limit`, aggregate usage, and bounded last assistant output. This permits a crossing turn plus the final response to overshoot; it is not an exact hard cap. Raw executor launches enforce the extra-turn window but do not guarantee tools are disabled.
25
- - Every Role launch installs the shared tool policy. On a continuing token crossing, or the continuing penultimate `maxTurns` turn, the policy waits for `turn_end`, disables every tool, and steers one structured final report. This covers `delegate_task` and library Role launches made with the public launch API. With `maxTurns` set to 1, tools are disabled during `session_start`, and the sole provider turn is the response-only handoff. The fixed decision packet is the default, but exact task or Role output takes precedence and is returned alone. Terminal boundary responses get no handoff. Before the final boundary, the policy steers the fixed convergence warning once at each 80% threshold for completed turns, aggregate tokens when configured, and maximum runtime. It combines thresholds first due together and starts no timer or extra warning turn. Timeout, provider, or child-process failures can prevent a handoff. After direct Pi exits, inherited stdout/stderr drain until EOF unless an escaped descendant holds them past short inactivity or a one-second hard deadline. Configurable in `~/.pi/agent/config/pi-subagent/config.json` (`maxTurns` defaults to 50; `maxTokens` defaults to unlimited; `timeout.idleMinutes`/`maxMinutes` default to 10/30).
26
- - Up to five active ephemeral `delegate_task` children run per Main by default, configurable via `maxSubagents` in `~/.pi/agent/config/pi-subagent/config.json` or the `PI_SUBAGENT_MAX_SUBAGENTS` environment variable; excess calls wait FIFO. Queued calls do not start a child or consume child timeout.
27
- - Ambient child extensions and Skills stay disabled. Every Role requires `tools`, `extensions`, and `skills` YAML arrays, and every launch installs the Role tool policy. Optional `mcps` defaults to an empty deny-all list. Non-empty `mcps` loads `pi-mcp-adapter` with an isolated in-memory config containing only those exact configured servers; every tool that those servers expose is active, unknown names fail before the first model turn, and direct adapter loading is rejected. `tools: []` activates no base built-ins but does activate all tools from explicitly selected trusted extension bundles and explicit caller tool additions; `skills: []` selects no separately named Role Skills but trusted selected extension Skills still load; `extensions: []` selects no Role extension bundle. A Role/caller explicitly selected extension is a trusted atomic capability bundle: all tools it registers and all Skills supplied through its Pi package metadata or dynamic `resources_discover` load alongside separately named Role Skills. This intentionally includes the extension's executable lifecycle/prompt behavior; pi-subagent does not infer or externally narrow undocumented dependencies, and loading an extension is not sandboxing. Scope children by selecting fewer trusted extensions; finer granularity requires separate entry points/configuration or an upstream split. Explicit Role/caller tool names still verify against the final filtered registry, while parent-only recursive orchestration tools remain excluded.
28
- - Role Skill names resolve through Main's effective Pi Skill registry. `prepareRoleLaunch` and `resolveConfiguredRoleLaunch` reject a nonempty `missingSkills` list before returning a prepared launch, so `delegate_task` does not start an under-capable child. Callers that bypass preparation must reject missing Skills before launch. Explicit Role/caller tool names verify against the final filtered child registry after explicit provider `session_start` handlers, and unavailable names fail before the first turn.
29
- - Route precedence is explicit call-level `modelClass` > Role `modelClass` > configured Model Task assignment or declared default. pi-subagent's local `pi-subagent/delegateTask` declaration defaults to `fast`. A direct model replaces only the selected route model and must honor its exact thinking level. Library callers select a Role plus their own Model Task declaration.
30
- - The selected profile resolves primary then fallback only before launch when a route, model, or thinking level is unavailable. A direct model never changes the route level and fails before launch if it cannot honor it. A missing local JSON config uses defaults quietly. Missing shared task-model config warns once per session because delegation needs a route. If neither route is usable, launch rejects with `Run /task-models`; a started child is never retried by this package.
31
- - User Role Markdown files and Subagent JSON config (`config/pi-subagent/config.json`) live only in the user `config/pi-subagent` directory; model routes live in shared `config/pi-task-models/config.json`. Package-shipped built-in Roles (`implementer`, `reviewer`, `scout`) resolve from the package's own `examples/roles/` Markdown through the same parser and intentionally leave `modelClass` unset; same-named user files override built-ins for `delegate_task` and public API callers. The `synthesizer` Markdown remains the only optional inert sample.
32
- - Low-level worktree APIs allocate optional isolated child worktrees and return explicit `pruned`, `retained`, or `recovery` lifecycle payloads. They never force-delete uncertain work or choose integration policy for callers.
33
- - `prepareExactReviewEvidence` validates Git identity and produces a bounded private patch with exact base and tip OIDs. The caller owns review criteria, verdict validation, integration, cleanup, and durable state.
34
- - Numbered Codex routes prefer Main's active account slot and explicitly load the multi-Codex child extension.
27
+ ### Unified surface and modes
28
+
29
+ - Every `delegate_task` request declares `mode: "direct"` or `mode: "isolated"`. No path silently falls back between modes.
30
+ - Direct mode selects exactly one single, parallel, or chain shape. Unknown properties and mixed shapes fail before launch.
31
+ - Isolated mode accepts 1–8 typed tasks. `dependsOn` controls waves. `contextFrom` may name completed text tasks only and preserves declared order.
32
+ - Roles describe capability, not checkout placement. Role-level worktree isolation is rejected.
33
+
34
+ ### Global limits
35
+
36
+ - `config/pi-subagent/config.json` is the only user execution-policy source. It owns `maxSubagents`, `maxTurns`, optional `maxTokens`, `maxCorrections`, and child idle/hard timeout.
37
+ - Request fields cannot override or replenish policy. Durable requests store the policy snapshot and cumulative correction count; current config may tighten the correction allowance.
38
+ - Productive execution has no whole-run wall-clock deadline. Long productive work and later resume remain valid. Direct Herdr observation also has no whole-task elapsed deadline; worker idleness is bounded.
39
+ - Child idle/hard runtime, turn/token handling, outer abort, process I/O, status inspection, termination, and cleanup remain independently bounded. Direct Herdr workers use idle limits, not ephemeral child hard runtime.
40
+ - Ephemeral children share one FIFO executor pool. Queued time consumes no child timeout. Isolated Herdr Role launches receive the same non-refilling turn/token/runtime budget metadata.
41
+
42
+ ### Direct evidence
43
+
44
+ - Direct mode admits only Roles proven read-only from declared tools, extensions, and MCP resources. Direct changesets and write-capable Roles require isolated mode.
45
+ - Each direct worker has a recorded Herdr tab and Pi session identity. The first handle returns before completion; subsequent tab identities remain recoverable on the session branch.
46
+ - An exact settled worker with a bounded final Pi answer produces a follow-up to Main. Blocked, unknown, idle-stalled, truncated, or ambiguous outcomes retain actionable recovery identity. Session replacement never delivers to the wrong Main session.
47
+
48
+ ### Isolated lifecycle
49
+
50
+ - Changeset tasks durably record allocation intent before each external side effect. Unknown outcomes are retained and never guessed.
51
+ - One worker remains live across prompts, preliminary checks, follow-up/correction, readiness, Main's staging decisions, combined validation, and guarded promotion. Selected workers terminate after promotion; rejected workers terminate on explicit rejection.
52
+ - Optional same-worker follow-ups are admitted only while a changeset is actively working. After the final checked-evidence save, one synchronous queue-or-seal transition prevents admitted revisions from being lost; the runner never waits for routine input.
53
+ - Preliminary checks gate readiness. Any changed candidate invalidates previous readiness and validation.
54
+ - Optional task judgment binds to the exact preliminary candidate. Main selects and orders candidates in a separate owned integration checkout, where final full-suite checks and optional final judgment bind to the clean combined tip before promotion.
55
+ - Promotion requires exact unchanged clean Main and a passing combined generation. Durable promotion evidence precedes selected-worker termination and cleanup; recovery never rolls back a proven promotion.
56
+ - Rejected workers and superseded generations stay retained until Main explicitly releases their exact clean, proved-owned resources. At most two unreleased integration checkouts coexist.
57
+ - Failures, ambiguity, interruption, review findings, conflicts, drift, unproved termination, or cleanup failure enter `needs_attention` and preserve evidence.
58
+ - `subagent_status` is read-only. `subagent_resume` accepts only strict `retry`, `verify`, or `finalize` continuations. `subagent_abort` terminates only exact owned workers.
59
+ - The extension never pushes, opens a pull request, publishes, deploys, stashes, resets, force-cleans, or deletes unproved recoverable work.
60
+
61
+ ### Resource and launch policy
62
+
63
+ - Ambient child extensions and Skills stay disabled. Only Role/caller resources plus required internal adapters load.
64
+ - Every Role requires `tools`, `extensions`, and `skills` arrays. Omitted or empty `mcps` denies MCP access. Direct `pi-mcp-adapter` loading is rejected because it bypasses the allowlist.
65
+ - Selected extensions are trusted executable bundles, not a sandbox. All tools and lifecycle behavior they register load together.
66
+ - Role Skill names resolve through Main's effective Pi registry. Missing Roles, Skills, tools, MCP servers, routes, models, or thinking levels fail before the first productive turn.
67
+ - Route precedence is call model class, then Role default, then registered Model Task assignment/default. A direct model replaces only the route model and must support its thinking level.
68
+ - Main-only delegation/recovery tools and `ask_question` are excluded from children. Recursive delegation is unavailable.
69
+ - Package built-ins are `implementer`, `reviewer`, and `scout`; a same-named user Role overrides a built-in.
70
+
71
+ ### Executor boundaries
72
+
73
+ - A terminal response at the turn boundary succeeds; attempted continuation fails with typed `turn_limit` and bounded retained output.
74
+ - Token accounting sums completed assistant responses once. A terminal crossing succeeds. A continuing crossing completes tools and receives one response-only handoff; further continuation fails.
75
+ - Recognized Pi JSON events renew idle timeout; raw bytes do not. Child maximum runtime always terminates ephemeral children.
76
+ - Output, stderr, protocol events, callback draining, and inherited descendant streams are bounded.
77
+ - Low-level worktree finalization returns explicit `pruned`, `retained`, or `recovery` evidence and never force-deletes uncertain work.
78
+ - Exact review and working-change helpers produce bounded private evidence; callers must preserve identity guards and verdict validation.
79
+
80
+ ## Owned storage
81
+
82
+ - User config and Role Markdown: `<agent-dir>/config/pi-subagent/`
83
+ - Durable state: `<agent-dir>/config/pi-subagent/state/<repository-hash>/`
84
+ - Shared model routes: `<agent-dir>/config/pi-task-models/config.json`
85
+
86
+ State version 4 is strict. Older or malformed files are rejected rather than rewritten or migrated silently.
package/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # `@henryqw/pi-subagent`
2
2
 
3
- Delegate bounded work from Main to isolated Pi Roles. `delegate_task` remains lightweight generic delegation. It does not own durable checked implementation graphs.
4
-
5
- The public API also supplies Role launch, executor, worktree, and exact-evidence support for packages such as `@henryqw/pi-orchestrator`.
3
+ Delegate work to configured Pi Roles with [Herdr](https://herdr.dev/). Read-only direct tasks run in Herdr tabs in your current workspace; checked implementation runs in isolated Herdr worktrees. Both return a handle so Main can continue while they work.
6
4
 
7
5
  ![Pi showing six delegated tasks running in parallel](./example.png)
8
6
 
@@ -13,205 +11,181 @@ pi install npm:@henryqw/pi-task-models
13
11
  pi install npm:@henryqw/pi-subagent
14
12
  ```
15
13
 
16
- Run `/task-models` and configure the `fast` profile before delegating. Open `/task-models` again and verify that `fast` no longer says `not configured`.
14
+ Run `/task-models` and configure the `fast`, `balanced`, `frontier`, and `fav` routes you use.
17
15
 
18
- Install `pi-mcp-adapter` when any Role declares an MCP server allowlist:
16
+ Install `pi-mcp-adapter` only when a Role declares an MCP server allowlist:
19
17
 
20
18
  ```bash
21
19
  pi install npm:pi-mcp-adapter
22
20
  ```
23
21
 
24
- ## Works with
25
-
26
- | Package | Relationship | Purpose |
27
- | --- | --- | --- |
28
- | [`@henryqw/pi-orchestrator`](https://pi.henry.wang/extensions/pi-orchestrator) | Consumer | Owns durable checked local implementation graphs. |
29
- | [`@henryqw/pi-task-models`](https://pi.henry.wang/extensions/pi-task-models) | Required | Supplies `fast`, `balanced`, `frontier`, and `fav` model routes. |
30
- | [`@henryqw/pi-process`](https://pi.henry.wang/packages/pi-process) | Required | Runs bounded captured Git commands. |
31
- | [`pi-mcp-adapter`](https://www.npmjs.com/package/pi-mcp-adapter) | Optional | Exposes only the MCP servers selected by a Role. |
32
-
33
- Routes come from `~/.pi/agent/config/pi-task-models/config.json`. It stores explicit task overrides. Missing shared model config warns once because delegation needs a route.
34
-
35
22
  ## Use
36
23
 
37
- Start with one read-only delegation:
24
+ ![Delegation mode routing and the checked changeset lifecycle through recovery, validation, and promotion](./docs/delegation-routing.svg)
38
25
 
39
- ```json
40
- {
41
- "role": "scout",
42
- "name": "Map sign-in flow",
43
- "task": "Trace the sign-in request from entry point to session creation. Report the relevant files and unresolved risks. Do not edit files."
44
- }
45
- ```
26
+ Ask Main to delegate a bounded task, such as “Have a scout trace sign-in without editing files.” Main uses `delegate_task`; you receive progress and a result or an actionable failure while Main remains available.
46
27
 
47
- A separate child returns a bounded report to Main. It creates no saved Pi session and makes model requests through the selected route.
28
+ Commands are for you; tools and the packaged skill are for Main. You do not need to call agent tools or manage candidate identities yourself.
48
29
 
49
30
  | Surface | Type | Purpose |
50
31
  | --- | --- | --- |
51
- | `delegate_task` | tool | Run one bounded task, independent tasks in parallel, or dependent tasks in a chain. |
52
-
53
- Pi's built-in tool block shows each call and result.
32
+ | `/subagent` | command | Pick direct or isolated work to inspect; send or edit pending instructions for a locally active isolated changeset. Requires interactive TUI or RPC. |
33
+ | `delegate_task` | tool | Start read-only direct work or a durable isolated checked graph. |
34
+ | `subagent_status` | tool | Read durable isolated state and the exact allowed continuation without replaying work. |
35
+ | `subagent_resume` | tool | Perform the reported `retry`, `verify`, or `finalize` continuation; does not replace staging or integration. |
36
+ | `subagent_stage` | tool | Stage or resolve an exact candidate, or reject/revise it; rejection of a staged candidate freezes that generation. |
37
+ | `subagent_integrate` | tool | Advance staged dependents, refresh after clean Main drift, validate, record one correction, promote, reconcile interrupted promotion, clean up proven promotion, or explicitly release rejected/superseded resources. |
38
+ | `subagent_abort` | tool | Abort an isolated request only when no retained candidates or integration worktrees remain; cannot discard them. |
39
+ | `pi-subagent` | skill | Guide Main through delegation, authorization, checks, integration, and recovery. |
54
40
 
55
- Select exactly one `delegate_task` shape:
41
+ ### How Main routes delegation
56
42
 
57
- ```text
58
- // Single
59
- { role, name, task, model?, modelClass?, background? }
60
-
61
- // Parallel: 1–8 independent tasks
62
- { tasks: [{ role, name, task, model?, modelClass? }], background? }
63
-
64
- // Chain: 1–8 dependent tasks
65
- { chain: [{ role, name, task, model?, modelClass? }], background? }
66
- ```
43
+ Main chooses the mode from your request; you do not need to select one. Words like “direct” and “isolated” can signal your intent, but the work determines the route. Main declares the chosen mode in every `delegate_task` call, and the extension never falls back between modes.
67
44
 
68
- Main supplies each `name`. It must be a short description, about five words and fewer than 30 characters. Names cannot contain C0/C1 control characters such as newlines or terminal escapes. `role` and an explicit `model` also reject those controls.
45
+ | Mode | Request that triggers it | Checkout behavior |
46
+ | --- | --- | --- |
47
+ | `direct` | Read-only research, analysis, or review | Opens non-focused Herdr tabs in Main's current workspace. No worktree is created. |
48
+ | `isolated` | Implementation or a checked task graph | Runs changesets in owned Herdr worktrees; Main selects, validates, and promotes exact candidates. |
69
49
 
70
- `modelClass` selects `fast`, `balanced`, `frontier`, or `fav`. An explicit call class wins over a Role class. Without either, the configured task assignment or declared default applies. The route sets the model and exact thinking level.
50
+ Keep trivial mechanically verifiable work in Main. Keep tightly coupled changes under one owner rather than splitting by file count.
71
51
 
72
- An explicit `model` (`provider/modelId`) replaces only the route model and must support that thinking level. `background` applies to the whole selected mode, never one entry.
52
+ ### Direct delegation
73
53
 
74
- Parallel tasks start together, settle together, and report in input order. Chains are sequential and fail at the first failure. `{previous}` passes only the immediately preceding successful assistant output.
54
+ A read-only task:
75
55
 
76
- Foreground failures throw after keeping bounded sibling and recovery evidence. One call has one aggregate 50 KiB cap for Main-visible text. Final results show summaries first and full evidence below.
56
+ ```json
57
+ {
58
+ "mode": "direct",
59
+ "role": "scout",
60
+ "name": "Map sign-in flow",
61
+ "task": "Trace sign-in through session creation. Report files and risks. Do not edit files."
62
+ }
63
+ ```
77
64
 
78
- The status widget shows each task group name above at most three indented child rows. Each row shows a one-letter Role badge, status, activity, usage, and duration.
65
+ Direct mode selects exactly one shape:
79
66
 
80
- Background work belongs to its launching session. Shutdown or reload aborts it and may leave only recoverable-work evidence or no follow-up message.
67
+ ```text
68
+ Single: { mode: "direct", role, name, task, ... }
69
+ Parallel: { mode: "direct", tasks: [{ role, name, task, ... }] }
70
+ Chain: { mode: "direct", chain: [{ role, name, task, ... }] }
71
+ ```
81
72
 
82
- Each entry resolves its own Role, resources, route, and optional isolation. A Role with `isolation: worktree` gets a separate deterministic worktree when available. Non-Git and unborn-`HEAD` contexts can use Main's directory. Other setup failures, including unsafe submodule layouts, reject instead of falling back. Siblings and chain steps never share a created worktree.
73
+ Only Roles with known read-only tools and no extensions or MCP servers may run direct. Write-capable Roles and direct `changeset` tasks are rejected; use isolated mode for implementation. Parallel tasks run independently; chains replace each literal `{previous}` with the preceding successful answer and stop on failure.
83
74
 
84
- See the [orchestration guide](./docs/orchestration.md) for full delegation, transport, isolation, and UI behavior.
75
+ The tool returns a task ID and first Herdr tab after launch, not the answer. The extension observes each worker and sends one result to Main when the workflow finishes. If Main is busy, Pi queues it after the current turn; if idle, it starts a turn. A blocked, stalled, unknown, or truncated result is not reported as success. Session switch or shutdown stops observation and preserves tab identities for recovery. Open `/subagent` on the session branch to inspect exact tabs, agents, and Pi session files, including tabs launched after the first. A recorded tab may no longer be running; locally observed work is labelled separately.
85
76
 
86
- ### Delegation guidance
77
+ ### Isolated checked graphs
87
78
 
88
- `delegate_task` is generic delegation, not checked implementation orchestration. Main or a consuming package owns validation, review, integration, recovery, and durable state.
79
+ An isolated request has one durable ID, one goal, and 1–8 typed tasks:
89
80
 
90
- Implementers remove only task-created temporary, generated, or ignored artifacts. Required deliverables and unrelated files stay intact. They never use `git clean` or blanket deletion, and unclear paths block.
81
+ ```json
82
+ {
83
+ "mode": "isolated",
84
+ "id": "refresh-repair",
85
+ "goal": "Repair token refresh with exact checked evidence.",
86
+ "tasks": [
87
+ {
88
+ "id": "inspect",
89
+ "kind": "text",
90
+ "role": "scout",
91
+ "modelClass": "fast",
92
+ "requirements": "Identify the refresh failure and relevant tests.",
93
+ "deliverable": "A concise evidence report.",
94
+ "dependsOn": [],
95
+ "contextFrom": []
96
+ },
97
+ {
98
+ "id": "repair",
99
+ "kind": "changeset",
100
+ "role": "implementer",
101
+ "modelClass": "balanced",
102
+ "requirements": "Implement the smallest correct repair.",
103
+ "deliverable": "A committed candidate in the owned worktree.",
104
+ "dependsOn": ["inspect"],
105
+ "contextFrom": ["inspect"],
106
+ "checks": [
107
+ { "command": "pnpm", "args": ["test", "--", "refresh"] }
108
+ ],
109
+ "judgment": {
110
+ "role": "reviewer",
111
+ "modelClass": "balanced",
112
+ "criterion": "The exact candidate fixes refresh and preserves unrelated behavior."
113
+ }
114
+ }
115
+ ],
116
+ "finalChecks": [
117
+ { "command": "pnpm", "args": ["test", "--", "refresh"] }
118
+ ]
119
+ }
120
+ ```
91
121
 
92
- For known regressions with a runner that supports test-name filtering, use a test-name filter. Keep broad package or workspace checks to one caller-owned final validation after relevant changes integrate. Generic delegation does not run that check.
122
+ `dependsOn` controls scheduling. `contextFrom` may name completed text tasks and preserves the declared order. Changesets require focused task checks; a graph with changesets requires final checks. A canonical root `package.json` test script is required: the runner adds `pnpm test` to final checks if missing, and Main runs it on the combined integration checkout before promotion. Checks and judgments bind to exact Git identities; candidate drift, Main drift, mutation during validation, ambiguity, or conflicts stop promotion and retain evidence.
93
123
 
94
- Before delegating:
124
+ `delegate_task` and `subagent_resume` return a durable request ID after its state is saved; productive work continues while Main is free. Pi delivers completion or attention as a follow-up message in the launching session. Use `subagent_status` if delivery is missed or you need current state.
95
125
 
96
- - Keep trivial, single-owner, mechanically verifiable edits in Main.
97
- - For literal UI or copy defects, search the exact text first. Read only its producer and nearby assertions unless ownership remains unclear.
98
- - Find concrete outcomes that can ship on their own.
99
- - Split only those outcomes. Give each one owner and a focused check.
100
- - Run independent work in parallel.
101
- - Prefer parallel delegation when at least two outcomes are independent.
102
- - Use only as many units as independent outcomes require. Never create units to reach a count.
126
+ The worker produces a clean committed candidate, then runs focused preliminary checks and optional judgment. The runner records a ready candidate but **does not integrate it automatically**. Main inspects `subagent_status`, selects each exact candidate with `subagent_stage` in an owned integration worktree, and resolves conflicts there. `subagent_integrate` can advance dependent tasks from a staged snapshot, validate the combined tip with full root checks and optional final judgment, and promote it to Main only after exact validation. A definitive failed combined check/review allows one committed correction in the integration checkout followed by another validation. Main must explicitly promote; promotion and cleanup never silently discard retained work.
103
127
 
104
- Its ordinary review loop is optional. Use it only when the caller or repository policy explicitly requires judgment review.
128
+ Open `/subagent` without arguments to pick work by label. The menu shows direct records on this session branch and isolated requests in the current canonical Git checkout (including requests launched in other sessions). Pick a task to inspect status and recovery in separate notices; retained paths and worker identities remain exact even when the request has many attempts or generations. Refresh or choose completed/history for older work. Invalid state IDs are warned about and preserved. Outside Git or when isolated inventory is unavailable, direct recovery still works. Browsing does not resume work or change resources.
105
129
 
106
- - Call `delegate_task` with `role: "reviewer"` to select the effective `reviewer` Role.
107
- - Its task packet must state the read-only scope and exact `PASS` or findings contract. Include exact acceptance criteria and validation evidence.
108
- - The Reviewer must receive exact candidate evidence. An ordinary review from Main's unchanged checkout cannot inspect an isolated candidate.
109
- - Fix initial findings together. Validate repaired inputs once before focused re-review. Include the original findings and acceptance criteria, exact repaired-candidate evidence, and validation evidence.
110
- - Only `PASS` completes the loop. Surface and block on re-review findings or empty output. Retry empty output only when explicit caller policy requires one. A second empty result blocks. Do not add another round.
130
+ For a locally active unsealed isolated changeset, choose **Send follow-up instructions** to open a native multiline editor; submission queues the instruction, not a completed revision. Choose **Edit queued instructions** to withdraw *all still-pending* instructions for that selected task before the editor opens. Their full text appears in FIFO order separated by blank lines. Explicit submission queues **one replacement** at the tail. Cancel leaves the withdrawn instructions removed; already-claimed instructions, initial prompts, and automatic corrections cannot be recalled. A worker may seal during editing: failed submission reopens the full submitted text for deliberate editing or copying, never automatically requeues it. Combined text above the single-instruction limit is preserved in the editor but rejected until shortened. Cancelling before withdrawal makes no change. Neither action touches Main's editor or Pi's global Option+Up queue shortcut. No-UI sessions must use the agent tools for inspection and recovery.
111
131
 
112
- A Role selects base tools, extensions, named Skills, MCP servers, instructions, and optional worktree isolation. Named Skills resolve from Main's effective Pi registry. Unavailable names warn and skip.
132
+ A queued follow-up runs after the current turn settles and must produce a new clean commit that passes preliminary checks again. When no queued revision remains, the checked candidate seals immediately; there is no guaranteed post-completion editing window. Late follow-ups fail visibly.
113
133
 
114
- Use `mcps` to allow configured MCP servers by exact name. An omitted or empty `mcps` list denies MCP access. Every tool exposed by an allowed server is available through `pi-mcp-adapter`.
134
+ ### Isolated recovery
115
135
 
116
- The adapter receives an isolated in-memory config containing only those servers. Unknown names fail before the first model turn. Loading `pi-mcp-adapter` directly through `extensions` is rejected because it would bypass the allowlist.
136
+ Main uses the recovery tools in the interface table above. The following details describe their exact identity and retained-work requirements.
117
137
 
118
- Children disable ambient extension and Skill discovery. `tools: []` adds no base tools, but selected extension tools and caller tools still activate. `extensions: []` adds no Role extension bundle. `skills: []` adds no separately named Role Skills, but selected extension Skills still load.
138
+ Pass the exact `generation`, candidate (`taskId`, `attempt`, `candidate`) and `expectedTip` identities reported by status to stage; pass the generation and combined `expectedTip` to integrate. If Main advances cleanly before promotion, call `subagent_integrate refresh` with the recorded `expectedMain`, old `expectedTip`, and new clean `newMain`. Main must restage selected immutable candidates into the new generation and rerun combined checks. Dirty or divergent Main blocks refresh. Stale identities fail. At most two integration worktrees may remain unreleased per request; a superseded generation is read-only. To release a superseded *clean* integration checkout, call `subagent_integrate` with `{ id, action: "release", generation, expectedTip }`, using that generation's last staged tip (or integration base when no stage completed). To release a rejected candidate after its worker has terminated and all generations using it are released, pass `{ id, action: "release", generation: <latest generation number, or 1>, taskId, attempt, expectedTip: <candidate tip> }`. Release persists each host/checkout/branch step, verifies ownership and cleanliness (including ignored files), and deletes the branch only if its ref still names the exact tip. Dirty, conflicted, changed, or uncertain resources remain for manual inspection; retry the same release after resolving the obstacle. Released checkouts free a retained slot; abort is available after every retained resource is released. Rejection can invalidate dependent work; if fresh dependent execution is impossible, start a new request instead of forcing promotion. Productive requests have no whole-run wall-clock deadline. Resume does not reset the recorded policy or correction count. Child limits, abort signals, process I/O, status inspection, exact termination, and cleanup retain finite safety bounds.
119
139
 
120
- Main-only delegation and orchestration tools, plus `ask_question`, are always excluded. Requested Role or caller tool names are checked after provider loading. Unavailable tools fail before the first model turn.
140
+ The extension never pushes, opens a pull request, publishes, deploys, force-cleans recoverable work, or silently falls back to Main.
121
141
 
122
142
  ## Config
123
143
 
124
- pi-subagent owns `~/.pi/agent/config/pi-subagent/config.json`. It is optional. A missing file uses these defaults without a warning.
144
+ pi-subagent owns `~/.pi/agent/config/pi-subagent/config.json`. A missing file silently uses defaults.
125
145
 
126
146
  | Name | Description | Values | Default |
127
147
  | --- | --- | --- | --- |
128
- | `maxSubagents` | Sets the maximum number of active child processes. | Safe integer of at least 1. | `5` |
129
- | `maxTurns` | Sets the hard provider-turn limit for each child. | Safe integer of at least 1. | `50` |
130
- | `maxTokens` | Sets the token limit for each child. | Safe integer of at least 1. | Unlimited |
131
- | `timeout.idleMinutes` | Sets the idle timeout for a child. | Positive minutes; minutes × 60,000 ≤ 2,147,483,647 ms | `10` |
132
- | `timeout.maxMinutes` | Sets the maximum runtime for a child. | Positive minutes greater than `idleMinutes`; minutes × 60,000 ≤ 2,147,483,647 ms | `30` |
133
-
134
- `maxTokens` applies separately to every child. It is not a shared pool or per-call option. Set it only in this file.
135
-
136
- Pi adds each completed assistant response's `Usage.totalTokens` once. This matches the executor's aggregate `Usage`. At 80%, a Role receives one convergence warning.
137
-
138
- A terminal response that crosses `maxTokens` succeeds. A continuing crossing turn completes its tools. Pi then disables tools and allows one response-only handoff. That handoff can overshoot the limit, so `maxTokens` is not an exact cap. Further continuation rejects with `token_limit`, aggregate `Usage`, and bounded last output.
139
-
140
- Excess children wait FIFO without using a child timeout. A terminal response on turn 50 succeeds; an attempted continuation rejects with `turn_limit`.
141
-
142
- ### Final response handoff
143
-
144
- Role launches reserve a response-only handoff at a continuing `maxTokens` crossing or the penultimate `maxTurns` turn. This includes `delegate_task` and Role launches made through the public API.
145
-
146
- With `maxTurns` set to 1, Pi disables tools at startup. The sole provider turn is the response-only handoff.
148
+ | `maxSubagents` | Concurrent direct Herdr workers and ephemeral child-process limit | Safe integer ≥ 1 | `5` |
149
+ | `maxTurns` | Provider-turn limit per child | Safe integer ≥ 1 | `50` |
150
+ | `maxTokens` | Optional token limit per child | Safe integer ≥ 1 | Unlimited |
151
+ | `maxCorrections` | Same-worker automatic corrections per isolated request | Safe integer ≥ 0 | `1` |
152
+ | `timeout.idleMinutes` | Direct worker and ephemeral child idle timeout | Positive and within Node's timer range | `10` |
153
+ | `timeout.maxMinutes` | Ephemeral child hard runtime | Greater than idle and within Node's timer range | `30` |
147
154
 
148
- Pi waits for the current turn's tools. It then disables all tools and requests a final report. A terminal boundary response gets no handoff.
155
+ Limits come only from this global file. Request fields cannot override or replenish them. Existing durable requests keep their recorded policy, while a lower current correction limit can tighten recovery. There is intentionally no whole-run timeout setting.
149
156
 
150
- The fixed decision packet asks for Status (completed, blocked, or incomplete), one-sentence Outcome, up to three concrete Evidence facts, Blocker, one material Risk, and one Suggested next action. It is the default. Exact output required by the assigned task or Role takes precedence. The child returns only that caller-required exact output, such as structured JSON or `PASS`. The handoff stays within `maxTurns`, but it is the one allowed turn after a token crossing. Structured executor and retained-worktree facts remain authoritative; the model handoff supplies semantic context and a suggested next action.
151
-
152
- A raw `createEphemeralSubagentExecutor` launch can enforce the extra-turn window. It cannot guarantee disabled tools or the final handoff. A timeout, provider failure, or child-process failure can also end a Role launch before handoff.
153
-
154
- Malformed or unreadable JSON, a non-object root, unknown keys, and invalid values produce one warning. Invalid settings use defaults while valid settings still apply. If the effective maximum is not greater than the idle timeout, both timeout settings use defaults. The file is never rewritten.
155
-
156
- `PI_SUBAGENT_MAX_SUBAGENTS` overrides `maxSubagents` for the session. It must be a positive integer. An invalid value prevents the extension from loading, so `delegate_task` is unavailable.
157
+ Malformed or unreadable JSON, unknown keys, and invalid values block delegation with one actionable warning. The file is preserved and never rewritten automatically.
157
158
 
158
159
  ### Roles
159
160
 
160
- Role Markdown files live beside the config file. They require frontmatter and a Markdown system prompt body.
161
+ Role Markdown lives in `~/.pi/agent/config/pi-subagent/` and requires frontmatter plus a Markdown system prompt.
161
162
 
162
163
  | Field | Requirement |
163
164
  | --- | --- |
164
- | `name` | Required unique, non-empty text without C0/C1 controls. |
165
- | `description` | Required non-empty text without C0/C1 controls. |
166
- | `modelClass` | Optional; `fast`, `balanced`, `frontier`, or `fav`. |
167
- | `tools` | Required YAML array of non-empty tool names. `[]` selects no base built-ins. |
168
- | `isolation` | Optional; only `worktree`. |
169
- | `extensions` | Required YAML array. Entries are absolute paths, `~/…`, `file://`, or `npm:`, `git:`, `github:`, `https?:`, or `ssh:` sources. |
170
- | `skills` | Required YAML array of non-empty Skill names. |
171
- | `mcps` | Optional YAML array of exact MCP server names. Omitted or `[]` denies MCP access. |
172
- | body | Required Markdown system prompt after the frontmatter. |
165
+ | `name`, `description` | Required non-empty text without terminal control characters |
166
+ | `modelClass` | Optional `fast`, `balanced`, `frontier`, or `fav` default |
167
+ | `tools` | Required array of base tool names; `[]` selects none |
168
+ | `extensions` | Required array of trusted absolute paths or supported package sources |
169
+ | `skills` | Required array of effective Pi Skill names |
170
+ | `mcps` | Optional exact MCP server names; omitted or `[]` denies MCP access |
171
+ | body | Required system instructions |
173
172
 
174
- A Role's `modelClass` is a default. A call-level class wins.
173
+ Roles describe responsibility and capabilities. They do not choose isolation; each request does. A same-named user Role overrides a built-in Role. The package ships `implementer`, `reviewer`, and `scout`.
175
174
 
176
- An unreadable or invalid Role fails loading fast. Duplicate Role names are rejected. A same-named user file overrides a built-in Role.
177
-
178
- A missing named Skill rejects `prepareRoleLaunch` and `resolveConfiguredRoleLaunch`. `delegate_task` returns a Role-specific workflow error and does not start a child.
179
-
180
- The package always provides these built-in Roles. Their files leave `modelClass` unset, so they use the configured `pi-subagent/delegateTask` assignment or declared default unless a call overrides it:
181
-
182
- | Role | Purpose | Isolation/use |
183
- | --- | --- | --- |
184
- | `implementer` | Make and validate one focused change. | Requests a worktree; commits scoped work locally. Never pushes or opens a PR without permission. |
185
- | `reviewer` | Review supplied plans, files, or caller-prepared exact evidence. | Read-only; never edits or commits. |
186
- | `scout` | Map code and evidence for one bounded task. | Read-only; never changes files. |
175
+ Children disable ambient extension and Skill discovery. Only declared resources and required internal policy adapters load. Missing Skills, tools, MCP servers, Roles, or routes fail before productive work starts. Main-only delegation and recovery tools plus `ask_question` are excluded from children.
187
176
 
188
177
  ## API
189
178
 
190
- The package root includes these main exports:
179
+ The package root exports the Role loader and launch APIs, the FIFO ephemeral executor, child-worktree helpers, exact review and working-change evidence helpers, checked isolated schemas, runtimes, and runner types.
191
180
 
192
- | Surface | Type | Purpose |
193
- | --- | --- | --- |
194
- | `loadRoles` | function | Loads built-in and user Role definitions. |
195
- | `RoleName` / `parseRoleName` | type/function | Normalizes arbitrary Role names and rejects empty or C0/C1 control-character values. |
196
- | `resolveRoleSkills` | function | Resolves a Role's named Skills from Pi's effective registry. |
197
- | `resolveRoleLaunch` | function | Resolves a Role, route, and launch resources. |
198
- | `resolveConfiguredRoleLaunch` | function | Resolves a configured Role and its package resources with an explicit model class. Rejects missing Role Skills. |
199
- | `createRoleLaunch` | function | Builds launch arguments from a resolved route. |
200
- | `prepareRoleLaunch` / `finalizeRoleLaunch` | functions | Separates the stable Role prompt, exposes its immutable tool policy, and rejects missing Role Skills. |
201
- | `createEphemeralSubagentExecutor` | function | Creates the bounded child-process executor. |
202
- | Worktree helpers | functions | Create, inspect, finalize, and report child worktrees. |
203
- | `prepareExactReviewEvidence` | function | Create a bounded private base-to-tip patch with exact Git identity for caller-owned review. |
181
+ `createEphemeralSubagentExecutor` accepts global concurrency, turn/token, idle, and hard-runtime policy. Queued time consumes no child timeout. A run resolves resources only after receiving its permit and accepts abort, output, token, and activity callbacks. Output and diagnostics are bounded.
204
182
 
205
- The executor works only inside the active Pi process. It does not discover or start a standalone Node.js Pi installation.
183
+ See [Orchestration and package-author API](./docs/orchestration.md) for the detailed contracts and recovery model.
206
184
 
207
- `finalizeChildWorktree` returns the breaking `WorktreePayload` lifecycle union. `pruned` proves zero commits, a clean tree, and removed worktree and branch. `retained` contains measured `commits` and `dirty` values. `recovery` has an actionable `note` and only completed measurements. An omitted recovery measurement is unknown.
185
+ ## State and storage
208
186
 
209
- See the [public Role and executor API](./docs/orchestration.md#public-role-and-executor-api) for contracts and a `prepare` example. Pass `modelClass` to `resolveRoleLaunch` to override a Role default. `resolveConfiguredRoleLaunch` requires a model class and does not use Role or task defaults.
187
+ Durable state is private under `config/pi-subagent/state/`. State v5 rejects v4 and older files rather than migrating them; retain the old state and recover its work manually.
210
188
 
211
189
  ## Limits and recovery
212
190
 
213
- An explicitly selected extension is trusted, not sandboxed. Its tools, Skills, and executable behavior load together. Select fewer trusted extensions to reduce scope. pi-subagent does not guess or remove undocumented dependencies.
214
-
215
- Worktree cleanup never force-deletes recoverable work. Retained and recovery payloads report the worktree path and branch.
216
-
217
- See the [public Role and executor API](./docs/orchestration.md#public-role-and-executor-api) for worktree and exact-review-evidence contracts.
191
+ Role extensions and MCP servers are trusted executable code, not a sandbox. Select the smallest resource set. Read-only direct Roles cannot write through their declared tools. This is a capability check, not an OS sandbox; external processes and changes to Main's checkout can still make a concurrent read stale. Retained-work reports identify exact resources for deliberate recovery.
@@ -0,0 +1,11 @@
1
+ import type { EphemeralSubagentExecutor } from "./ephemeral.ts";
2
+ import type { DirectProcessRunner, ExactReviewExecutor } from "./git-runtime.ts";
3
+ import type { LaunchRuntimeOptions } from "./launch-runtime.ts";
4
+ export interface CanonicalGitRootResolverOptions {
5
+ runProcess?: DirectProcessRunner;
6
+ now?: () => number;
7
+ }
8
+ /** Resolve one canonical Git top-level with cancellation and bounded Git I/O. */
9
+ export declare function createCanonicalGitRootResolver(options?: CanonicalGitRootResolverOptions): LaunchRuntimeOptions["resolveRoot"];
10
+ /** Adapt an already verified Judgment launch without adding argv, environment, or resources. */
11
+ export declare function createExactJudgmentExecutor(executor: EphemeralSubagentExecutor): ExactReviewExecutor;