pi-subagents 0.50.0 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/agents/oracle.md +3 -1
  3. package/agents/reviewer.md +1 -0
  4. package/agents/scout.md +2 -2
  5. package/agents/worker.md +2 -1
  6. package/async-retention-discovery-worker.mjs +180 -0
  7. package/docs/agents.md +40 -2
  8. package/docs/configuration.md +30 -12
  9. package/docs/extension-api.md +42 -1
  10. package/docs/models.md +2 -0
  11. package/docs/observability.md +41 -5
  12. package/docs/tool-reference.md +38 -39
  13. package/docs/workflows.md +171 -3
  14. package/package.json +4 -2
  15. package/skills/pi-subagents/SKILL.md +6 -4
  16. package/skills/pi-subagents/references/constraints-and-recipes.md +12 -6
  17. package/skills/pi-subagents/references/execution-controls.md +29 -15
  18. package/skills/pi-subagents/references/management-authoring-rpc.md +3 -3
  19. package/skills/pi-subagents/references/prompting-and-roles.md +4 -2
  20. package/src/agents/agent-management.ts +124 -351
  21. package/src/agents/agents.ts +157 -31
  22. package/src/agents/skills.ts +1 -1
  23. package/src/api/external-job-provider.ts +185 -0
  24. package/src/api/preflight.ts +36 -8
  25. package/src/api/shared-types.ts +2 -0
  26. package/src/extension/config.ts +3 -3
  27. package/src/extension/doctor.ts +3 -6
  28. package/src/extension/fanout-child.ts +2 -2
  29. package/src/extension/index.ts +166 -88
  30. package/src/extension/public-execution.ts +31 -2
  31. package/src/extension/schemas.ts +12 -35
  32. package/src/extension/tool-description.ts +35 -24
  33. package/src/inspectors/herdr/actions.ts +2 -2
  34. package/src/inspectors/herdr/inspector-runner.ts +2 -1
  35. package/src/inspectors/herdr/project-panes.ts +2 -2
  36. package/src/intercom/native-supervisor-channel.ts +32 -10
  37. package/src/missions/lifecycle.ts +6 -1
  38. package/src/missions/store.ts +4 -9
  39. package/src/missions/workflow-state.ts +2 -2
  40. package/src/profiles/profiles.ts +3 -1
  41. package/src/runs/background/active-run-index.ts +31 -8
  42. package/src/runs/background/async-execution.ts +68 -40
  43. package/src/runs/background/async-job-tracker.ts +20 -4
  44. package/src/runs/background/async-resume.ts +47 -15
  45. package/src/runs/background/async-retention.ts +886 -0
  46. package/src/runs/background/async-status.ts +39 -53
  47. package/src/runs/background/chain-append.ts +3 -33
  48. package/src/runs/background/completion-dedupe.ts +5 -1
  49. package/src/runs/background/completion-replay.ts +22 -12
  50. package/src/runs/background/control-channel.ts +14 -68
  51. package/src/runs/background/fleet-view.ts +68 -19
  52. package/src/runs/background/index-segment.ts +59 -0
  53. package/src/runs/background/inspect-rpc.ts +443 -0
  54. package/src/runs/background/notify.ts +31 -5
  55. package/src/runs/background/result-files.ts +158 -90
  56. package/src/runs/background/result-watcher.ts +116 -20
  57. package/src/runs/background/resume-guidance.ts +8 -5
  58. package/src/runs/background/retained-children.ts +13 -3
  59. package/src/runs/background/run-id-query.ts +7 -0
  60. package/src/runs/background/run-id-resolver.ts +11 -9
  61. package/src/runs/background/run-status.ts +27 -18
  62. package/src/runs/background/scheduled-runs.ts +24 -4
  63. package/src/runs/background/stale-run-reconciler.ts +6 -3
  64. package/src/runs/background/steering.ts +11 -1
  65. package/src/runs/background/subagent-runner.ts +253 -140
  66. package/src/runs/background/subagent-wait.ts +8 -8
  67. package/src/runs/background/terminal-run-index.ts +129 -0
  68. package/src/runs/background/wait-completions.ts +21 -4
  69. package/src/runs/background/wait-subscriptions.ts +81 -2
  70. package/src/runs/foreground/async-steering-action.ts +21 -14
  71. package/src/runs/foreground/execution.ts +3 -1
  72. package/src/runs/foreground/subagent-executor.ts +666 -1579
  73. package/src/runs/foreground/workflow-detach-reconcile.ts +194 -0
  74. package/src/runs/foreground/workflow-foreground-steering.ts +6 -5
  75. package/src/runs/shared/acceptance.ts +4 -4
  76. package/src/runs/shared/chain-outputs.ts +1 -3
  77. package/src/runs/shared/external-job-bridge.ts +444 -0
  78. package/src/runs/shared/external-job-runner.ts +286 -0
  79. package/src/runs/shared/mcp-direct-tool-allowlist.ts +14 -0
  80. package/src/runs/shared/model-fallback.ts +98 -38
  81. package/src/runs/shared/orca-progress-tabs.ts +84 -22
  82. package/src/runs/shared/parallel-handoff.ts +46 -4
  83. package/src/runs/shared/parallel-utils.ts +4 -15
  84. package/src/runs/shared/permissions.ts +5 -1
  85. package/src/runs/shared/pi-args.ts +8 -1
  86. package/src/runs/shared/session-lease.ts +0 -6
  87. package/src/runs/shared/subagent-control.ts +26 -4
  88. package/src/runs/shared/subagent-prompt-runtime.ts +12 -6
  89. package/src/runs/shared/workflow-graph.ts +1 -23
  90. package/src/runs/shared/worktree.ts +14 -2
  91. package/src/shared/atomic-json.ts +22 -2
  92. package/src/shared/capacity-resilient-json.ts +102 -0
  93. package/src/shared/completion-owner.ts +14 -0
  94. package/src/shared/file-system-retry.ts +49 -1
  95. package/src/shared/fork-context.ts +42 -0
  96. package/src/shared/prompt-resources.ts +0 -40
  97. package/src/shared/settings.ts +3 -27
  98. package/src/shared/types.ts +71 -26
  99. package/src/shared/utils.ts +8 -0
  100. package/src/shared/watch-strategy.ts +10 -0
  101. package/src/slash/slash-bridge.ts +2 -1
  102. package/src/slash/slash-commands.ts +29 -3
  103. package/src/tui/fleet.ts +63 -15
  104. package/src/watchdog/change-signature.ts +1 -1
  105. package/src/watchdog/lsp-diagnostics.ts +1 -0
  106. package/src/workflows/chat-progress.ts +2 -2
  107. package/src/workflows/scripted-workflow.ts +220 -67
  108. package/src/runs/foreground/chain-clarify.ts +0 -1354
  109. package/src/runs/foreground/chain-execution.ts +0 -1581
@@ -99,6 +99,42 @@ Pi binds `Ctrl+B` to editor cursor-left by default. The extension shortcut takes
99
99
 
100
100
  If something feels misconfigured, run `/subagents-doctor` or ask: "Check whether subagents and intercom are set up correctly."
101
101
 
102
+ ## Host inspection protocol (RPC)
103
+
104
+ RPC hosts receive live async status through the bounded `subagent-async` widget
105
+ (`PI_SUBAGENT_ASYNC_JSON:` payload). For on-demand detail — a child's delegated
106
+ task, transcript window, or final output — hosts can invoke the extension
107
+ command:
108
+
109
+ ```text
110
+ /subagents-inspect-rpc <requestId> <asyncId> [childId] [--lines N]
111
+ ```
112
+
113
+ Extension commands execute inline over Pi RPC without a model turn. The reply
114
+ arrives as a single emit-then-retract update on the dedicated `subagent-inspect`
115
+ widget key: the first (and only) line is `PI_SUBAGENT_INSPECT_JSON:<JSON>` with a
116
+ versioned `pi-subagents.inspect-reply` payload correlated by `requestId`. Hosts
117
+ must not render this widget; they should buffer the payload by `requestId` and
118
+ drop unmatched replies.
119
+
120
+ Inspection properties:
121
+
122
+ - Read-only and on demand: nothing is persisted, broadcast, or added to
123
+ notification details; every request re-reads canonical run artifacts after
124
+ the same reconciliation the status action performs.
125
+ - Session-scoped: runs owned by another session fail with `foreign_session`;
126
+ unknown ids fail with `not_found`; cleaned-up artifacts fail with `stale`.
127
+ - Bounded: per-field string caps, a message-count cap (`--lines`, max 200), and
128
+ a hard 64 KB serialized budget with explicit `truncated` markers.
129
+ - No filesystem paths appear in the reply.
130
+ - `task` is the child session's first user message and is only populated when
131
+ it is genuinely attributable (fresh-context child whose session file fits the
132
+ read window); forked children omit it.
133
+ - `childId` is exactly the node id the host received in the status snapshot
134
+ (step `workflowKey`/`runId`/`step:<index>`, or a nested run id).
135
+
136
+ In TUI mode the command only points at the interactive `/subagents` inspector.
137
+
102
138
  ## Async run artifacts
103
139
 
104
140
  Async runs write machine-readable lifecycle artifacts for observability and workflow gates:
@@ -122,7 +158,7 @@ The result file is consumed and deleted once its completion notice is delivered.
122
158
 
123
159
  `subagent_wait` surfaces a slim projection of each terminal payload it covered in its own tool-result `details.completions` — run identity, per-child agent/`runId`/success, artifact paths, and the bounded `archivePath`, without duplicating output text. It reads the replay when watcher delivery or a watcher restart has removed the one-shot result file and in-memory completion state is unavailable. Durable non-blocking wait subscriptions use the same replay in their delivered details. Workflow result files record each child's `runId` explicitly, since a workflow child's `artifactPaths` entry points at its saved output rather than the artifact files keyed by the id. Extensions observing `tool_result` events can read run and artifact identity from there instead of parsing the text summary.
124
160
 
125
- Output archives reference an existing child output artifact or session file when one is available. For children without either file, the archive stores only the tail of result text, bounded to 64 KiB per run, and records whether it was truncated. Replay and archive JSON use `version: 1`; consumers must ignore unknown fields.
161
+ Output archives reference an existing child output artifact or session file when one is available. For children without either file, the archive stores a per-child `result-tail` entry with `resultIndex`, bounded to 64 KiB per child, and records whether it was truncated. Replay and archive JSON use `version: 1`; consumers must ignore unknown fields.
126
162
 
127
163
  Nested fanout status is stored as compact sidecar event/registry metadata and merged into parent status views and result/intercom payloads; full recursive status snapshots are not embedded in parent result files.
128
164
 
@@ -164,15 +200,15 @@ Foreground and async runners share bounded child-protocol handling:
164
200
  - `agent_end.willRetry` defers completion until the child settles.
165
201
  - Current Pi builds use `agent_settled` as the terminal watermark; older builds retain the bounded terminal-message fallback.
166
202
 
167
- ## Chain and debug artifacts
203
+ ## Workflow and debug artifacts
168
204
 
169
- Each chain run creates a scratch directory under its resolved chain root. With the default `artifactDir: "session"` or with `"temp"`, it is user-scoped temp storage. With `artifactDir: "project"`, the root is `<cwd>/.pi/subagents/chain-runs/`:
205
+ Each scripted workflow stores runtime artifacts under a workflow artifact directory. The on-disk directory is still named `chain-runs` for compatibility. With the default `artifactDir: "session"` or with `"temp"`, it is user-scoped temp storage. With `artifactDir: "project"`, the root is `<cwd>/.pi/subagents/chain-runs/`:
170
206
 
171
207
  ```text
172
208
  <tmpdir>/pi-subagents-<scope>/chain-runs/{runId}/
173
209
  ```
174
210
 
175
- A run directory may contain files such as `context.md`, `plan.md`, `progress.md`, and `parallel-{stepIndex}/.../output.md`. User-scoped temp chain directories older than 24 hours are cleaned up on extension startup; project-local and explicit persistent roots are not age-scanned.
211
+ A run directory may contain files such as `context.md`, `plan.md`, `progress.md`, and `parallel-{stepIndex}/.../output.md`. User-scoped temp workflow artifact directories older than 24 hours are cleaned up on extension startup; project-local and explicit persistent roots are not age-scanned.
176
212
 
177
213
  Debug artifacts live under `{sessionDir}/subagent-artifacts/`, `.pi/subagents/artifacts/` for project-scoped runs, or a user-scoped temp artifact directory. Single-run relative `output` files are saved under `{artifactsDir}/outputs/{runId}/` unless `singleRunOutputBaseDir` is configured. Per task you may see:
178
214
 
@@ -187,7 +223,7 @@ For npm package projects, project-scoped artifacts need a `.npmignore` rule (or
187
223
 
188
224
  ## Sessions
189
225
 
190
- Session files are stored under a per-run session directory. With `context: "fork"`, each child starts with `--session <branched-session-file>` produced from the parent's current leaf. That is a real session fork, not an injected summary.
226
+ Session files are stored under a per-run session directory. With `context: "fork"`, each child starts with `--session <branched-session-file>` produced from the parent's current leaf. That is a real session fork, not an injected summary. An omitted launch `context` that resolves through `defaultContext: fork` uses the same branch when the parent session file and current leaf exist, and otherwise starts fresh.
191
227
 
192
228
  ## Completion notifications
193
229
 
@@ -4,6 +4,8 @@ Parameters and actions for the `subagent` tool. These are what the LLM passes wh
4
4
 
5
5
  ## Execution examples
6
6
 
7
+ Chaining is code-driven through `workflowScript`. Use `await runs.run(...)` for sequential steps and `await runs.all([{ key, agent, task }, ...])` for ordinary parallel fanout. Do not read `.output` from an unawaited `runs.run` launch. Stored `runs.run` promises are only for the advanced rolling fanout pattern under [Workflow steering](#workflow-steering), where every promise is later observed with direct `await`, `Promise.race`, or `Promise.all`. Legacy top-level `chain`, `tasks`, and `parallel` inputs are not supported. Helper functions must be plain functions or explicit Promise chains. Nested `async function` helpers, async arrows, and async methods are rejected so child-launch tracking stays portable across Node and Bun.
8
+
7
9
  ```js
8
10
  // One child; return the child promise explicitly
9
11
  { workflowScript: `return runs.run("main", { agent: "scout", task: "Analyze the auth flow" })` }
@@ -31,18 +33,18 @@ Parameters and actions for the `subagent` tool. These are what the LLM passes wh
31
33
  | `agent` | string | - | Agent target for management actions. Workflow child agents are set inside `runs.run` or `runs.all`. |
32
34
  | `action` | string | - | Agent management (including `guide`, `children.list`, and `refine`/`refine.show`/`refine.rollback`), mission (`mission.create/list/show/update/resolve-decision/attach-run/close`), Herdr inspector (`inspector.open/status/close`), status/control, schedule, watchdog, or doctor action. |
33
35
  | `topic` | `overview \| workflows \| agents \| missions \| observability \| tool-reference \| configuration \| models \| watchdog \| extension-api` | `overview` | Packaged guide topic for `action: "guide"`. |
34
- | `chainName` | string | - | Chain name for management actions. |
35
- | `config` | object/string | - | Agent or existing durable chain config for management create/update. |
36
- | `context` | `fresh \| fork` | per-agent default or `fresh` | Explicit `fresh` or `fork` overrides every workflow child. When omitted, each child agent uses its own `defaultContext`; `fork` creates real branched sessions from the parent leaf. Packaged `worker`, `oracle`, and `advisor` default to `fork`. |
36
+ | `config` | object/string | - | Agent config for management create/update. |
37
+ | `context` | `fresh \| fork` | global or per-agent default, else `fresh` | Explicit `fresh` or `fork` overrides every workflow child. When omitted, [`defaultSubagentContext`](configuration.md#defaultsubagentcontext) wins over each agent's `defaultContext`; `"fork"` creates a real branched session when the parent session file and current leaf exist, otherwise it falls back to `fresh`. Packaged `worker`, `oracle`, and `advisor` default to `fork`. |
37
38
  | `missionId` | string | - | Attach a workflow to an existing project mission instead of creating its default enclosing mission. |
38
39
  | `mission` | object/false | auto-create | Override the default enclosing mission with `{ title \| summary, objective?, goal?, budget?, labels? }`. Set exactly one non-empty `title` or `summary`; `objective` and `labels` are optional. `goal` may only be `true`, requires `budget.tokens`, and enables continuation notices. Pass `false` for an intentionally ephemeral workflow with no mission for it or its children and no `state` global. Explicit mission persistence failures are strict. |
39
40
  | `handoffPath` | string | - | Aggregate handoff manifest required by `action: "worktree.discard"`. |
40
- | `focus` | boolean | true | Focus the newly split pane for `action: "inspector.open"` or `action: "project.open"`; not a standalone action. |
41
+ | `focus` | boolean | false | Focus the newly split pane for `action: "inspector.open"` or `action: "project.open"`; not a standalone action. Panes open in the background unless you set `focus: true`. |
41
42
  | `view` | `fleet \| transcript` | - | Optional `status` view for the active fleet surface or transcript tail inspection. |
42
43
  | `lines` | number | `80` | Maximum transcript lines for `action: "status", view: "transcript"`; capped at 500. |
43
44
  | `agentScope` | `user \| project \| both` | `both` | Agent discovery scope. Project wins on collisions. |
44
- | `async` | boolean | default-on | Background execution. Workflows default to background and accept `async:false` as an explicit foreground escape hatch. |
45
- | `chatProgress` | `auto \| off \| live-card` | `auto` | WorkflowScript chat projection. `auto` renders a live in-chat card only for watched foreground workflows in the same Git repository, including managed worktrees; it is off otherwise. Explicit `live-card` requires `async:false` and the same Git repository. |
45
+ | `async` | boolean | default-on | Background execution. Workflows default to background. `async:false` blocks the parent until completion. |
46
+ | `chatProgress` | `auto \| off \| live-card` | `auto` | WorkflowScript chat projection. `auto` renders a live in-chat card only for watched foreground workflows in the same Git repository, including managed worktrees; it is off otherwise. Explicit `live-card` requires `async:false` and the same Git repository. Async workflows have no inline live card, so omit `chatProgress` or use `auto`/`off`; use `async:false` only when the parent must block. |
47
+ | `isolation` | `none \| worktree` | - | Workflow child isolation. `none` runs in the shared cwd and does not need Git. `worktree` requires a managed Git worktree. Do not combine it with a contradictory `worktree` value. |
46
48
  | `timeoutMs` / `maxRuntimeMs` | number | config `timeoutMs`, else 30 min foreground / single-agent async | Optional run-level max runtime in milliseconds. When omitted, the global [`timeoutMs`](configuration.md#timeoutms) config provides the default; absent that, foreground and plain single-agent async runs fall back to 30 minutes, while composite async runs (chains, parallel tasks, workflows) stay unbounded at the top level. |
47
49
  | `toolTimeoutMs` | number | fast-tool default | Optional positive hard per-tool-call deadline in milliseconds. Precedence: call value → agent frontmatter → config → `PI_SUBAGENT_TOOL_TIMEOUT_MS`. The timer starts on `tool_execution_start`, clears on the matching `tool_execution_end`, and terminates the run with `timedOut: true` if the tool remains open. When omitted, known-fast built-in tools get a five-minute default; long-running tools get attention notices but no hard default. It never extends the run deadline; `contact_supervisor`, `intercom`, and `subagent_wait` are exempt. |
48
50
  | `turnBudget` | object | none | Optional assistant-turn budget `{ maxTurns, graceTurns }`. At `maxTurns` the child is warned to wrap up. After the grace window (default 1), termination occurs at the next assistant boundary; a response that starts tool work records `termination-deferred` until a later boundary. Partial output is returned on abort. |
@@ -65,11 +67,34 @@ Bound writer work with a narrow task and an outer `timeoutMs` or `maxRuntimeMs`
65
67
 
66
68
  ### Fork context details
67
69
 
68
- `context: "fork"` fails fast when the parent session is not persisted, the current leaf is missing, or the branched child session cannot be created.
70
+ Explicit `context: "fork"` fails fast when the parent session is not persisted, the current leaf is missing, or the branched child session cannot be created. By contrast, global `defaultSubagentContext: "fork"` and agent-level `defaultContext: fork` are preferences: when the parent has no persisted session file or current leaf yet, the launch uses `fresh` immediately instead of failing and requiring a retry. Global `defaultSubagentContext: "fresh"` starts fresh. Explicit `context: "fresh"` always wins over both preferences.
71
+
72
+ When the inherited transcript contains signed Anthropic `thinking` / `redacted_thinking` blocks, `pi-subagents` strips those provider-private blocks from the forked child session. It forces thinking `off` only when the child's effective primary or fallback model resolves through the model registry to the Anthropic provider or `anthropic-messages` API; unresolved models are treated conservatively. The result reports every affected child, including on failed runs. Use `context: "fresh"` when an Anthropic child needs thinking. Explicit `context: "fork"` never silently downgrades to `fresh`.
73
+
74
+ In workflow runs that omit `context`, each `runs.run` child follows the global `defaultSubagentContext` when set, then its own `defaultContext`. Without the global setting, a fresh-default scout can run fresh beside a fork-default worker. If the parent session file or current leaf is not available yet, implicit fork-default children run fresh. Pass explicit `context: "fork"` or `context: "fresh"` when you intentionally want one context for every child.
75
+
76
+ ### Workflow steering
77
+
78
+ `runs.steer(key, message, options?)` targets a stable key already launched by `runs.run` or `runs.all`. It does not accept a raw run id. Options are `mode?: "steer" | "follow_up" | "auto"`, `index?: number`, and `ackTimeoutMs?: number`. The promise returns `{ key, state, requestId?, deliveryStatus?, targets?, error? }`, where `state` is `queued`, `delivered`, `missed`, or `failed`.
69
79
 
70
- When the inherited transcript contains signed Anthropic `thinking` / `redacted_thinking` blocks, `pi-subagents` strips those provider-private blocks from the forked child session. It forces thinking `off` only when the child's effective primary or fallback model resolves through the model registry to the Anthropic provider or `anthropic-messages` API; unresolved models are treated conservatively. The result reports every affected child, including on failed runs. Use `context: "fresh"` when an Anthropic child needs thinking. Forking never silently downgrades to `fresh`.
80
+ The workflow trace records the attempt and receipt. Always await, return, or include the promise in an awaited standard Promise combinator. Unawaited steering calls reject workflow completion after the side effect settles. `Promise.race` remains the rolling primitive. This slice reuses the foreground and async steering transports and disables steering recovery.
71
81
 
72
- In workflow runs that omit `context`, each `runs.run` child follows its own `defaultContext`, so a fresh-default scout can run fresh beside a fork-default worker. Pass explicit `context: "fork"` or `context: "fresh"` when you intentionally want one context for every child.
82
+ For advanced rolling fanout, keep the launched `runs.run` promises in ordinary JavaScript data only when every promise is later observed with direct `await`, `Promise.race`, or `Promise.all`. `Promise.race` gives the next completed child, `runs.steer` can challenge a still-running keyed sibling, and `Promise.all` collects the rest. No separate `runs.start`, `runs.next`, or `runs.collect` API is exposed.
83
+
84
+ ```js
85
+ { workflowScript: `
86
+ let pending = [
87
+ { key: "writer", promise: runs.run("writer", { agent: "worker", task: "Draft the fix" }).then((result) => ({ key: "writer", result })) },
88
+ { key: "reviewer", promise: runs.run("reviewer", { agent: "reviewer", task: "Review likely risks" }).then((result) => ({ key: "reviewer", result })) }
89
+ ];
90
+ const first = await Promise.race(pending.map((child) => child.promise));
91
+ pending = pending.filter((child) => child.key !== first.key);
92
+ const target = pending[0];
93
+ const receipt = await runs.steer(target.key, "Use this early review:\n" + first.result.output, { mode: "auto" });
94
+ const rest = await Promise.all(pending.map((child) => child.promise));
95
+ return { first: first.key, rest: rest.map((child) => child.key), receipt };
96
+ ` }
97
+ ```
73
98
 
74
99
  ### Output mode details
75
100
 
@@ -79,12 +104,6 @@ In workflowScript, give each child an explicit output path when later script ste
79
104
 
80
105
  Workflows get `await state.get(key)` and `await state.set(key, value)` through their default or explicit mission. Use them to share durable JSON values across later workflows attached with the same `missionId`. Each `set` takes the state-file lock and merges its key with the latest on-disk state. Missing keys return `undefined`, and the complete state file has a strict 256 KiB limit. `mission:false` workflows have no `state` global.
81
106
 
82
- ### Prompt fragments
83
-
84
- Use `await prompts.render(ref, vars?)` to render reusable plain task text. Refs require an explicit scope: `package:<name>` reads the installed package `prompts/` directory, `user:<name>` reads the Pi agent `prompts/` directory, and `project:<name>` reads the current workflow project's config `prompts/` directory. Each ref names a top-level `<name>.md` file. Frontmatter is removed. Scalar string, number, and boolean variables replace matching `{{name}}` placeholders. Unknown placeholders stay unchanged.
85
-
86
- Rendering only returns text to the sandbox. It does not give the script filesystem access and does not change child launch parameters, worktree capture, or cleanup. Pass the rendered result explicitly as `task`.
87
-
88
107
  ### Retained children
89
108
 
90
109
  Completed workflow children from the current parent session stay addressable as retained children. `{ action: "children.list" }` lists up to the last 10 with their run ids and explicit `resumable` or `not resumable` state. Resume only rows reported `resumable`; if no row is resumable, start a same-role fallback challenge and label it as fallback. A later workflow continues a resumable child by passing `resume` instead of `agent`:
@@ -94,8 +113,7 @@ Completed workflow children from the current parent session stay addressable as
94
113
  let writer = await runs.run("implement", { agent: "worker", task: "Implement the accepted contract" });
95
114
  for (const pass of [1, 2]) {
96
115
  if (!writer.runId) throw new Error("writer did not return a retained run id");
97
- const task = await prompts.render("project:writer-followup", { pass, previous: writer.output });
98
- writer = await runs.run("followup-" + pass, { resume: writer.runId, task });
116
+ writer = await runs.run("followup-" + pass, { resume: writer.runId, task: "Revisit pass " + pass + ": " + writer.output });
99
117
  }
100
118
  return writer;
101
119
  ` }
@@ -113,7 +131,7 @@ For a simple implementation challenge outside a workflow script, send the challe
113
131
 
114
132
  `{ action: "guide" }` reads the packaged `README.md` from the installed version. Pass `topic` to read its packaged `docs/<topic>.md` file instead. Valid topics are `overview`, `workflows`, `agents`, `missions`, `observability`, `tool-reference`, `configuration`, `models`, `watchdog`, and `extension-api`. Unknown topics list the valid values and do not change files. Use `/subagents-guide [topic]` for the slash equivalent.
115
133
 
116
- Agent definitions are not loaded into context by default. Management actions let the LLM discover, inspect, create, update, and delete agents and chains at runtime. An unknown action returns safe next steps (`status` and `list`) and may suggest a close non-destructive action. Destructive actions are only named for a near-complete one-character typo, and suggestions never execute an action.
134
+ Agent definitions are not loaded into context by default. Management actions let the LLM discover, inspect, create, update, and delete agents at runtime. An unknown action returns safe next steps (`status` and `list`) and may suggest a close non-destructive action. Destructive actions are only named for a near-complete one-character typo, and suggestions never execute an action.
117
135
 
118
136
  ```ts
119
137
  { action: "list" }
@@ -122,7 +140,6 @@ Agent definitions are not loaded into context by default. Management actions let
122
140
  { action: "models" }
123
141
  { action: "models", agent: "reviewer" }
124
142
  { action: "get", agent: "code-analysis.scout" }
125
- { action: "get", chainName: "review-pipeline" }
126
143
 
127
144
  { action: "create", config: {
128
145
  name: "Code Scout",
@@ -146,22 +163,11 @@ Agent definitions are not loaded into context by default. Management actions let
146
163
  progress: true
147
164
  }}
148
165
 
149
- { action: "create", config: {
150
- name: "review-pipeline",
151
- description: "Scout then review",
152
- scope: "project",
153
- steps: [
154
- { agent: "scout", task: "Scan {task}", output: "context.md" },
155
- { agent: "reviewer", task: "Review {previous}", reads: ["context.md"] }
156
- ]
157
- }}
158
166
 
159
167
  { action: "update", agent: "code-analysis.scout", config: { model: "openai/gpt-4o" } }
160
168
  { action: "update", agent: "code-analysis.scout", config: { acceptance: "" } } // clear the frontmatter default
161
169
  { action: "update", agent: "code-analysis.scout", config: { acceptanceRole: false } } // restore inferred name fallback
162
- { action: "update", chainName: "review-pipeline", config: { steps: [...] } }
163
170
  { action: "delete", agent: "scout" }
164
- { action: "delete", chainName: "review-pipeline" }
165
171
 
166
172
  { action: "eject", agent: "reviewer" }
167
173
  { action: "eject", agent: "reviewer", agentScope: "project" }
@@ -201,9 +207,6 @@ subagent({ action: "resume", id: "<nested-run-id>", message: "follow-up for a ne
201
207
  subagent({ action: "steer", id: "<run-id>", message: "guidance for the running child" })
202
208
  subagent({ action: "steer", id: "<run-id>", mode: "follow_up", message: "check this after the current turn" })
203
209
  subagent({ action: "steer", id: "<run-id>", index: 1, mode: "auto", message: "guidance for child 2" })
204
- subagent({ action: "append-step", id: "<run-id>", step: { agent: "worker", task: "Continue from {previous}" } })
205
- subagent({ action: "approve-checkpoint", id: "<run-id>" })
206
- subagent({ action: "reject-checkpoint", id: "<run-id>" })
207
210
  subagent({ action: "doctor" })
208
211
  ```
209
212
 
@@ -247,10 +250,6 @@ Only a top-level single run may interrupt after the acknowledgment deadline and
247
250
 
248
251
  The persisted `steering` ledger retains 20 requests and replaces the old `steerCount`/`lastSteerAt` fields.
249
252
 
250
- ### append-step
251
-
252
- `append-step` requires `legacyChainControls: true`. The default registered model-facing schema omits this legacy control surface. When enabled, it accepts exactly one `step` object for an existing durable chain for a top-level async chain whose status is still `running`. The step is persisted in the run directory and becomes eligible only after the chain's already-queued steps finish. Completed, failed, rejected, paused, foreground, single, and non-chain runs reject appends.
253
-
254
253
  ## Acceptance gates
255
254
 
256
255
  Every run resolves an effective acceptance policy. Callers may omit `acceptance` for the inferred default, or set it on single runs, top-level parallel task items, chain steps, static parallel tasks, and dynamic fanout templates.
@@ -326,11 +325,11 @@ Orca progress tabs are a global, opt-in observer, not an agent runner. Enable th
326
325
  { "orcaProgressTabs": { "enabled": true } }
327
326
  ```
328
327
 
329
- Every foreground or background child keeps running through its normal native Pi or `external-cli` path. For each logical child, the observer asks Orca to create a background terminal tab in that child's current worktree and mirrors progress into it. Titles receive a persistent worktree-local sequence number, including across separate workflow calls. Model/startup retries reuse the same tab. Parallel and chain children each receive their own tab; attaching an already-running async root does not create a duplicate. Terminal control sequences are removed at the viewer sink across read boundaries. Each mirror is capped at 1 MiB and truncates when the cap or stream backpressure is reached. After the child finishes, its viewer returns to the terminal shell instead of ending the terminal session, so the tab and scrollback remain until the user closes them. Successful native Pi children with a known session append a safely quoted removal command for the exact verified session path; unsuccessful and sessionless children append only their terminal status.
328
+ Every foreground or background child keeps running through its normal native Pi or `external-cli` path. For each logical child, the observer asks Orca to create a background terminal tab in that child's current worktree and mirrors progress into it. Titles receive a persistent worktree-local sequence number, including across separate workflow calls. Creates for the same worktree are serialized in that sequence so tabs appear to the right in order (`1`, then `2`, then `3`) instead of racing. Model/startup retries reuse the same tab. Parallel and chain children each receive their own tab; attaching an already-running async root does not create a duplicate. Terminal control sequences are removed at the viewer sink across read boundaries. Each mirror is capped at 1 MiB and truncates when the cap or stream backpressure is reached. After the child finishes, its viewer returns to the terminal shell instead of ending the terminal session, so the tab and scrollback remain until the user closes them. Successful native Pi children with a known session append a safely quoted removal command for the exact verified session path; unsuccessful and sessionless children append only their terminal status.
330
329
 
331
330
  The observer supports macOS and Linux and is disabled on Windows. It requires executable `orca` on `PATH` (or `PI_SUBAGENT_ORCA_BINARY`) and a running Orca runtime that recognizes the child cwd. Availability and tab creation are best-effort: failures never fail, stop, or delay the subagent. Set `orcaProgressTabs.enabled` to `false` to guarantee that no Orca command or tab is created.
332
331
 
333
- Agent profile `runner.type` remains unchanged: supported values are native Pi (the default) and `external-cli`. Orca is intentionally not a profile runner and does not own subagent execution, completion, cancellation, artifacts, or result delivery.
332
+ Agent profile `runner.type` supports native Pi (the default), `external-cli`, and `external-job`. Orca is intentionally not a profile runner and does not own subagent execution, completion, cancellation, artifacts, or result delivery.
334
333
 
335
334
  ## External CLI agent profiles
336
335
 
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`; pass `context: "fresh"` when you intentionally want a fresh child run.
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
- For watched same-repo workflows, pass `async:false` to show the live in-chat workflow card. `chatProgress` can force `off` or `live-card` when the automatic policy is not what you want. Foreground 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.
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
 
@@ -114,6 +280,8 @@ The parent replies with `subagent_supervisor({ action: "reply", replyTo, message
114
280
 
115
281
  Child-side routine completion handoffs are not expected. If a child appears stalled, needs-attention notices show up in the parent session with useful next actions, such as checking `subagent({ action: "status" })`, interrupting the run, or nudging the child.
116
282
 
283
+ If a `workflowScript` child detaches through `contact_supervisor`, the enclosing async workflow stays `paused` until that child exits. Then the extension reconciles it to `complete` or `failed`. Wait on the child until that happens.
284
+
117
285
  If messages do not show up, run `/subagents-doctor`. Advanced users can tune the bridge with `intercomBridge` in [configuration.md](configuration.md).
118
286
 
119
287
  ## Recursion guard
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-subagents",
3
- "version": "0.50.0",
3
+ "version": "0.52.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, compatibility-chain, async, forked-context, and coordinated workflows. Use
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. Use `return runs.run("main", { agent, task })` for one child and `runs.all([...])` for coordinated waves: sequence, parallelism, branching, retries, gate monitors, and aggregation. Scripted workflows start asynchronously by default; pass `async:false` only for a small foreground run.
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 or chains, edit agent files, use prompt-template integration, or expose extension RPC | `references/management-authoring-rpc.md` |
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.
@@ -30,11 +30,13 @@ For broad or uncertain requests, read more than one reference. For complex work,
30
30
  ## Always-on constraints
31
31
 
32
32
  - Keep the parent as orchestrator and final decision-maker.
33
+ - For plan, design, or architecture advice that asks to consult, discuss with, or come to agreement with `oracle`, use a short same-session consultation loop: read the first result, resume once with a targeted challenge when material tradeoffs remain, then synthesize the parent decision. Keep explicit one-shot, trivial, and fully settled consultations one-shot.
33
34
  - Use one writer per cwd/worktree unless isolated worktrees are intentional.
34
35
  - 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
36
  - 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
37
  - Prefer fresh-context review/validation fanout, then synthesize and apply fixes in the parent.
37
- - Use async/background by default when work can proceed independently; do not poll just to wait. For adaptive gates, branch in `workflowScript`. Approval controls remain available only for already-running durable legacy chains.
38
+ - 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`.
39
+ - 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
40
  - Preserve capability ceilings, including child tool restrictions and session-scoped allowed-agent restrictions.
39
41
  - Escalate unresolved product, architecture, authority, release, merge, or safety decisions upward instead of letting a child decide silently.
40
42
  - 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
- - **Forking requires a persisted parent session.** If the current session does not
8
- have a persisted session file, forked runs fail. Packaged `worker`, `oracle`,
9
- and `advisor` default to forked context, so use `context: "fresh"` explicitly
10
- when that is not available or not wanted.
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
@@ -63,6 +65,10 @@ Give subagents specific tasks rather than vague mandates.
63
65
 
64
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
 
68
+ ### Use a short oracle consultation for material advice
69
+
70
+ When a user asks to ask, consult, discuss with, or come to agreement with `oracle` about a plan, design, or architecture decision, do not treat the first advisory report as final when it raises a material challenge or tradeoff. Read it, resume the same oracle session once with a targeted question, then make the parent decision. An explicit one-shot request, a trivial question, or a fully settled first answer does not need a follow-up.
71
+
66
72
  ### Intervene only on clear control signals
67
73
 
68
74
  Use subagent control proactively when a delegated run emits `needs_attention`, or when a human asks you to regain control. Do not interrupt just because a child has briefly produced no output. Silence can be normal during long tool calls, test runs, or model reasoning.
@@ -77,8 +83,8 @@ Use `/name` so intercom targeting stays stable.
77
83
 
78
84
  ```js
79
85
  subagent({ workflowScript: `
80
- const context = await runs.run("recon", { agent: "scout", task: "Inspect the codebase and identify the implementation seam" });
81
- return (await runs.run("implement", { agent: "worker", task: "Implement from: " + context.output })).output;
86
+ const context = await runs.run("recon", { agent: "scout", task: "Start from the named source roots, paths, and symbols. Identify the implementation seam before broad search." });
87
+ return (await runs.run("implement", { agent: "worker", task: "Read the scout output, plan paths, and named files/seams first. Implement from: " + context.output })).output;
82
88
  ` })
83
89
  ```
84
90