@henryqw/pi-subagent 18.0.1 → 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.
- package/CONTEXT.md +76 -24
- package/README.md +116 -142
- package/dist/composition.d.ts +11 -0
- package/dist/composition.js +107 -0
- package/dist/direct-herdr.d.ts +17 -0
- package/dist/direct-herdr.js +219 -0
- package/dist/ephemeral.d.ts +2 -2
- package/dist/ephemeral.js +7 -7
- package/dist/git-runtime.d.ts +110 -0
- package/dist/git-runtime.js +472 -0
- package/dist/herdr-runtime.d.ts +137 -0
- package/dist/herdr-runtime.js +1228 -0
- package/dist/index.d.ts +11 -5
- package/dist/index.js +20 -16
- package/dist/integration-git.d.ts +59 -0
- package/dist/integration-git.js +484 -0
- package/dist/launch-runtime.d.ts +54 -0
- package/dist/launch-runtime.js +195 -0
- package/dist/process.d.ts +9 -0
- package/dist/process.js +11 -0
- package/dist/runner.d.ts +356 -0
- package/dist/runner.js +2710 -0
- package/dist/schema.d.ts +2234 -0
- package/dist/schema.js +1236 -0
- package/dist/store.d.ts +50 -0
- package/dist/store.js +288 -0
- package/dist/working-evidence.d.ts +25 -0
- package/dist/working-evidence.js +261 -0
- package/docs/adr/001-composable-ephemeral-execution.md +3 -3
- package/docs/delegation-routing.html +89 -0
- package/docs/delegation-routing.svg +78 -0
- package/docs/orchestration-architecture.html +238 -0
- package/docs/orchestration-architecture.svg +189 -0
- package/docs/orchestration.md +122 -331
- package/example.png +0 -0
- package/examples/roles/implementer.md +2 -3
- package/extensions/admission.ts +66 -0
- package/extensions/config.ts +51 -37
- package/extensions/isolated.ts +638 -0
- package/extensions/result-transport.ts +13 -103
- package/extensions/role-tools.ts +11 -11
- package/extensions/subagent-command.ts +167 -0
- package/extensions/subagent.ts +264 -540
- package/extensions/task-name.ts +1 -2
- package/extensions/workflow.ts +60 -96
- package/package.json +14 -6
- package/skills/pi-subagent/SKILL.md +35 -0
package/CONTEXT.md
CHANGED
|
@@ -2,33 +2,85 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
|
|
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
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- `
|
|
34
|
-
-
|
|
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
|
|
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
|

|
|
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`
|
|
14
|
+
Run `/task-models` and configure the `fast`, `balanced`, `frontier`, and `fav` routes you use.
|
|
17
15
|
|
|
18
|
-
Install `pi-mcp-adapter` when
|
|
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
|
-
|
|
24
|
+

|
|
38
25
|
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
| `
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
41
|
+
### How Main routes delegation
|
|
56
42
|
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
50
|
+
Keep trivial mechanically verifiable work in Main. Keep tightly coupled changes under one owner rather than splitting by file count.
|
|
71
51
|
|
|
72
|
-
|
|
52
|
+
### Direct delegation
|
|
73
53
|
|
|
74
|
-
|
|
54
|
+
A read-only task:
|
|
75
55
|
|
|
76
|
-
|
|
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
|
-
|
|
65
|
+
Direct mode selects exactly one shape:
|
|
79
66
|
|
|
80
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
77
|
+
### Isolated checked graphs
|
|
87
78
|
|
|
88
|
-
|
|
79
|
+
An isolated request has one durable ID, one goal, and 1–8 typed tasks:
|
|
89
80
|
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
|
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
|
-
|
|
134
|
+
### Isolated recovery
|
|
115
135
|
|
|
116
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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`.
|
|
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` |
|
|
129
|
-
| `maxTurns` |
|
|
130
|
-
| `maxTokens` |
|
|
131
|
-
| `
|
|
132
|
-
| `timeout.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
165
|
-
| `
|
|
166
|
-
| `
|
|
167
|
-
| `
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
|
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
183
|
+
See [Orchestration and package-author API](./docs/orchestration.md) for the detailed contracts and recovery model.
|
|
206
184
|
|
|
207
|
-
|
|
185
|
+
## State and storage
|
|
208
186
|
|
|
209
|
-
|
|
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
|
-
|
|
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;
|