pi-subagents 0.49.0 → 0.51.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/CHANGELOG.md +97 -0
- package/agents/gpt-pro.md +17 -0
- package/agents/oracle.md +7 -5
- package/agents/researcher.md +1 -1
- package/agents/reviewer.md +2 -2
- package/agents/scout.md +1 -1
- package/agents/worker.md +1 -1
- package/async-retention-discovery-worker.mjs +180 -0
- package/docs/agents.md +37 -2
- package/docs/configuration.md +76 -14
- package/docs/extension-api.md +78 -1
- package/docs/missions.md +1 -1
- package/docs/observability.md +20 -4
- package/docs/tool-reference.md +55 -39
- package/docs/workflows.md +171 -5
- package/package.json +4 -2
- package/skills/pi-subagents/SKILL.md +5 -4
- package/skills/pi-subagents/references/constraints-and-recipes.md +9 -6
- package/skills/pi-subagents/references/execution-controls.md +22 -18
- package/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
- package/skills/pi-subagents/references/prompting-and-roles.md +33 -17
- package/src/agents/agent-management.ts +100 -345
- package/src/agents/agent-serializer.ts +2 -0
- package/src/agents/agents.ts +135 -25
- package/src/api/external-job-provider.ts +185 -0
- package/src/api/external-runs.ts +174 -84
- package/src/api/preflight.ts +42 -11
- package/src/api/shared-types.ts +2 -0
- package/src/extension/config.ts +36 -3
- package/src/extension/doctor.ts +3 -6
- package/src/extension/fanout-child.ts +2 -2
- package/src/extension/index.ts +210 -90
- package/src/extension/public-execution.ts +31 -2
- package/src/extension/rpc.ts +5 -1
- package/src/extension/schemas.ts +14 -36
- package/src/extension/tool-description.ts +37 -24
- package/src/inspectors/herdr/actions.ts +5 -9
- package/src/inspectors/herdr/inspector-runner.ts +2 -1
- package/src/inspectors/herdr/project-panes.ts +4 -8
- package/src/inspectors/herdr/shell-command.ts +16 -0
- package/src/intercom/intercom-bridge.ts +2 -3
- package/src/intercom/native-supervisor-channel.ts +49 -51
- package/src/missions/goal-driver.ts +3 -1
- package/src/missions/lifecycle.ts +6 -1
- package/src/missions/store.ts +4 -9
- package/src/profiles/profiles.ts +3 -1
- package/src/runs/background/active-run-index.ts +94 -1
- package/src/runs/background/async-execution.ts +98 -38
- package/src/runs/background/async-job-tracker.ts +21 -4
- package/src/runs/background/async-resume.ts +30 -17
- package/src/runs/background/async-retention.ts +888 -0
- package/src/runs/background/async-status-snapshot.ts +277 -0
- package/src/runs/background/async-status.ts +47 -56
- package/src/runs/background/chain-append.ts +3 -33
- package/src/runs/background/chain-root-attachment.ts +2 -2
- package/src/runs/background/completion-replay.ts +11 -1
- package/src/runs/background/control-channel.ts +14 -68
- package/src/runs/background/fleet-view.ts +3 -1
- package/src/runs/background/index-segment.ts +59 -0
- package/src/runs/background/notify.ts +3 -1
- package/src/runs/background/result-files.ts +505 -0
- package/src/runs/background/result-watcher.ts +250 -51
- package/src/runs/background/retained-children.ts +79 -20
- package/src/runs/background/run-id-query.ts +7 -0
- package/src/runs/background/run-id-resolver.ts +37 -29
- package/src/runs/background/run-status.ts +34 -20
- package/src/runs/background/scheduled-runs.ts +71 -27
- package/src/runs/background/stale-run-reconciler.ts +33 -16
- package/src/runs/background/steering.ts +11 -1
- package/src/runs/background/subagent-runner.ts +535 -161
- package/src/runs/background/subagent-wait.ts +9 -7
- package/src/runs/background/terminal-run-index.ts +129 -0
- package/src/runs/background/wait-completions.ts +22 -2
- package/src/runs/background/wait-subscriptions.ts +80 -1
- package/src/runs/foreground/async-dismiss-action.ts +2 -1
- package/src/runs/foreground/async-steering-action.ts +21 -14
- package/src/runs/foreground/execution.ts +221 -15
- package/src/runs/foreground/subagent-executor.ts +529 -1519
- package/src/runs/foreground/workflow-foreground-steering.ts +6 -5
- package/src/runs/shared/chain-outputs.ts +1 -3
- package/src/runs/shared/completion-guard.ts +96 -6
- package/src/runs/shared/external-cli-runner.ts +4 -0
- package/src/runs/shared/external-job-bridge.ts +450 -0
- package/src/runs/shared/external-job-runner.ts +286 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +14 -0
- package/src/runs/shared/model-fallback.ts +34 -3
- package/src/runs/shared/nested-events.ts +66 -62
- package/src/runs/shared/orca-progress-tabs.ts +437 -0
- package/src/runs/shared/parallel-handoff.ts +46 -4
- package/src/runs/shared/parallel-utils.ts +6 -15
- package/src/runs/shared/permissions.ts +5 -1
- package/src/runs/shared/pi-args.ts +8 -1
- package/src/runs/shared/subagent-control.ts +41 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +13 -15
- package/src/runs/shared/subagent-startup-retry.ts +12 -0
- package/src/runs/shared/tool-timeout.ts +93 -0
- package/src/runs/shared/workflow-graph.ts +1 -23
- package/src/runs/shared/worktree.ts +12 -1
- package/src/shared/atomic-json.ts +22 -2
- package/src/shared/capacity-resilient-json.ts +102 -0
- package/src/shared/completion-owner.ts +14 -0
- package/src/shared/file-system-retry.ts +49 -1
- package/src/shared/fork-context.ts +42 -0
- package/src/shared/prompt-resources.ts +0 -40
- package/src/shared/settings.ts +3 -27
- package/src/shared/types.ts +88 -27
- package/src/shared/utils.ts +8 -0
- package/src/shared/watch-strategy.ts +10 -0
- package/src/slash/slash-commands.ts +45 -28
- package/src/slash/slash-live-state.ts +3 -0
- package/src/tui/fleet-status.ts +160 -45
- package/src/tui/fleet.ts +186 -28
- package/src/tui/render.ts +41 -10
- package/src/workflows/chat-progress.ts +7 -5
- package/src/workflows/scripted-workflow.ts +424 -125
- package/src/runs/foreground/chain-clarify.ts +0 -1354
- package/src/runs/foreground/chain-execution.ts +0 -1565
package/docs/workflows.md
CHANGED
|
@@ -10,7 +10,7 @@ Use orchestration as parent-agent guidance, not as a runtime workflow mode. For
|
|
|
10
10
|
clarify → scout → worker → fresh reviewers → worker
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
Packaged `worker`, `oracle`, and `advisor` default to forked context when a launch omits `context
|
|
13
|
+
Packaged `worker`, `oracle`, and `advisor` default to forked context when a launch omits `context`. If the parent has no persisted session file or current leaf yet, that implicit default falls back to `fresh`. Pass `context: "fresh"` when you intentionally want a fresh child run, or `context: "fork"` when fork must remain strict.
|
|
14
14
|
|
|
15
15
|
Child-safety boundaries are enforced at runtime:
|
|
16
16
|
|
|
@@ -35,7 +35,7 @@ Add `autofix` to `/parallel-review` or `/parallel-cleanup` to apply only the syn
|
|
|
35
35
|
|
|
36
36
|
## Scripted workflows (workflowScript)
|
|
37
37
|
|
|
38
|
-
All model-facing subagent execution is expressed through `workflowScript` in the `subagent` tool. Use stable keys and ordinary JavaScript for one child, sequence, and parallelism. Scripts are ordinary JavaScript statement bodies. Use an explicit `return` for a useful result:
|
|
38
|
+
All model-facing subagent execution is expressed through `workflowScript` in the `subagent` tool. Use stable keys and ordinary JavaScript for one child, sequence, and parallelism. For ordinary parallel fanout, use `await runs.all([{ key, agent, task }, ...])`; do not read `.output` from unawaited `runs.run` launches. Store a `runs.run` promise only when the script later observes it with `await`, `Promise.race`, or `Promise.all`, such as steering a live child before awaiting its result. Scripts are ordinary JavaScript statement bodies. Use an explicit `return` for a useful result:
|
|
39
39
|
|
|
40
40
|
```js
|
|
41
41
|
subagent({ workflowScript: `
|
|
@@ -48,6 +48,153 @@ subagent({ workflowScript: `
|
|
|
48
48
|
` });
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
Keep helper functions portable across Node and Bun. Use top-level `await`, plain helper functions that return `runs.run(...)`, or explicit Promise chains. Do not define nested `async function` helpers, async arrows, or async methods inside `workflowScript`; native async helpers hide child-launch observation in Bun and are rejected.
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
subagent({ workflowScript: `
|
|
55
|
+
function scan() {
|
|
56
|
+
return runs.run("scan", { agent: "scout", task: "Scan the codebase" });
|
|
57
|
+
}
|
|
58
|
+
const result = await scan();
|
|
59
|
+
return result.output;
|
|
60
|
+
` });
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Chaining is still supported. The supported form is scripted chaining: await one `runs.run(...)` result, then pass its output into the next step. Parallel fanout uses `runs.all(...)` inside the same script.
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
subagent({ workflowScript: `
|
|
67
|
+
const plan = await runs.run("plan", { agent: "scout", task: "Plan the migration" });
|
|
68
|
+
const patch = await runs.run("patch", { agent: "worker", task: "Implement this plan:\n" + plan.output });
|
|
69
|
+
return patch.output;
|
|
70
|
+
` });
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Steering a workflow child
|
|
74
|
+
|
|
75
|
+
Use `await runs.steer(key, message, options?)` after `runs.run` or `runs.all` has launched that stable key. Scripts do not target raw run ids. The optional fields are `mode: "steer" | "follow_up" | "auto"`, a non-negative child `index`, and a positive `ackTimeoutMs`.
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
subagent({ workflowScript: `
|
|
79
|
+
const writer = runs.run("writer", { agent: "worker", task: "Implement the change" });
|
|
80
|
+
const evidence = await runs.run("evidence", { agent: "scout", task: "Find the exact contract" });
|
|
81
|
+
const receipt = await runs.steer("writer", "Also check: " + evidence.output, { mode: "follow_up" });
|
|
82
|
+
return { writer: await writer, receipt };
|
|
83
|
+
` });
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The receipt state is `queued`, `delivered`, `missed`, or `failed`. `delivered` means the child Pi session accepted the input. It does not mean the model followed it. `missed` means the keyed child became terminal or had no live route before delivery. This first slice uses the existing foreground and async steering transports but does not start steering recovery. Workflow traces include one steering attempt entry and one receipt entry.
|
|
87
|
+
|
|
88
|
+
Always await or return a `runs.steer` promise. The workflow waits for an observed steering side effect to settle before it exits and rejects fire-and-forget calls. Use ordinary `Promise.race` when the first child or steering receipt should advance the script. There is no callback API or child inbox access.
|
|
89
|
+
|
|
90
|
+
### Advanced rolling child runs
|
|
91
|
+
|
|
92
|
+
`runs.run` starts a keyed child when you call it. You do not need separate `runs.start`, `runs.next`, or `runs.collect` helpers for rolling councils or staged reviews. This is the advanced exception to ordinary `runs.all` fanout: keep launched promises only when the script later observes each one with direct `await`, `Promise.race`, or `Promise.all`. Use `Promise.race` to wait for the next completed child, steer a still-running sibling by its stable key, and use `Promise.all` to collect the remaining children.
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
subagent({ workflowScript: `
|
|
96
|
+
let pending = [
|
|
97
|
+
{ key: "analysis-a", promise: runs.run("analysis-a", { agent: "reviewer", task: "Analyze option A" }).then((result) => ({ key: "analysis-a", result })) },
|
|
98
|
+
{ key: "analysis-b", promise: runs.run("analysis-b", { agent: "reviewer", task: "Analyze option B" }).then((result) => ({ key: "analysis-b", result })) },
|
|
99
|
+
{ key: "critic", promise: runs.run("critic", { agent: "reviewer", task: "Find the strongest objection" }).then((result) => ({ key: "critic", result })) }
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
const first = await Promise.race(pending.map((child) => child.promise));
|
|
103
|
+
pending = pending.filter((child) => child.key !== first.key);
|
|
104
|
+
|
|
105
|
+
const target = pending.find((child) => child.key === "critic") ?? pending[0];
|
|
106
|
+
const receipt = await runs.steer(target.key, "Challenge this early result:\n" + first.result.output, { mode: "auto" });
|
|
107
|
+
const rest = await Promise.all(pending.map((child) => child.promise));
|
|
108
|
+
|
|
109
|
+
return { first: first.result.output, rest: rest.map((child) => child.result.output), receipt };
|
|
110
|
+
` });
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The workflow trace records the run completions and steering receipt. Scripts still never see raw async directories, inbox paths, or session files. If the keyed child is terminal, stale, or has no live route when `runs.steer` runs, the receipt reports `missed` or `failed` and the script can decide whether to continue.
|
|
114
|
+
|
|
115
|
+
Use named outputs when later workflow steps need structured data or durable references:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
subagent({ workflowScript: `
|
|
119
|
+
const inventory = await runs.run("inventory", {
|
|
120
|
+
agent: "scout",
|
|
121
|
+
task: "List the files that need review.",
|
|
122
|
+
outputSchema: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: { files: { type: "array", items: { type: "string" } } },
|
|
125
|
+
required: ["files"],
|
|
126
|
+
additionalProperties: false
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
return runs.run("review", {
|
|
130
|
+
agent: "reviewer",
|
|
131
|
+
task: "Review these files: " + inventory.structuredOutput.files.join(", ")
|
|
132
|
+
});
|
|
133
|
+
` });
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
For dynamic fanout, have one step return a structured list, check it in JavaScript, then map the bounded entries into `runs.all(...)`:
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
subagent({ workflowScript: `
|
|
140
|
+
const targets = await runs.run("targets", {
|
|
141
|
+
agent: "scout",
|
|
142
|
+
task: "Return up to five source files that need review.",
|
|
143
|
+
outputSchema: {
|
|
144
|
+
type: "object",
|
|
145
|
+
properties: { files: { type: "array", items: { type: "string" }, maxItems: 5 } },
|
|
146
|
+
required: ["files"],
|
|
147
|
+
additionalProperties: false
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
const files = targets.structuredOutput.files.slice(0, 5);
|
|
151
|
+
return runs.all(files.map((file, index) => ({
|
|
152
|
+
key: "review-" + index,
|
|
153
|
+
agent: "reviewer",
|
|
154
|
+
task: "Review " + file
|
|
155
|
+
})));
|
|
156
|
+
` });
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
For intermediate data that only later steps need, prefer the prior child's returned output or `structuredOutput` instead of writing shared files:
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
subagent({ workflowScript: `
|
|
163
|
+
const scan = await runs.run("scan", { agent: "scout", task: "Find the files that need fixes." });
|
|
164
|
+
return runs.run("fix", { agent: "worker", task: "Implement these findings:\n" + scan.output });
|
|
165
|
+
` });
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`{chain_dir}` remains available inside scripted workflow step templates for legacy-compatible path templates. It expands to the workflow cwd, not to private temporary storage.
|
|
169
|
+
|
|
170
|
+
### Migrating old chain shapes
|
|
171
|
+
|
|
172
|
+
Legacy top-level `chain`, `tasks`, `parallel`, `chainDir`, `/chain`, `/parallel`, `/run-chain`, and durable `.chain.md` execution are no longer the public workflow API. Rewrite them as JavaScript:
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
// Old shape, no longer supported:
|
|
176
|
+
// { chain: [{ agent: "scout", task: "Scan" }, { agent: "worker", task: "Fix from {previous}" }] }
|
|
177
|
+
|
|
178
|
+
// Current shape:
|
|
179
|
+
{ workflowScript: `
|
|
180
|
+
const scan = await runs.run("scan", { agent: "scout", task: "Scan" });
|
|
181
|
+
return runs.run("fix", { agent: "worker", task: "Fix from: " + scan.output });
|
|
182
|
+
` }
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
// Old shape, no longer supported:
|
|
187
|
+
// { tasks: [{ agent: "reviewer", task: "Review API" }, { agent: "reviewer", task: "Review UI" }] }
|
|
188
|
+
|
|
189
|
+
// Current shape:
|
|
190
|
+
{ workflowScript: `
|
|
191
|
+
return runs.all([
|
|
192
|
+
{ key: "api", agent: "reviewer", task: "Review API" },
|
|
193
|
+
{ key: "ui", agent: "reviewer", task: "Review UI" }
|
|
194
|
+
]);
|
|
195
|
+
` }
|
|
196
|
+
```
|
|
197
|
+
|
|
51
198
|
For long task text with Markdown fences or shell blocks, use quoted lines instead of a raw template literal:
|
|
52
199
|
|
|
53
200
|
````js
|
|
@@ -62,7 +209,26 @@ return runs.run("test", { agent: "worker", task });
|
|
|
62
209
|
|
|
63
210
|
A plain workflow creates one enclosing mission by default. Its children do not create separate missions. The result exposes the id as `details.missionId`, and human-readable output ends with `Mission: <id> (<status>)`. Pass `mission:false` for an ephemeral workflow with no mission or durable `state` global.
|
|
64
211
|
|
|
65
|
-
|
|
212
|
+
### Repeatable workflows
|
|
213
|
+
|
|
214
|
+
Use stable child keys and keep process logic in ordinary JavaScript. `runs.run` launches one child, `runs.all` launches independent children together, and later steps can use each completed child's `output`. Put long task text in arrays joined with `"\n"` so Markdown fences do not conflict with the script string.
|
|
215
|
+
|
|
216
|
+
For a process you run often, save the task as a prompt template under `.pi/prompts/` or `~/.pi/agent/prompts/` and launch it with `/prompt-workflow`. The adapter compiles prompt steps into `workflowScript`, so templates describe the work instead of embedding raw `subagent` tool-call JSON. You can ask the parent agent to create or update these prompt files from a process described in natural language.
|
|
217
|
+
|
|
218
|
+
```md
|
|
219
|
+
---
|
|
220
|
+
description: Review a release candidate
|
|
221
|
+
subagent: reviewer
|
|
222
|
+
fresh: true
|
|
223
|
+
---
|
|
224
|
+
Review $@. Return concrete findings with source proof, or state that no issue was found.
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
```text
|
|
228
|
+
/prompt-workflow review-release-candidate v0.51.0
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
For watched same-repo workflows, pass `async:false` only when the parent must block until completion. That blocking mode also shows the live in-chat workflow card. `chatProgress` can force `off` or `live-card` when the automatic policy is not what you want. Blocking workflows default to a 30-minute timeout; async workflows have no default timeout. See the [tool reference](tool-reference.md) for the full parameter list.
|
|
66
232
|
|
|
67
233
|
The legacy `/chain`, `/parallel`, and `/run-chain` commands are not registered.
|
|
68
234
|
|
|
@@ -90,12 +256,12 @@ Configure the worktree base directory and setup hook in [configuration.md](confi
|
|
|
90
256
|
|
|
91
257
|
## Supervisor coordination (child asks parent)
|
|
92
258
|
|
|
93
|
-
Child agents can talk back to the parent Pi session without installing `pi-intercom`. `pi-subagents` provides the child-facing `contact_supervisor` tool and the parent-facing `subagent_supervisor({ action: "reply" })` path natively.
|
|
259
|
+
Child agents can talk back to the parent Pi session without installing `pi-intercom`. `pi-subagents` provides the child-facing `contact_supervisor` tool and the parent-facing `subagent_supervisor({ action: "reply" })` path natively. Generic `intercom` remains available only when an explicitly loaded external provider supplies it.
|
|
94
260
|
|
|
95
261
|
Use it for work where the child might need a decision instead of guessing:
|
|
96
262
|
|
|
97
263
|
```text
|
|
98
|
-
Run this implementation in the background. If the worker gets blocked or needs a product decision, have it ask me through
|
|
264
|
+
Run this implementation in the background. If the worker gets blocked or needs a product decision, have it ask me through the supervisor channel.
|
|
99
265
|
```
|
|
100
266
|
|
|
101
267
|
```text
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-subagents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.51.0",
|
|
4
4
|
"description": "Pi extension for single-agent delegation and scripted multi-agent workflows",
|
|
5
5
|
"author": "Nico Bailon",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./index.ts",
|
|
10
10
|
"./background-work": "./src/api/background-work.ts",
|
|
11
|
+
"./external-job-provider": "./src/api/external-job-provider.ts",
|
|
11
12
|
"./external-runs": "./src/api/external-runs.ts",
|
|
12
13
|
"./delegation": "./src/api/delegation.ts",
|
|
13
14
|
"./capability-ceiling": "./src/api/capability-ceiling.ts",
|
|
@@ -52,7 +53,7 @@
|
|
|
52
53
|
"scripts": {
|
|
53
54
|
"typecheck": "tsc --noEmit",
|
|
54
55
|
"test": "npm run test:unit",
|
|
55
|
-
"test:unit": "node --experimental-strip-types --test test/unit/*.test.ts",
|
|
56
|
+
"test:unit": "node --experimental-strip-types --import ./test/support/isolated-temp-root.mjs --test test/unit/*.test.ts",
|
|
56
57
|
"test:integration": "node --experimental-strip-types --import ./test/support/register-loader.mjs --test test/integration/*.test.ts",
|
|
57
58
|
"test:e2e": "node --experimental-strip-types --import ./test/support/register-loader.mjs --test test/e2e/*.test.ts",
|
|
58
59
|
"test:all": "npm run test:unit && npm run test:integration && npm run test:e2e"
|
|
@@ -89,6 +90,7 @@
|
|
|
89
90
|
}
|
|
90
91
|
},
|
|
91
92
|
"dependencies": {
|
|
93
|
+
"acorn": "8.18.0",
|
|
92
94
|
"jiti": "2.7.0",
|
|
93
95
|
"typebox": "1.1.38",
|
|
94
96
|
"yaml": "2.8.3"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: pi-subagents
|
|
3
3
|
description: |
|
|
4
4
|
Delegate work to builtin or custom subagents with single-agent, parallel,
|
|
5
|
-
scripted
|
|
5
|
+
scripted-chaining, async, forked-context, and coordinated workflows. Use
|
|
6
6
|
for advisory review, implementation handoffs, and multi-step tasks where a
|
|
7
7
|
single agent should stay in control while other agents contribute context,
|
|
8
8
|
planning, or execution.
|
|
@@ -12,7 +12,7 @@ description: |
|
|
|
12
12
|
|
|
13
13
|
This skill is for the main parent orchestrator only. Do not inject or follow it inside spawned child subagents. The parent session owns delegation, orchestration, review fanout, and final fix-worker launches. Ordinary children should not run their own subagent workflows; the explicit exception is a delegated fanout child whose resolved builtin `tools` includes `subagent`, and that child may use `subagent` only for the fanout work the parent assigned.
|
|
14
14
|
|
|
15
|
-
Use this skill when the parent orchestrator needs one specialized child or composed orchestration. Use `workflowScript` for all execution, including one isolated child.
|
|
15
|
+
Use this skill when the parent orchestrator needs one specialized child or composed orchestration. Use `workflowScript` for all execution, including one isolated child. Chaining is still supported, but it is code-driven: use `await runs.run(...)` for sequential steps, `runs.all([...])` for parallel fanout, and ordinary JavaScript for branching, retries, gate monitors, and aggregation. Keep workflow helpers portable: use plain helper functions or explicit Promise chains, not nested `async function` helpers, async arrows, or async methods. Do not use legacy top-level `chain` / `tasks` inputs or durable `.chain.md` execution. Scripted workflows normally start asynchronously unless config sets `asyncByDefault:false`; set `async:true` explicitly when async behavior matters. Pass `async:false` only when the parent must block until completion. Async mode still shows progress. Do not use `async:false` for final reviews, backlog gates, run-to-completion convenience, or because no other work is available.
|
|
16
16
|
|
|
17
17
|
## How to use this router
|
|
18
18
|
|
|
@@ -22,7 +22,7 @@ Read the matching reference file before acting. Paths are relative to this `SKIL
|
|
|
22
22
|
| --- | --- |
|
|
23
23
|
| Decide whether to delegate, choose agents, compare tool versus slash commands, apply prompt techniques, or understand builtin roles | `references/prompting-and-roles.md` |
|
|
24
24
|
| Run one-child, scripted, async, scheduled, mission-backed, forked, watchdog, oracle, or intercom-coordinated workflows | `references/execution-controls.md` |
|
|
25
|
-
| List/create/update/delete/eject/disable agents
|
|
25
|
+
| List/create/update/delete/eject/disable agents, inspect legacy chain records, edit agent files, use prompt-template integration, or expose extension RPC | `references/management-authoring-rpc.md` |
|
|
26
26
|
| Check safety constraints, best practices, standard workflows, or error handling | `references/constraints-and-recipes.md` |
|
|
27
27
|
|
|
28
28
|
For broad or uncertain requests, read more than one reference. For complex work, start with `references/prompting-and-roles.md` and `references/execution-controls.md`, then consult `references/constraints-and-recipes.md` before launching or reviewing child work.
|
|
@@ -34,7 +34,8 @@ For broad or uncertain requests, read more than one reference. For complex work,
|
|
|
34
34
|
- For cross-codebase work, record the target repo, explicit `cwd`, authority boundary, and expected output before launch. Do not assume the parent session cwd is the child repo.
|
|
35
35
|
- For parallel fanout, compare child prompts before launch. Do not send clone prompts with only issue numbers, titles, or broad file globs swapped; each child needs a lane-specific task, source seam, prior evidence, and decision that remains distinct without the item number. Launch that fanout as one async `workflowScript` with stable keys and aggregate output unless there is truly only one child.
|
|
36
36
|
- Prefer fresh-context review/validation fanout, then synthesize and apply fixes in the parent.
|
|
37
|
-
- Use async/background by default when
|
|
37
|
+
- Use async/background by default. Final reviews, gate checks, oracle checks, and backlog lanes stay async. Use `async:false` only when the parent must block until completion. Do not poll just to wait. For adaptive gates, branch in `workflowScript`.
|
|
38
|
+
- For Pi extension repos whose canonical checkout is under `~/.pi/agent/extensions`, never create lane worktrees as sibling directories there. Pi auto-loads `~/.pi/agent/extensions/*/index.ts`, so sibling worktrees can register duplicate tools. Put lanes under `~/.pi/agent/worktrees`, another worktree base outside auto-discovery, or a temporary clone. If a lane must run the modified extension itself, use an isolated Pi config home with `PI_CODING_AGENT_DIR=<lane-config> pi --no-extensions -e <lane>/index.ts`. Use full containers only when path and config isolation are insufficient.
|
|
38
39
|
- Preserve capability ceilings, including child tool restrictions and session-scoped allowed-agent restrictions.
|
|
39
40
|
- Escalate unresolved product, architecture, authority, release, merge, or safety decisions upward instead of letting a child decide silently.
|
|
40
41
|
- Treat receipts, CI, review bots, and external-run records as evidence, not authority to merge, close, comment, publish, or release.
|
|
@@ -4,10 +4,12 @@ This file is a detailed reference loaded from `skills/pi-subagents/SKILL.md`.
|
|
|
4
4
|
|
|
5
5
|
## Important Constraints
|
|
6
6
|
|
|
7
|
-
- **
|
|
8
|
-
have a persisted session file
|
|
9
|
-
|
|
10
|
-
when
|
|
7
|
+
- **Explicit forking requires a persisted parent session.** If the current session
|
|
8
|
+
does not have a persisted session file or current leaf, explicit `context: "fork"`
|
|
9
|
+
fails. An agent-level `defaultContext: fork` is a preference: packaged `worker`,
|
|
10
|
+
`oracle`, and `advisor` fall back to `fresh` when those fork preconditions are not
|
|
11
|
+
met yet. Use `context: "fresh"` when you do not want a fork even after the parent
|
|
12
|
+
session exists.
|
|
11
13
|
- **Forked runs inherit parent history.** They are branched threads, not fresh
|
|
12
14
|
filtered contexts. Use fresh context for adversarial reviewers unless the user explicitly asks for forked context.
|
|
13
15
|
- **Default subagent nesting depth is 2.** Deeper recursive delegation is blocked
|
|
@@ -61,7 +63,7 @@ Give subagents specific tasks rather than vague mandates.
|
|
|
61
63
|
|
|
62
64
|
### Escalate decisions upward
|
|
63
65
|
|
|
64
|
-
If a subagent encounters an unapproved product, architecture, scope, merge, release, credential, or authority choice, it should use `contact_supervisor` and wait for the reply instead of deciding alone. Generic `intercom` is
|
|
66
|
+
If a subagent encounters an unapproved product, architecture, scope, merge, release, credential, or authority choice, it should use `contact_supervisor` and wait for the reply instead of deciding alone. Generic `intercom` is external or provider-supplied only. Use it only when external bridge instructions provide an explicit safe target. External checks, receipts, and review bots provide evidence only; they do not grant authority.
|
|
65
67
|
|
|
66
68
|
### Intervene only on clear control signals
|
|
67
69
|
|
|
@@ -211,7 +213,8 @@ Use distinct keys, prompts, and output paths. Do not launch parallel writers int
|
|
|
211
213
|
**"Unknown agent"**
|
|
212
214
|
```typescript
|
|
213
215
|
subagent({ action: "list" })
|
|
214
|
-
// Check available agents
|
|
216
|
+
// Check available agents, then confirm scope/precedence. Saved chains are not a
|
|
217
|
+
// public execution surface; author orchestration with workflowScript.
|
|
215
218
|
```
|
|
216
219
|
|
|
217
220
|
**Setup, discovery, or intercom confusion**
|
|
@@ -26,6 +26,14 @@ An agent may set `runner.type: external-cli` with a non-empty `command`, optiona
|
|
|
26
26
|
|
|
27
27
|
External CLI profiles are async-only and one-shot. They support lifecycle artifacts, stdout/stderr logs, timeout, and stop. Full stdout and stderr are retained in their log files, while the final stdout response and stderr error kept in memory are each limited to their last 64 KiB. They do not support foreground/clarify, steer/resume/interrupt-as-pause, Pi models/tools/extensions/skills, tool or turn budgets, structured output, nested subagents, fallbacks, or sessions.
|
|
28
28
|
|
|
29
|
+
### External job profiles
|
|
30
|
+
|
|
31
|
+
An agent may set `runner.type: external-job` with a non-empty `provider` and optional JSON `options`. The bundled `gpt-pro` agent uses provider `surf-oracle`. The provider must be registered in the host Pi process through `pi-subagents/external-job-provider`; the async runner talks to that parent-owned registry through a local operation bridge.
|
|
32
|
+
|
|
33
|
+
External job profiles are async-only. The provider owns the remote job and Pi owns the async run record. Status persists provider name, provider job id, prompt digest, provider options, handle/conversation URLs when supplied, result artifact path, last known state, and provider failure code/message. Recovery uses existing provider job metadata to call `reattach` and `result`; it refuses to redispatch a prompt when the persisted provider job does not match the prompt digest.
|
|
34
|
+
|
|
35
|
+
External job profiles do not support foreground/clarify, steer/resume, Pi models/tools/extensions/skills, tool or turn budgets, structured output, native child permissions, fallbacks, or Pi child sessions. Capacity conflicts fail closed and include the blocking provider job id when the provider supplies it.
|
|
36
|
+
|
|
29
37
|
### Single agent
|
|
30
38
|
|
|
31
39
|
```typescript
|
|
@@ -53,7 +61,7 @@ its resolved launch context as `[fresh]` or `[fork]`. Aggregate headers show
|
|
|
53
61
|
|
|
54
62
|
### Scripted workflows
|
|
55
63
|
|
|
56
|
-
`workflowScript` is the sole public execution surface. Use `runs.run(key, { agent, task, ... })` for one child, `runs.all([...])` for parallel children, and ordinary JavaScript for sequence, branching, filtering, retries, and aggregation. Scripts are ordinary JavaScript statement bodies, so use an explicit return such as `workflowScript: "return runs.run('main', { agent: 'worker', task: '...' })"` for a useful one-child result. Prefer a single scripted workflow whenever the parent is starting a coordinated wave, such as multiple reviews, review plus gate monitor, worker then monitor setup, cross-repo prep lanes, or a fanout that the parent will consume together.
|
|
64
|
+
`workflowScript` is the sole public execution surface. Use `runs.run(key, { agent, task, ... })` for one child, `runs.all([...])` for parallel children, and ordinary JavaScript for sequence, branching, filtering, retries, and aggregation. Scripts are ordinary JavaScript statement bodies, so use an explicit return such as `workflowScript: "return runs.run('main', { agent: 'worker', task: '...' })"` for a useful one-child result. Use top-level `await`, plain helper functions, or explicit Promise chains; nested `async function` helpers, async arrows, and async methods are rejected. Prefer a single scripted workflow whenever the parent is starting a coordinated wave, such as multiple reviews, review plus gate monitor, worker then monitor setup, cross-repo prep lanes, or a fanout that the parent will consume together.
|
|
57
65
|
|
|
58
66
|
```js
|
|
59
67
|
subagent({
|
|
@@ -68,21 +76,23 @@ subagent({
|
|
|
68
76
|
})
|
|
69
77
|
```
|
|
70
78
|
|
|
71
|
-
Scripts run in a timed worker with only `runs.run`, `runs.all`, `runs.status`, `runs.ref/refs`, `
|
|
79
|
+
Scripts run in a timed worker with only `runs.run`, `runs.all`, `runs.status`, `runs.ref/refs`, `emit`, captured `console`, and standard JavaScript. Pass explicit task text to `runs.run`. Mission-attached workflows also get `await state.get(key)` and `await state.set(key, value)` for durable JSON state shared across workflows on the same mission; `mission: false` workflows have no `state` global. Stable keys are required. Child launches follow ordinary single-agent execution controls. Give each child a distinct decision and output path when reports must outlive the workflow, then consume the aggregate workflow result before opening individual reports.
|
|
72
80
|
|
|
73
81
|
For one host-run verification command, pass `gate: "npm test"` on a `runs.run`/`runs.all` item (or at the top level as a workflow default). It is shorthand for verified acceptance with that single command: the runtime executes it on the host, records the result as evidence, and memoizes it per tracked workspace state and effective environment. `gate` cannot be combined with `acceptance`; use explicit `acceptance.verify` for multiple commands or custom criteria.
|
|
74
82
|
|
|
75
|
-
Completed workflow children from this parent session stay addressable as retained children. `subagent({ action: "children.list" })` lists up to the last 10 with run ids
|
|
83
|
+
Completed workflow children from this parent session stay addressable as retained children. `subagent({ action: "children.list" })` lists up to the last 10 with run ids and reports each row as `resumable` or `not resumable` with a reason. Resume only rows reported `resumable`. For a retained-child challenge, use `resume` instead of `steer` when the child is complete. If no retained child is resumable, launch a same-role fallback challenge and label it as fallback. A later workflow continues a resumable child with `runs.run(key, { resume: "<run-id>", task: "follow-up" })`. Inside `workflowScript`, awaiting that call waits for the revived child to finish and returns its completed output and new `runId`; top-level `{ action: "resume" }` remains detached. Pass explicit follow-up task text. Assign each returned child result back to the loop variable because every resume can return a new retained `runId`; always resume the latest returned id. `resume` and `agent` are mutually exclusive, the revived child keeps its stored agent/model/tool contract, and `gate` is rejected on retained resume items.
|
|
76
84
|
|
|
77
85
|
### Async/background
|
|
78
86
|
|
|
79
|
-
Prefer async mode for every subagent launch. Set `async: true` no matter the task unless
|
|
87
|
+
Prefer async mode for every subagent launch. Set `async: true` no matter the task unless the parent must block until completion. This applies to scouts, researchers, workers, reviewers, validators, oracle checks, one-off delegates, final review gates, backlog gates, and scripted workflows. Keep the write path single-threaded even when the run is async.
|
|
88
|
+
|
|
89
|
+
Use `async:false` only when the parent must block until completion. Async mode still shows progress. Do not use `async:false` because a task is short, because it is the last gate, because no other work is ready, because the user asked to finish the overall job, or because blocking is convenient.
|
|
80
90
|
|
|
81
91
|
Async does not mean parallel writes. Do not edit the same active worktree while an async worker is changing it. Parent-side overlap should be reading, validation prep, synthesis, command planning, or review of unaffected context unless the writer is isolated in a separate worktree.
|
|
82
92
|
|
|
83
|
-
Do not end your turn immediately after launching an async child if you promised to keep working. Continue the local inspection, synthesis, or validation prep, then check the async run when its result is needed.
|
|
93
|
+
Do not end your turn immediately after launching an async child if you promised to keep working. Continue the local inspection, synthesis, or validation prep, then check the async run when its result is needed. If no safe independent work remains, return control and let Pi wake the session; do not convert the child to foreground.
|
|
84
94
|
|
|
85
|
-
In an interactive chat, normally return control when ready to yield and let Pi wake the session on completion; do not call `subagent_wait()` merely to wait.
|
|
95
|
+
In an interactive chat, normally return control when ready to yield and let Pi wake the session on completion; do not call `subagent_wait()` merely to wait. A run-to-completion user request is not by itself a reason to use foreground children. Override the normal yield-and-wake flow only when this exact turn cannot safely end without the result, such as a headless provider flow or a skill contract that must produce a same-turn artifact. Use `subagent_wait()`, not `async:false`, for that current-turn dependency. Never substitute sleep or status-polling loops.
|
|
86
96
|
|
|
87
97
|
`subagent_wait()` returns when the next initially active async run or registered provider item finishes or a subagent needs attention. Use `subagent_wait({ all: true })` for all work active at call time, `subagent_wait({ id: "..." })` for one async or remembered detached foreground run, and `subagent_wait({ timeoutMs })` to cap the block. In a long-lived interactive parent session, use `subagent_wait({ id: "...", nonBlocking: true })` to resolve the prefix to one exact run, persist an armed subscription, return immediately, and wake later on completion, failure, attention, reconciliation failure, or timeout. Ordinary status lists armed subscriptions separately from active children. This differs from disabling `waitTool`, which returns immediately without arming a future wake. If a foreground child detaches for supervisor coordination, reply first, then wait on its id; do not resume or launch a replacement while it remains detached. Headless sessions also auto-drain exact current-session work at `agent_end` as a final safeguard.
|
|
88
98
|
|
|
@@ -110,17 +120,10 @@ While children run, the persistent FleetView and the collapsed foreground tool-r
|
|
|
110
120
|
|
|
111
121
|
Inspect async runs with `subagent({ action: "status", id: "..." })` or `subagent({ action: "status" })` for active runs. Use `subagent({ action: "status", view: "fleet" })` when supervising several active foreground/background runs and `subagent({ action: "status", id: "...", view: "transcript", index: 0 })` when you need the latest child output without digging through artifacts. If a delegated fanout child launches nested runs, the parent status view shows them as a tree and you can target a nested run directly with its nested id.
|
|
112
122
|
|
|
113
|
-
Stop a current-session top-level async run with `stop` (or `/subagents-stop`). Stopped runs finish as `stopped`/cancelled and are not resumable. For an active foreground single-subagent run, `/subagents-detach [run-id]` leaves the child running without terminating it and returns the eventual result through status/wait.
|
|
123
|
+
Stop a current-session top-level async run with `stop` (or `/subagents-stop`). Stopped runs finish as `stopped`/cancelled and are not resumable. For an active foreground single-subagent run, `/subagents-detach [run-id]` leaves the child running without terminating it and returns the eventual result through status/wait.
|
|
114
124
|
|
|
115
125
|
```typescript
|
|
116
126
|
subagent({ action: "stop", id: "run-id" })
|
|
117
|
-
subagent({
|
|
118
|
-
action: "append-step",
|
|
119
|
-
id: "run-id",
|
|
120
|
-
step: { checkpoint: "review", message: "Approve the next implementation step?" }
|
|
121
|
-
})
|
|
122
|
-
subagent({ action: "approve-checkpoint", id: "run-id" })
|
|
123
|
-
subagent({ action: "reject-checkpoint", id: "run-id" })
|
|
124
127
|
```
|
|
125
128
|
|
|
126
129
|
Use `steer` for top-level live async guidance and `resume` after a delegated run pauses or finishes. Routed nested runs retain their existing non-destructive live follow-up path:
|
|
@@ -183,7 +186,7 @@ Humans can use `/subagents-doctor` for the same read-only report. It checks runt
|
|
|
183
186
|
|
|
184
187
|
### Subagent control
|
|
185
188
|
|
|
186
|
-
Subagent control is the runtime visibility and intervention layer for delegated runs. It is separate from lifecycle status. Lifecycle status says whether a child is `queued`, `running`, `paused`, `complete`, `stopped`, `failed`, or `rejected`. Activity reporting is factual: it tracks the last observed activity time and the current tool when known. It does not pretend to know that a child is truly stuck. Manual top-level async cancellation uses `stop` / `/subagents-stop
|
|
189
|
+
Subagent control is the runtime visibility and intervention layer for delegated runs. It is separate from lifecycle status. Lifecycle status says whether a child is `queued`, `running`, `paused`, `complete`, `stopped`, `failed`, or `rejected`. Activity reporting is factual: it tracks the last observed activity time and the current tool when known. It does not pretend to know that a child is truly stuck. Manual top-level async cancellation uses `stop` / `/subagents-stop`.
|
|
187
190
|
|
|
188
191
|
Default behavior is intentionally conservative. When no activity has been observed past the configured threshold, the run emits a `needs_attention` control event. Foreground runs can push this as a `subagent:control-event` event, and async runs persist it to `events.jsonl` so the parent tracker can surface it without constant manual polling. Notification-worthy control events are also inserted into the visible transcript so both the user and the parent agent can see them, with a proactive hint plus concrete `nudge`, `status`, and `interrupt` options. Visible notifications fire once per child run and attention state.
|
|
189
192
|
|
|
@@ -287,7 +290,7 @@ Use `mission.update` while work runs to record decisions, artifacts, labels, sum
|
|
|
287
290
|
- **Use `missionId` for follow-up work.** Attach later work to an existing objective with `missionId`; attachment re-marks the mission active. `missionId` and `mission` are mutually exclusive. Explicit attachment fails before launch if the mission is missing, while automatic missions degrade to `details.missionWarning` without blocking the run.
|
|
288
291
|
- **Keep `state` small.** Mission `state` is JSON coordination across workflows on the same mission. Keys use the same format as run keys, values must be JSON, and the whole state file is capped at 256 KiB. Each `set` merges one key under a file lock. Put large content in artifact files and store paths in state. In goal missions, write `state.set("nextReadyAction", "...")` so the next idle-turn notice names the exact ready step.
|
|
289
292
|
- **Use artifacts and receipts as evidence.** Mission-backed launches already record run artifacts such as async `status.json`, `events.jsonl`, child output paths, and handoff manifests. Add `mission.update` artifacts only for extra durable outputs such as `patch`, `review`, or `note` files. Add receipts for external outcomes: `pull_request`, `ci`, `deployment`, or `release`; each receipt needs an absolute URL. Receipts are evidence, not authority to merge, deploy, or release.
|
|
290
|
-
- **
|
|
293
|
+
- **Resolve decisions explicitly.** `mission.update` `decisions` can only add open decisions; `mission.update` itself cannot resolve one — use the `mission.resolve-decision` action (decision `id` plus a non-empty `summary`) to settle and close it. In a goal mission, an unresolved decision becomes the fallback next ready action in each notice. Use decisions sparingly there; record them for escalation and audit, steer goal continuation through `state.nextReadyAction`, and close the mission when the question is settled.
|
|
291
294
|
- **Close missions when done.** `mission.close` takes `missionStatus` `completed`, `failed`, or `cancelled` plus a concise `summary`, and ends any goal loop. Goal notices go only to the owning session and stop silently at `budget-exhausted` without closing or claiming success, so close explicitly. Terminal missions are pruned beyond configured retention, so store durable outputs as artifacts, receipts, and summary before closing.
|
|
292
295
|
|
|
293
296
|
After compaction, restart, or confusing history, recover from durable state first: `mission.list` in the project, `mission.list` with `missionScope: "global"` for the user-local cross-project pointer index, then `mission.show` for the relevant mission. `mission.show` refreshes linked async status when available and returns warnings instead of hiding the mission if a linked status file is temporarily unreadable. Use the linked run ids with normal `status`, `steer`, `resume`, or `stop` actions. Project mission JSON remains authoritative over chat history.
|
|
@@ -305,6 +308,7 @@ subagent({ action: "mission.create", mission: { title: "Ship auth refresh", obje
|
|
|
305
308
|
subagent({ workflowScript: `return runs.run("main", { agent: "worker", task: "Implement the approved plan" })`, missionId: "<mission-id>" })
|
|
306
309
|
subagent({ workflowScript: `return runs.run("main", { agent: "scout", task: "Quickly answer whether this file exists" })`, mission: false })
|
|
307
310
|
subagent({ action: "mission.list", missionScope: "global" })
|
|
311
|
+
subagent({ action: "mission.resolve-decision", missionId: "<mission-id>", id: "<decision-id>", summary: "Settled: ship the v2 API; no schema freeze needed." })
|
|
308
312
|
subagent({ action: "project.open", cwd: "/path/to/other-repo", message: "Own this mission for the project and report back with receipts." })
|
|
309
313
|
subagent({ action: "project.status", cwd: "/path/to/other-repo" })
|
|
310
314
|
subagent({ action: "project.close", cwd: "/path/to/other-repo" })
|
|
@@ -381,7 +385,7 @@ Use `oracle` as a smart-friend escalation when the parent needs help with trajec
|
|
|
381
385
|
|
|
382
386
|
This is separate from optional external completion delivery. Set `intercomBridge.resultDelivery: true` only when an external listener consumes and acknowledges `subagent:result-intercom` grouped results. It does not deliver results by itself, and it does not change native supervisor asks or progress updates.
|
|
383
387
|
|
|
384
|
-
|
|
388
|
+
Generic `intercom` is external or provider-supplied only. Native supervisor coordination injects `contact_supervisor`, not generic `intercom`. Use generic `intercom` only when external bridge instructions provide an explicit safe target. Do not invent a target. Prefer the tool from the injected bridge instructions.
|
|
385
389
|
|
|
386
390
|
Use `contact_supervisor` with `reason: "need_decision"` when:
|
|
387
391
|
- a subagent is blocked on a decision
|
|
@@ -423,6 +427,6 @@ Or inspects unresolved asks first:
|
|
|
423
427
|
subagent_supervisor({ action: "pending" })
|
|
424
428
|
```
|
|
425
429
|
|
|
426
|
-
|
|
430
|
+
Native supervisor coordination does not expose generic `intercom` as a fallback. Use `subagent_supervisor` for parent replies.
|
|
427
431
|
|
|
428
432
|
If intercom messages do not show up, run `subagent({ action: "doctor" })` or `/subagents-doctor`.
|
|
@@ -6,7 +6,7 @@ This file is a detailed reference loaded from `skills/pi-subagents/SKILL.md`.
|
|
|
6
6
|
|
|
7
7
|
The `subagent(...)` tool also supports management actions.
|
|
8
8
|
|
|
9
|
-
### List available agents and
|
|
9
|
+
### List available agents and legacy chain records
|
|
10
10
|
|
|
11
11
|
```typescript
|
|
12
12
|
subagent({ action: "list" })
|
|
@@ -18,7 +18,7 @@ subagent({ action: "list" })
|
|
|
18
18
|
subagent({ action: "children.list" })
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Lists up to the last 10
|
|
21
|
+
Lists up to the last 10 retained workflow children from this parent session with explicit `resumable` or `not resumable` rows. Resume only rows reported `resumable`. Send a simple follow-up or implementation challenge with `subagent({ action: "resume", id: "<run-id>", message: "..." })`. Continue one inside a workflow with `runs.run(key, { resume: "<run-id>", task: "follow-up" })`; the revived child keeps its stored agent, model, and tool contract. If no resumable child is listed, start a same-role fallback challenge and label it as fallback. `steer` with `mode: "follow_up"` only queues text for the next `resume` when the child has already completed.
|
|
22
22
|
|
|
23
23
|
### Refinement overlays
|
|
24
24
|
|
|
@@ -85,7 +85,7 @@ subagent({ action: "reset", agent: "reviewer" })
|
|
|
85
85
|
Use management actions when the system needs to create or edit subagents on
|
|
86
86
|
demand without dropping into raw file editing.
|
|
87
87
|
|
|
88
|
-
Management actions create or update user/project agent files. `config.name` is the local frontmatter name; optional `config.package` registers and looks up the runtime name as `{package}.{name}`. Use the dotted runtime name for `get`, `update`, `delete`, slash commands, and
|
|
88
|
+
Management actions create or update user/project agent files. `config.name` is the local frontmatter name; optional `config.package` registers and looks up the runtime name as `{package}.{name}`. Use the dotted runtime name for `get`, `update`, `delete`, slash commands, and scripted workflow steps. For small builtin changes such as a model swap, prefer `subagents.agentOverrides` in settings. Durable `.chain.md` definitions are legacy records, not a current authoring target; use `workflowScript` or `/prompt-workflow` for repeatable orchestration.
|
|
89
89
|
|
|
90
90
|
## Creating and Editing Agents by File
|
|
91
91
|
|
|
@@ -129,7 +129,7 @@ That is only a starting point. Omit `package` for the traditional unqualified ru
|
|
|
129
129
|
|
|
130
130
|
`aliases` is an optional comma-separated or block-list set of alternate names for selecting an agent. Aliases resolve to the canonical `name` for execution, status, persistence, and config. Exact canonical names take precedence over aliases, and alias collisions between distinct canonical agents fail as ambiguous. Management create/update accepts a comma-separated string, string array, or `false`/empty string to clear aliases.
|
|
131
131
|
|
|
132
|
-
`acceptance` is a single-agent launch default. Use a scalar level such as `checked` or an inline/block YAML map such as `{ level: "none", reason: "lightweight lookup" }`. An explicit tool-call value wins;
|
|
132
|
+
`acceptance` is a single-agent launch default. Use a scalar level such as `checked` or an inline/block YAML map such as `{ level: "none", reason: "lightweight lookup" }`. An explicit tool-call value wins; scripted workflow child acceptance remains configured on the `runs.run` or `runs.all` item. Management create/update accepts the same policy object, and `acceptance: ""` clears the frontmatter default (`false` remains the deprecated disabled-policy shorthand).
|
|
133
133
|
|
|
134
134
|
`acceptanceRole` is `read-only` or `writer` and controls automatic acceptance inference only. Explicit task mutation or no-edit intent wins; otherwise the role replaces agent-name guessing. Omission preserves the current name heuristics. The field does not grant or revoke tools. Management accepts `false` or an empty string to clear it.
|
|
135
135
|
|
|
@@ -158,4 +158,4 @@ Additional user prompt templates can delegate into `pi-subagents` through the na
|
|
|
158
158
|
|
|
159
159
|
Other Pi extensions can call `pi-subagents` through the in-process event bus. The RPC channels are `subagents:rpc:v1:ready`, `subagents:rpc:v1:request`, and per-request replies at `subagents:rpc:v1:reply:<requestId>`. Envelopes use `{ version: 1, requestId, method, params }`, and replies use `{ version: 1, requestId, success, data | error }`. `ping` advertises the exact process-local async completion event as `events.asyncComplete` for RPC-spawn consumers.
|
|
160
160
|
|
|
161
|
-
Methods: `ping`, `status`, `spawn`, `steer`, `interrupt`, `resume`, and `stop`. `ping` capability metadata advertises optional projections: `capabilities.fleetStatus: { version: 1 }` adds bounded current-session `data.fleet` records (opaque reconciliation `key`, resolved `agent`, optional `role`, `model`, `effort`, caller-facing `goal`, `startedAt`, split `{ input, output, total }` tokens, plus `totalActive`/`omitted` overflow counts) to successful `status` replies; `capabilities.launchResolvedExtensions` advertises parent-resolved opaque launch-extension identifiers in status details; `capabilities.runtimeAcknowledgedExtensions` advertises the best-effort child-runtime acknowledgement projection fed by cooperating extensions emitting `subagent:acknowledge-extension`. Foreground `details.results[]` rows carry a stable numeric `index`; correlate children by `(runId, index)` rather than row position. Consumers should read status/result artifacts and RPC projections instead of scraping terminal output and must ignore unknown fields. `spawn` requires `workflowScript`, is async-only, and rejects management actions, `async: false`, or `clarify: true`; it reuses the normal executor, so discovery, validation, session attribution, configured spawn caps, child-safety depth, artifacts, and async status are shared with the `subagent` tool. `status`, acknowledged async `steer`, and `interrupt` map to the normal control actions. RPC steer disables pause-and-revive recovery and advertises `capabilities.nonRecoveringSteer`, preserving the caller's authority over the exact spawned child. `resume` requires a target plus non-empty message and delegates to the package-owned revival path; it may set a caller-owned `file-only` output path but cannot override the persisted child model, tools, budgets, session ownership, or exclusive session lease. `stop` targets running async runs through the existing timeout control channel. `pi.events` is process-local, so separate Pi processes and child subagents need lifecycle artifact files or `pi-intercom` instead.
|
|
161
|
+
Methods: `ping`, `status`, `spawn`, `steer`, `interrupt`, `resume`, and `stop`. `ping` capability metadata advertises optional projections: `capabilities.fleetStatus: { version: 1 }` adds bounded current-session `data.fleet` records (opaque reconciliation `key`, resolved `agent`, optional `role`, `model`, `effort`, caller-facing `goal`, `startedAt`, split `{ input, output, total }` tokens, plus `totalActive`/`omitted` overflow counts) to successful `status` replies; `capabilities.launchResolvedExtensions` advertises parent-resolved opaque launch-extension identifiers in status details; `capabilities.runtimeAcknowledgedExtensions` advertises the best-effort child-runtime acknowledgement projection fed by cooperating extensions emitting `subagent:acknowledge-extension`. Foreground `details.results[]` rows carry a stable numeric `index`; correlate children by `(runId, index)` rather than row position. Consumers should read status/result artifacts and RPC projections instead of scraping terminal output and must ignore unknown fields. `spawn` requires `workflowScript`, is async-only, and rejects management actions, `async: false`, or `clarify: true`; it reuses the normal executor, so discovery, validation, session attribution, configured spawn caps, child-safety depth, artifacts, and async status are shared with the `subagent` tool. `status`, acknowledged async `steer`, and `interrupt` map to the normal control actions. RPC steer disables pause-and-revive recovery and advertises `capabilities.nonRecoveringSteer`, preserving the caller's authority over the exact spawned child. `resume` requires a target plus non-empty message and delegates to the package-owned revival path; it may set a caller-owned `file-only` output path but cannot override the persisted child model, tools, budgets, session ownership, or exclusive session lease. For retained-child workflows, list children first and resume only rows reported `resumable`; otherwise start a same-role fallback challenge and label it as fallback. `stop` targets running async runs through the existing timeout control channel. `pi.events` is process-local, so separate Pi processes and child subagents need lifecycle artifact files or `pi-intercom` instead.
|
|
@@ -16,7 +16,7 @@ Parent extensions may register a session-scoped, out-of-band ceiling through `pi
|
|
|
16
16
|
- **Regular skill specialists**: when discovery shows proactive skill subagent suggestions and the current work is broad enough, launch a small fresh-context fanout that asks one subagent per relevant regularly used skill to apply that skill's perspective to the task
|
|
17
17
|
- **Long-running work**: launch async/background runs and inspect them later. For mutation-capable work, bound the delivery slice and elapsed runtime, then request checkpoints after active tool work returns. Reserve hard turn and tool-call caps for explicitly read-only children.
|
|
18
18
|
- **Subagent control**: watch needs-attention signals and soft-interrupt only when a delegated run is genuinely blocked
|
|
19
|
-
- **Agent authoring**: create, update, or override agents
|
|
19
|
+
- **Agent authoring**: create, update, or override project agents. Treat saved chain records as legacy inspection or migration inputs, not as a current authoring target.
|
|
20
20
|
|
|
21
21
|
## Tool vs Slash Commands
|
|
22
22
|
|
|
@@ -101,13 +101,13 @@ Use this after implementation when the user wants cleanup review or when a final
|
|
|
101
101
|
|
|
102
102
|
### Staged fix orchestration technique
|
|
103
103
|
|
|
104
|
-
Use this when a broad diff has known reviewer findings across several items and the user wants the parent to “orchestrate subagents like a boss.” Keep the active worktree safe with a three-stage
|
|
104
|
+
Use this when a broad diff has known reviewer findings across several items and the user wants the parent to “orchestrate subagents like a boss.” Keep the active worktree safe with a three-stage `workflowScript`:
|
|
105
105
|
|
|
106
106
|
1. A parallel read-only planning fanout, one reviewer per issue cluster. Each child inspects the real diff and returns exact files, line refs, proposed fixes, and focused validation. They must not edit.
|
|
107
|
-
2. One writer worker. It receives the reviewer summaries
|
|
107
|
+
2. One writer worker. It receives the reviewer summaries as the awaited planning results (or their durable output paths) interpolated into its task, plus the parent’s accepted scope, stop rules, and verification contract. It is the only child allowed to edit the active worktree.
|
|
108
108
|
3. A parallel read-only validation fanout. Validators inspect the worker diff from fresh context with distinct angles, report pass/fail, remaining blockers, and missing verification.
|
|
109
109
|
|
|
110
|
-
Prefer `async: true`, `context: "fresh"` for reviewers/validators, `outputMode: "file-only"` for large summaries, and per-stage output names that will not collide.
|
|
110
|
+
Prefer `async: true`, `context: "fresh"` for reviewers/validators, `outputMode: "file-only"` for large summaries, and per-stage output names that will not collide. Use stable `runs` keys plus `phase` and `label` on each launch item to make async status readable, and hold each awaited result in an ordinary JavaScript variable when a later step needs that specific result — interpolate it (or the durable output path you declared for that child) into the later task text instead of passing a whole aggregate blob. Use this pattern instead of launching several writer workers into a dirty worktree. Include non-blocking suggestions in the writer prompt only when they are small, safe, and do not expand product scope; otherwise record them as deferred.
|
|
111
111
|
|
|
112
112
|
When one child returns a structured target list, use ordinary JavaScript to validate/filter it and map bounded entries into `runs.all`; do not use the removed chain fanout DSL.
|
|
113
113
|
|
|
@@ -117,18 +117,34 @@ Example shape:
|
|
|
117
117
|
subagent({
|
|
118
118
|
async: true,
|
|
119
119
|
context: "fresh",
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
{ agent: "reviewer", phase: "Planning", label: "
|
|
124
|
-
{ agent: "reviewer", phase: "Planning", label: "
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
120
|
+
workflowScript: `
|
|
121
|
+
// Stage 1: parallel read-only planning fanout (stable keys, one per issue cluster)
|
|
122
|
+
const plans = await runs.all([
|
|
123
|
+
{ key: "deploy-plan", agent: "reviewer", phase: "Planning", label: "Deploy docs", task: "Plan fixes for deploy docs/workflow. Inspect the current diff. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "plans/deploy.md", outputMode: "file-only" },
|
|
124
|
+
{ key: "scheduler-plan", agent: "reviewer", phase: "Planning", label: "Scheduler contract", task: "Plan fixes for scheduler contract. Inspect the current diff. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "plans/scheduler.md", outputMode: "file-only" },
|
|
125
|
+
{ key: "sandbox-plan", agent: "reviewer", phase: "Planning", label: "Sandbox/security", task: "Plan fixes for sandbox/security. Inspect the current diff. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "plans/sandbox.md", outputMode: "file-only" }
|
|
126
|
+
]);
|
|
127
|
+
|
|
128
|
+
// Stage 2: single writer — the only child allowed to edit the active worktree.
|
|
129
|
+
// Under outputMode "file-only" the awaited .output is the saved-output
|
|
130
|
+
// reference, so pass the durable paths declared above to the writer.
|
|
131
|
+
const worker = await runs.run("apply-fixes", {
|
|
132
|
+
agent: "worker",
|
|
133
|
+
phase: "Implementation",
|
|
134
|
+
label: "Apply accepted fixes",
|
|
135
|
+
task: "Apply only the accepted fixes from these planning summaries. You are the sole writer for the active worktree. Run focused validation and report changed files, commands, failures, and remaining issues.\\n\\nDeploy plan: plans/deploy.md\\n\\nScheduler plan: plans/scheduler.md\\n\\nSandbox plan: plans/sandbox.md",
|
|
136
|
+
output: "worker/fixes.md",
|
|
137
|
+
outputMode: "file-only"
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Stage 3: parallel read-only validation fanout
|
|
141
|
+
const validations = await runs.all([
|
|
142
|
+
{ key: "validate-deploy-scheduler", agent: "reviewer", phase: "Validation", label: "Deploy/scheduler validation", task: "Validate the post-worker diff for deploy and scheduler fixes. Start from the worker result: " + worker.output + " (also worker/fixes.md). Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "validation/deploy-scheduler.md", outputMode: "file-only" },
|
|
143
|
+
{ key: "validate-sandbox", agent: "reviewer", phase: "Validation", label: "Sandbox validation", task: "Validate the post-worker diff for sandbox/security fixes. Start from the worker result: " + worker.output + " (also worker/fixes.md). Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "validation/sandbox.md", outputMode: "file-only" }
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
return { worker: worker.output, validations: validations.map(v => v.output) };
|
|
147
|
+
`
|
|
132
148
|
})
|
|
133
149
|
```
|
|
134
150
|
|
|
@@ -239,4 +255,4 @@ override can opt one builtin back in. Existing custom-agent frontmatter remains
|
|
|
239
255
|
|
|
240
256
|
Set `subagents.defaultExtensions` to give agents without an `extensions` field a shared child extension allowlist. Omit it to preserve ambient extension discovery, set it to `[]` to disable ambient extensions by default, or use `agentOverrides.<name>.extensions` for one agent. Explicit custom-agent frontmatter still wins.
|
|
241
257
|
|
|
242
|
-
Tool description modes live in `~/.pi/agent/extensions/subagent/config.json`, not `subagents` settings.
|
|
258
|
+
Tool description modes live in `~/.pi/agent/extensions/subagent/config.json`, not `subagents` settings. The default uses split prompt metadata: a short tool description plus active `promptSnippet` and `promptGuidelines`. Set `toolDescriptionMode` to `full` or `compact` to force one description string, or `custom` to read `subagent-tool-description.md` from the project config dir or agent dir; invalid custom files fall back to full mode and the safety guidance is still appended.
|