pi-subagents 0.73.1 → 0.74.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 (237) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +1 -1
  3. package/docs/agents.md +16 -12
  4. package/docs/configuration.md +66 -9
  5. package/docs/extension-api.md +5 -4
  6. package/docs/missions.md +13 -13
  7. package/docs/models.md +2 -2
  8. package/docs/observability.md +2 -2
  9. package/docs/tool-reference.md +63 -86
  10. package/docs/workflows.md +165 -171
  11. package/package.json +2 -2
  12. package/prompts/review-loop.md +1 -1
  13. package/skills/council-mode/SKILL.md +1 -1
  14. package/skills/pi-subagents/SKILL.md +12 -5
  15. package/skills/pi-subagents/references/constraints-and-recipes.md +3 -3
  16. package/skills/pi-subagents/references/execution-controls.md +68 -67
  17. package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  18. package/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
  19. package/skills/pi-subagents/references/prompting-and-roles.md +45 -48
  20. package/src/agents/agent-management.js +2 -2
  21. package/src/agents/agent-management.js.map +1 -1
  22. package/src/agents/agents.d.ts +1 -0
  23. package/src/agents/agents.d.ts.map +1 -1
  24. package/src/agents/agents.js +8 -0
  25. package/src/agents/agents.js.map +1 -1
  26. package/src/api/preflight.d.ts +5 -1
  27. package/src/api/preflight.d.ts.map +1 -1
  28. package/src/api/preflight.js +2 -1
  29. package/src/api/preflight.js.map +1 -1
  30. package/src/extension/config.d.ts.map +1 -1
  31. package/src/extension/config.js +12 -4
  32. package/src/extension/config.js.map +1 -1
  33. package/src/extension/control-notices.d.ts +2 -0
  34. package/src/extension/control-notices.d.ts.map +1 -1
  35. package/src/extension/control-notices.js +2 -0
  36. package/src/extension/control-notices.js.map +1 -1
  37. package/src/extension/doctor.d.ts.map +1 -1
  38. package/src/extension/doctor.js +1 -0
  39. package/src/extension/doctor.js.map +1 -1
  40. package/src/extension/fanout-child.d.ts.map +1 -1
  41. package/src/extension/fanout-child.js +13 -3
  42. package/src/extension/fanout-child.js.map +1 -1
  43. package/src/extension/herdr-pi-bridge.d.ts.map +1 -1
  44. package/src/extension/herdr-pi-bridge.js +2 -0
  45. package/src/extension/herdr-pi-bridge.js.map +1 -1
  46. package/src/extension/index.d.ts +1 -1
  47. package/src/extension/index.d.ts.map +1 -1
  48. package/src/extension/index.js +62 -161
  49. package/src/extension/index.js.map +1 -1
  50. package/src/extension/public-execution.d.ts +10 -1
  51. package/src/extension/public-execution.d.ts.map +1 -1
  52. package/src/extension/public-execution.js +69 -35
  53. package/src/extension/public-execution.js.map +1 -1
  54. package/src/extension/reply-workflow-script.d.ts +12 -0
  55. package/src/extension/reply-workflow-script.d.ts.map +1 -0
  56. package/src/extension/reply-workflow-script.js +61 -0
  57. package/src/extension/reply-workflow-script.js.map +1 -0
  58. package/src/extension/rpc.d.ts +2 -0
  59. package/src/extension/rpc.d.ts.map +1 -1
  60. package/src/extension/rpc.js +33 -10
  61. package/src/extension/rpc.js.map +1 -1
  62. package/src/extension/schemas.d.ts +3 -117
  63. package/src/extension/schemas.d.ts.map +1 -1
  64. package/src/extension/schemas.js +38 -125
  65. package/src/extension/schemas.js.map +1 -1
  66. package/src/extension/tool-activation.d.ts +2 -4
  67. package/src/extension/tool-activation.d.ts.map +1 -1
  68. package/src/extension/tool-activation.js +54 -104
  69. package/src/extension/tool-activation.js.map +1 -1
  70. package/src/extension/tool-description.d.ts +8 -5
  71. package/src/extension/tool-description.d.ts.map +1 -1
  72. package/src/extension/tool-description.js +51 -27
  73. package/src/extension/tool-description.js.map +1 -1
  74. package/src/extension/upgrade-notice.d.ts +7 -0
  75. package/src/extension/upgrade-notice.d.ts.map +1 -0
  76. package/src/extension/upgrade-notice.js +37 -0
  77. package/src/extension/upgrade-notice.js.map +1 -0
  78. package/src/integrations/herdr-status.d.ts +3 -0
  79. package/src/integrations/herdr-status.d.ts.map +1 -1
  80. package/src/integrations/herdr-status.js +17 -3
  81. package/src/integrations/herdr-status.js.map +1 -1
  82. package/src/intercom/native-supervisor-channel.d.ts.map +1 -1
  83. package/src/intercom/native-supervisor-channel.js +4 -1
  84. package/src/intercom/native-supervisor-channel.js.map +1 -1
  85. package/src/runs/background/async-execution.d.ts +3 -0
  86. package/src/runs/background/async-execution.d.ts.map +1 -1
  87. package/src/runs/background/async-execution.js +6 -2
  88. package/src/runs/background/async-execution.js.map +1 -1
  89. package/src/runs/background/async-job-tracker.d.ts.map +1 -1
  90. package/src/runs/background/async-job-tracker.js +65 -22
  91. package/src/runs/background/async-job-tracker.js.map +1 -1
  92. package/src/runs/background/async-retention.d.ts +1 -1
  93. package/src/runs/background/async-retention.d.ts.map +1 -1
  94. package/src/runs/background/async-retention.js +47 -15
  95. package/src/runs/background/async-retention.js.map +1 -1
  96. package/src/runs/background/await-async-run.d.ts +23 -0
  97. package/src/runs/background/await-async-run.d.ts.map +1 -0
  98. package/src/runs/background/await-async-run.js +80 -0
  99. package/src/runs/background/await-async-run.js.map +1 -0
  100. package/src/runs/background/chain-root-attachment.d.ts +1 -0
  101. package/src/runs/background/chain-root-attachment.d.ts.map +1 -1
  102. package/src/runs/background/chain-root-attachment.js +3 -3
  103. package/src/runs/background/chain-root-attachment.js.map +1 -1
  104. package/src/runs/background/notify.d.ts +3 -0
  105. package/src/runs/background/notify.d.ts.map +1 -1
  106. package/src/runs/background/notify.js +3 -0
  107. package/src/runs/background/notify.js.map +1 -1
  108. package/src/runs/background/run-child-session.d.ts.map +1 -1
  109. package/src/runs/background/run-child-session.js +4 -1
  110. package/src/runs/background/run-child-session.js.map +1 -1
  111. package/src/runs/background/run-status.d.ts.map +1 -1
  112. package/src/runs/background/run-status.js +6 -0
  113. package/src/runs/background/run-status.js.map +1 -1
  114. package/src/runs/background/runner-child-launch.d.ts +1 -0
  115. package/src/runs/background/runner-child-launch.d.ts.map +1 -1
  116. package/src/runs/background/runner-child-launch.js +2 -0
  117. package/src/runs/background/runner-child-launch.js.map +1 -1
  118. package/src/runs/background/scheduled-runs.js +5 -5
  119. package/src/runs/background/scheduled-runs.js.map +1 -1
  120. package/src/runs/background/subagent-runner.d.ts +3 -0
  121. package/src/runs/background/subagent-runner.d.ts.map +1 -1
  122. package/src/runs/background/subagent-runner.js +5 -0
  123. package/src/runs/background/subagent-runner.js.map +1 -1
  124. package/src/runs/foreground/async-stop-action.d.ts.map +1 -1
  125. package/src/runs/foreground/async-stop-action.js +15 -1
  126. package/src/runs/foreground/async-stop-action.js.map +1 -1
  127. package/src/runs/foreground/execution.d.ts.map +1 -1
  128. package/src/runs/foreground/execution.js +8 -1
  129. package/src/runs/foreground/execution.js.map +1 -1
  130. package/src/runs/foreground/subagent-executor.d.ts +6 -5
  131. package/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  132. package/src/runs/foreground/subagent-executor.js +293 -182
  133. package/src/runs/foreground/subagent-executor.js.map +1 -1
  134. package/src/runs/shared/child-launch.d.ts +5 -1
  135. package/src/runs/shared/child-launch.d.ts.map +1 -1
  136. package/src/runs/shared/child-launch.js +15 -1
  137. package/src/runs/shared/child-launch.js.map +1 -1
  138. package/src/runs/shared/child-session.d.ts +9 -0
  139. package/src/runs/shared/child-session.d.ts.map +1 -1
  140. package/src/runs/shared/child-session.js +90 -4
  141. package/src/runs/shared/child-session.js.map +1 -1
  142. package/src/runs/shared/child-tool-plan.d.ts +4 -0
  143. package/src/runs/shared/child-tool-plan.d.ts.map +1 -1
  144. package/src/runs/shared/child-tool-plan.js +11 -4
  145. package/src/runs/shared/child-tool-plan.js.map +1 -1
  146. package/src/runs/shared/dynamic-fanout.js +1 -1
  147. package/src/runs/shared/dynamic-fanout.js.map +1 -1
  148. package/src/runs/shared/mcp-config-sources.d.ts +1 -0
  149. package/src/runs/shared/mcp-config-sources.d.ts.map +1 -1
  150. package/src/runs/shared/mcp-config-sources.js +1 -1
  151. package/src/runs/shared/mcp-config-sources.js.map +1 -1
  152. package/src/runs/shared/mcp-direct-tool-allowlist.d.ts +28 -0
  153. package/src/runs/shared/mcp-direct-tool-allowlist.d.ts.map +1 -1
  154. package/src/runs/shared/mcp-direct-tool-allowlist.js +78 -2
  155. package/src/runs/shared/mcp-direct-tool-allowlist.js.map +1 -1
  156. package/src/runs/shared/model-resolution.d.ts +10 -0
  157. package/src/runs/shared/model-resolution.d.ts.map +1 -1
  158. package/src/runs/shared/model-resolution.js +30 -9
  159. package/src/runs/shared/model-resolution.js.map +1 -1
  160. package/src/runs/shared/model-scope.d.ts +2 -1
  161. package/src/runs/shared/model-scope.d.ts.map +1 -1
  162. package/src/runs/shared/model-scope.js +26 -10
  163. package/src/runs/shared/model-scope.js.map +1 -1
  164. package/src/runs/shared/parallel-utils.d.ts +2 -0
  165. package/src/runs/shared/parallel-utils.d.ts.map +1 -1
  166. package/src/runs/shared/parallel-utils.js.map +1 -1
  167. package/src/runs/shared/pi-spawn.d.ts +1 -10
  168. package/src/runs/shared/pi-spawn.d.ts.map +1 -1
  169. package/src/runs/shared/pi-spawn.js +7 -86
  170. package/src/runs/shared/pi-spawn.js.map +1 -1
  171. package/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
  172. package/src/runs/shared/subagent-prompt-runtime.js +2 -0
  173. package/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
  174. package/src/runs/shared/worktree.d.ts +1 -1
  175. package/src/runs/shared/worktree.d.ts.map +1 -1
  176. package/src/runs/shared/worktree.js +1 -1
  177. package/src/runs/shared/worktree.js.map +1 -1
  178. package/src/shared/disabled-features.d.ts +92 -0
  179. package/src/shared/disabled-features.d.ts.map +1 -0
  180. package/src/shared/disabled-features.js +113 -0
  181. package/src/shared/disabled-features.js.map +1 -0
  182. package/src/shared/extension-context.d.ts +9 -0
  183. package/src/shared/extension-context.d.ts.map +1 -1
  184. package/src/shared/extension-context.js +7 -0
  185. package/src/shared/extension-context.js.map +1 -1
  186. package/src/shared/settings.d.ts +1 -1
  187. package/src/shared/types.d.ts +20 -0
  188. package/src/shared/types.d.ts.map +1 -1
  189. package/src/shared/types.js.map +1 -1
  190. package/src/slash/prompt-template-bridge.js +2 -2
  191. package/src/slash/prompt-template-bridge.js.map +1 -1
  192. package/src/slash/slash-commands.d.ts +2 -0
  193. package/src/slash/slash-commands.d.ts.map +1 -1
  194. package/src/slash/slash-commands.js +2 -2
  195. package/src/slash/slash-commands.js.map +1 -1
  196. package/src/tui/render.d.ts.map +1 -1
  197. package/src/tui/render.js +55 -17
  198. package/src/tui/render.js.map +1 -1
  199. package/src/watchdog/diff-tool.d.ts +3 -1
  200. package/src/watchdog/diff-tool.d.ts.map +1 -1
  201. package/src/watchdog/diff-tool.js +20 -11
  202. package/src/watchdog/diff-tool.js.map +1 -1
  203. package/src/watchdog/register-child.js +2 -2
  204. package/src/watchdog/register-child.js.map +1 -1
  205. package/src/watchdog/register-main.js +2 -2
  206. package/src/watchdog/register-main.js.map +1 -1
  207. package/src/watchdog/review.d.ts +1 -1
  208. package/src/watchdog/review.d.ts.map +1 -1
  209. package/src/watchdog/review.js +1 -1
  210. package/src/watchdog/review.js.map +1 -1
  211. package/src/watchdog/turn-delta.js +1 -1
  212. package/src/watchdog/turn-delta.js.map +1 -1
  213. package/src/workflows/chat-progress.js +2 -2
  214. package/src/workflows/chat-progress.js.map +1 -1
  215. package/src/workflows/scripted-workflow.d.ts +8 -0
  216. package/src/workflows/scripted-workflow.d.ts.map +1 -1
  217. package/src/workflows/scripted-workflow.js +10 -2
  218. package/src/workflows/scripted-workflow.js.map +1 -1
  219. package/src/workflows/structured-workflow-scripts.d.ts +10 -0
  220. package/src/workflows/structured-workflow-scripts.d.ts.map +1 -0
  221. package/src/workflows/structured-workflow-scripts.js +166 -0
  222. package/src/workflows/structured-workflow-scripts.js.map +1 -0
  223. package/src/workflows/workflow-receipt.d.ts.map +1 -1
  224. package/src/workflows/workflow-receipt.js +8 -2
  225. package/src/workflows/workflow-receipt.js.map +1 -1
  226. package/src/workflows/workflow-resources.d.ts +9 -0
  227. package/src/workflows/workflow-resources.d.ts.map +1 -1
  228. package/src/workflows/workflow-resources.js +39 -12
  229. package/src/workflows/workflow-resources.js.map +1 -1
  230. package/src/workflows/workflow-reuse.d.ts +51 -0
  231. package/src/workflows/workflow-reuse.d.ts.map +1 -0
  232. package/src/workflows/workflow-reuse.js +103 -0
  233. package/src/workflows/workflow-reuse.js.map +1 -0
  234. package/src/workflows/workflow-revival.d.ts +32 -0
  235. package/src/workflows/workflow-revival.d.ts.map +1 -0
  236. package/src/workflows/workflow-revival.js +111 -0
  237. package/src/workflows/workflow-revival.js.map +1 -0
package/docs/workflows.md CHANGED
@@ -27,6 +27,8 @@ Stop and report the exact failure, run/status, and repository/cwd/worktree/branc
27
27
 
28
28
  Failed workflow details and async `status.json` include `workflow.failureKind` as `validation`, `script`, `child`, `return-serialization`, `timeout`, `detached-child`, or `runtime`. `validation` means the host rejected the script before it ran (syntax or portability) or at completion (unawaited calls); errors thrown into a running script, including rejected `runs.run` parameters and runtime `SyntaxError`s, are `script` because the script could catch them, and failed children are `child`. `runtime` covers host setup and infrastructure failures, such as an unavailable cwd or a crashed worker. A workflow that is stopped or reloaded is not a failure and has no `failureKind`.
29
29
 
30
+ When `/reload`, a session resume, or a pi-web project switch replaces the extension runtime, a running async workflow stops with `workflow.stopCause: "runtime-replaced"` in `status.json`, and its awaited async children keep running. When the same workflow script is launched again with the same `args` in that session, a `runs.run` with the same key and params returns a child that already finished successfully without launching it, and waits for a child that is still running instead of starting another. Those results carry `reused: true`, their step and trace entry are marked `reused`, and the new run records `workflow.reusedFrom`. Failed and stopped children, and children that ran in-process, launch again. Reuse applies only when the newest run of that script and args in the session was stopped this way; a user stop or a completed run ends it.
31
+
30
32
  Pi core may print a generic `pi -ne` extension-load hint; that out-of-repo hint is not protocol-approved fallback. A verified compaction abort may continue the retained child once on its already resolved model; it does not authorize an execution-mode or model switch.
31
33
 
32
34
  ## Prompt shortcuts
@@ -43,9 +45,17 @@ The package includes reusable prompt templates for common workflows. You do not
43
45
 
44
46
  Add `autofix` to `/parallel-review` or `/parallel-cleanup` to apply only the synthesized fixes worth doing now after reviewers return.
45
47
 
46
- ## Scripted workflows (workflowScript)
48
+ ## Scripted workflows
49
+
50
+ Use direct `{ agent, task }` for one bounded child. Use a workflow script when the parent needs a stable keyed child, sequence, fanout, steering, retry, or aggregation. For ordinary parallel fanout, use `await runs.all([{ key, agent, task }, ...])`. It resolves to an ordered array, not a key map, so use indexes, destructuring, or `.map(...)`, not `results.<key>`. 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.
51
+
52
+ The `workflow` field selects the script source:
47
53
 
48
- Use direct `{ agent, task }` for one bounded child. Use `workflowScript` when the parent needs a stable keyed child, sequence, fanout, steering, retry, or aggregation. For ordinary parallel fanout, use `await runs.all([{ key, agent, task }, ...])`. It resolves to an ordered array, not a key map, so use indexes, destructuring, or `.map(...)`, not `results.<key>`. 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:
54
+ - `workflow: true` runs the one ```` ```js workflow ```` fenced block written in the same assistant reply as the `subagent` call. The script is plain text in the reply, so it needs no JSON string escaping. A reply can carry exactly one such block and one `workflow: true` call; zero or several blocks fail. A line containing only ```` ``` ```` (three or more backticks, optionally followed by spaces or tabs) ends the block, so keep Markdown fences inside quoted strings.
55
+ - A string containing `/` (or `\` in a Windows path), such as `workflow: "./workflows/review.js"`, is a script file.
56
+ - Any other string, such as `workflow: "review"`, is a [named workflow resource](#named-workflow-resources-for-permission-extensions).
57
+
58
+ `workflow: true` works only from a model tool call. Slash commands, RPC, and schedules pass scripts through their own inputs. Examples below that show only a ```` ```js workflow ```` block run with `subagent({ workflow: true })` in the same reply.
49
59
 
50
60
  For multi-step or parallel work, make exactly one top-level `subagent` workflow call with `async:true` and launch children only inside it. Read this guide for recipes rather than constructing a second top-level orchestration. Available sandbox helpers include `runs.run`, `runs.all`, `runs.lanes`, `runs.steer`, `runs.status`, `runs.ref`/`runs.refs`, `emit`, `console`, standard JavaScript, and mission `state` when enabled. No filesystem, shell, arbitrary Pi tools, or host globals are available; named resources alone may grant `runs.host` authority.
51
61
 
@@ -59,26 +69,28 @@ A workflow can finish dispatch while these children remain running. Its summary
59
69
 
60
70
  Validate a script without launching children:
61
71
 
72
+ ```js workflow
73
+ const results = await runs.all([{ key: "scan", agent: "scout", task: "Scan" }]);
74
+ return results[0].output;
75
+ ```
76
+
62
77
  ```js
63
- subagent({ action: "validate", workflowScript: `
64
- const results = await runs.all([{ key: "scan", agent: "scout", task: "Scan" }]);
65
- return results[0].output;
66
- ` });
78
+ subagent({ action: "validate", workflow: true });
67
79
  ```
68
80
 
69
- For a script stored in a file, use `workflowScriptPath` instead of `workflowScript`:
81
+ For a script stored in a file, pass its path:
70
82
 
71
83
  ```js
72
- subagent({ workflowScriptPath: "workflows/review.js", cwd: "/path/to/project" });
73
- subagent({ action: "validate", workflowScriptPath: "workflows/review.js" });
84
+ subagent({ workflow: "./workflows/review.js", cwd: "/path/to/project" });
85
+ subagent({ action: "validate", workflow: "./workflows/review.js" });
74
86
  ```
75
87
 
76
- The fields are mutually exclusive. Relative paths resolve against the request `cwd`; absolute paths pass through. The host reads the file before validation, schedule creation, or workflow sandbox execution. The sandbox still has no filesystem access. Missing, unreadable, and empty files return file input errors instead of script syntax errors.
88
+ Relative paths resolve against the request `cwd`; absolute paths pass through. The host reads the file before validation, schedule creation, or workflow sandbox execution. The sandbox still has no filesystem access. Missing, unreadable, and empty files return file input errors instead of script syntax errors.
77
89
 
78
- Inline and file-backed scripts accept bounded plain-JSON `args`:
90
+ Reply-block and file-backed scripts accept bounded plain-JSON `args`:
79
91
 
80
92
  ```js
81
- subagent({ workflowScriptPath: "workflows/review.js", args: { target: "src/workflows" } });
93
+ subagent({ workflow: "./workflows/review.js", args: { target: "src/workflows" } });
82
94
  // workflows/review.js
83
95
  return runs.run("review", { agent: "reviewer", task: `Review ${args.target}` });
84
96
  ```
@@ -94,18 +106,20 @@ subagent({ workflow: "review", args: { task: "Review the change" } });
94
106
  subagent({ workflow: "run-ci", args: { command: "npm test" } });
95
107
  ```
96
108
 
97
- The host resolves the name and validates bounded plain-JSON `args` before starting the workflow. Resource provenance is recorded in workflow details and receipts for downstream permission/policy checks. Resource authority is not caller-supplied: `runs.host` is available only when the resolved resource explicitly grants the requested host key and command. Inline `workflowScript` and `workflowScriptPath` remain raw, unknown-provenance inputs, so their `runs.host` calls are unavailable through the public execution boundary. Named resources cannot be combined with `agent`, `task`, `workflowScript`, or `workflowScriptPath`; this first slice ships only the package-owned `review` and `run-ci` resources, not a user/project resource registry.
109
+ The host resolves the name and validates bounded plain-JSON `args` before starting the workflow. Resource provenance is recorded in workflow details and receipts for downstream permission/policy checks. Resource authority is not caller-supplied: `runs.host` is available only when the resolved resource explicitly grants the requested host key and command. Reply-block (`workflow: true`) and file-path scripts remain raw, unknown-provenance inputs, so their `runs.host` calls are unavailable through the public execution boundary. Named resources cannot be combined with `agent` or `task`; this first slice ships only the package-owned `review` and `run-ci` resources, not a user/project resource registry.
98
110
 
99
111
  ### Opt-in bounded workflows
100
112
 
101
113
  Composite workflows have no default parent deadline. Add bounds only when the workflow contract calls for them:
102
114
 
115
+ ```js workflow
116
+ const scan = await runs.run("scan", { agent: "scout", task: "Inspect the named files." });
117
+ return runs.run("review", { agent: "reviewer", task: "Review:\n" + scan.output });
118
+ ```
119
+
103
120
  ```js
104
121
  subagent({
105
- workflowScript: `
106
- const scan = await runs.run("scan", { agent: "scout", task: "Inspect the named files." });
107
- return runs.run("review", { agent: "reviewer", task: "Review:\n" + scan.output });
108
- `,
122
+ workflow: true,
109
123
  timeoutMs: 900000,
110
124
  toolBudget: { soft: 40, hard: 60 },
111
125
  usageBudget: { tokens: { soft: 100000, hard: 150000 } }
@@ -120,80 +134,76 @@ subagent({
120
134
 
121
135
  These controls are opt-in. Avoid tight hard budgets for mutation-capable workers unless the workflow has an explicit checkpoint and handoff path.
122
136
 
123
- The optional `preflight` lane hints apply only to raw `workflowScript` or `workflowScriptPath` requests, not named workflows or direct child calls. They are display-only: mismatched coverage warns but never changes execution authority.
137
+ The optional `preflight` lane hints apply only to raw scripts (`workflow: true` or a script path), not named workflows or direct child calls. They are display-only: mismatched coverage warns but never changes execution authority.
124
138
 
125
139
  The result is `{ ok, errors }`. Invalid scripts return a tool error and include line and column data when available. Validation checks syntax, portable nested-async rules, literal `runs.run` and `runs.all` keys and child `baseRef` values, duplicate literal keys in one `runs.all` group, direct keyed access to a known `runs.all` result, and statically clear non-JSON boundary values. It also looks up literal `agent` names in `runs.run`, `runs.all`, and `runs.lanes` children against the agents discovered for the request `cwd` and `agentScope`, and reports unknown or ambiguous names with a close match when one exists. Children with their own `cwd`, `agentScope`, or `resume`, object spreads, and names built at runtime are left to launch time. Dynamic keys and other runtime-only values are accepted without a warning. Validation does not launch children or create run artifacts. Executing a workflow runs the same agent-name check first, so an unknown literal agent fails before any child launches.
126
140
 
141
+ ```js workflow
142
+ const scan = await runs.run("scan", { label: "Map codebase behavior", agent: "scout", task: "Scan the codebase" });
143
+ const reviews = await runs.all([
144
+ { key: "correctness", label: "Review codebase correctness", agent: "reviewer", task: "Review correctness: " + scan.output },
145
+ { key: "tests", label: "Review test coverage", agent: "reviewer", task: "Review tests: " + scan.output }
146
+ ]);
147
+ return reviews.map(result => result.output);
148
+ ```
149
+
127
150
  ```js
128
- subagent({ workflowScript: `
129
- const scan = await runs.run("scan", { label: "Map codebase behavior", agent: "scout", task: "Scan the codebase" });
130
- const reviews = await runs.all([
131
- { key: "correctness", label: "Review codebase correctness", agent: "reviewer", task: "Review correctness: " + scan.output },
132
- { key: "tests", label: "Review test coverage", agent: "reviewer", task: "Review tests: " + scan.output }
133
- ]);
134
- return reviews.map(result => result.output);
135
- ` });
151
+ subagent({ workflow: true });
136
152
  ```
137
153
 
138
- 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.
154
+ 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 a workflow script; native async helpers hide child-launch observation in Bun and are rejected.
139
155
 
140
- ```js
141
- subagent({ workflowScript: `
142
- function scan() {
143
- return runs.run("scan", { label: "Map codebase behavior", agent: "scout", task: "Scan the codebase" });
144
- }
145
- const result = await scan();
146
- return result.output;
147
- ` });
156
+ ```js workflow
157
+ function scan() {
158
+ return runs.run("scan", { label: "Map codebase behavior", agent: "scout", task: "Scan the codebase" });
159
+ }
160
+ const result = await scan();
161
+ return result.output;
148
162
  ```
149
163
 
150
164
  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.
151
165
 
152
- ```js
153
- subagent({ workflowScript: `
154
- const plan = await runs.run("plan", { label: "Plan migration behavior", agent: "scout", task: "Plan the migration" });
155
- const patch = await runs.run("patch", { label: "Implement migration behavior", agent: "worker", task: "Implement this plan:\n" + plan.output });
156
- return patch.output;
157
- ` });
166
+ ```js workflow
167
+ const plan = await runs.run("plan", { label: "Plan migration behavior", agent: "scout", task: "Plan the migration" });
168
+ const patch = await runs.run("patch", { label: "Implement migration behavior", agent: "worker", task: "Implement this plan:\n" + plan.output });
169
+ return patch.output;
158
170
  ```
159
171
 
160
172
  ### Parallel sequential lanes
161
173
 
162
- For a bounded set of independent chains, `runs.lanes(...)` removes the mechanical loop that would otherwise connect each lane's stages. It is a helper inside `workflowScript`, not a new top-level `subagent` execution mode:
163
-
164
- ```js
165
- subagent({ workflowScript: `
166
- const board = await runs.lanes([
167
- {
168
- key: "api",
169
- stages: [
170
- { key: "writer", label: "Implement API behavior", agent: "worker", task: "Implement the API change" },
171
- { key: "challenge", label: "Challenge API behavior", resume: "previous", task: "Challenge the API implementation" },
172
- { key: "review", label: "Review API behavior", agent: "reviewer", task: "Review the API lane" }
173
- ]
174
- },
175
- {
176
- key: "ui",
177
- stages: [
178
- { key: "writer", label: "Implement UI behavior", agent: "worker", task: "Implement the UI change" },
179
- { key: "review", label: "Review UI behavior", agent: "reviewer", task: "Review the UI lane" }
180
- ]
181
- }
182
- ]);
183
- return board.map((lane) => ({
184
- key: lane.key,
185
- state: lane.state,
186
- failedStage: lane.failedStage,
187
- stages: lane.stages.map((stage) => ({
188
- key: stage.key,
189
- state: stage.state,
190
- ok: stage.ok,
191
- runId: stage.runId,
192
- outputReference: stage.outputReference,
193
- verdict: stage.verdict
194
- }))
195
- }));
196
- ` });
174
+ For a bounded set of independent chains, `runs.lanes(...)` removes the mechanical loop that would otherwise connect each lane's stages. It is a helper inside a workflow script, not a new top-level `subagent` execution mode:
175
+
176
+ ```js workflow
177
+ const board = await runs.lanes([
178
+ {
179
+ key: "api",
180
+ stages: [
181
+ { key: "writer", label: "Implement API behavior", agent: "worker", task: "Implement the API change" },
182
+ { key: "challenge", label: "Challenge API behavior", resume: "previous", task: "Challenge the API implementation" },
183
+ { key: "review", label: "Review API behavior", agent: "reviewer", task: "Review the API lane" }
184
+ ]
185
+ },
186
+ {
187
+ key: "ui",
188
+ stages: [
189
+ { key: "writer", label: "Implement UI behavior", agent: "worker", task: "Implement the UI change" },
190
+ { key: "review", label: "Review UI behavior", agent: "reviewer", task: "Review the UI lane" }
191
+ ]
192
+ }
193
+ ]);
194
+ return board.map((lane) => ({
195
+ key: lane.key,
196
+ state: lane.state,
197
+ failedStage: lane.failedStage,
198
+ stages: lane.stages.map((stage) => ({
199
+ key: stage.key,
200
+ state: stage.state,
201
+ ok: stage.ok,
202
+ runId: stage.runId,
203
+ outputReference: stage.outputReference,
204
+ verdict: stage.verdict
205
+ }))
206
+ }));
197
207
  ```
198
208
 
199
209
  The first stage from every lane is launched in one existing `runs.all(...)` batch. Later stages in each lane start only after the preceding stage settles. A later stage with `resume: "previous"` requires the preceding child to return a retained `runId`; the helper then uses the existing retained-resume launch checks and does not accept an arbitrary run id. Generated child keys use `<lane>.<stage>`, while the returned board uses the local lane and stage keys.
@@ -208,18 +218,16 @@ Two ways to put a small classifier (a script, a lookup, a fast evaluation model)
208
218
 
209
219
  **A typed gate** runs a command after a child finishes and turns its JSON stdout into that child's `structuredOutput`:
210
220
 
211
- ```js
212
- subagent({ workflowScript: `
213
- const review = await runs.run("review", {
214
- agent: "reviewer", task: packet,
215
- output: "reports/review.md", outputMode: "file-only",
216
- gate: { command: "classify --report reports/review.md", output: "json" }
217
- });
218
- if (review.structuredOutput.verdict === "blocked") {
219
- return runs.run("fix", { agent: "worker", task: "Fix the findings in reports/review.md" });
220
- }
221
- return { verdict: review.structuredOutput.verdict, report: "reports/review.md" };
222
- ` });
221
+ ```js workflow
222
+ const review = await runs.run("review", {
223
+ agent: "reviewer", task: args.packet,
224
+ output: "reports/review.md", outputMode: "file-only",
225
+ gate: { command: "classify --report reports/review.md", output: "json" }
226
+ });
227
+ if (review.structuredOutput.verdict === "blocked") {
228
+ return runs.run("fix", { agent: "worker", task: "Fix the findings in reports/review.md" });
229
+ }
230
+ return { verdict: review.structuredOutput.verdict, report: "reports/review.md" };
223
231
  ```
224
232
 
225
233
  The parent receives a pointer plus a verdict instead of the review text. See [typed gates](tool-reference.md#typed-gates) for the contract and failure rules.
@@ -264,13 +272,11 @@ The first named resource version supports only `npm test` and `npm run typecheck
264
272
 
265
273
  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`.
266
274
 
267
- ```js
268
- subagent({ workflowScript: `
269
- const writer = runs.run("writer", { agent: "worker", task: "Implement the change" });
270
- const evidence = await runs.run("evidence", { agent: "scout", task: "Find the exact contract" });
271
- const receipt = await runs.steer("writer", "Also check: " + evidence.output, { mode: "follow_up" });
272
- return { writer: await writer, receipt };
273
- ` });
275
+ ```js workflow
276
+ const writer = runs.run("writer", { agent: "worker", task: "Implement the change" });
277
+ const evidence = await runs.run("evidence", { agent: "scout", task: "Find the exact contract" });
278
+ const receipt = await runs.steer("writer", "Also check: " + evidence.output, { mode: "follow_up" });
279
+ return { writer: await writer, receipt };
274
280
  ```
275
281
 
276
282
  The receipt state is `queued`, `delivered`, `missed`, or `failed`. For an async child, `delivered` means it consumed the correlated user input; for a foreground child, it means the in-process Pi transport 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.
@@ -281,111 +287,99 @@ Always await or return a `runs.steer` promise. The workflow waits for an observe
281
287
 
282
288
  `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.
283
289
 
284
- ```js
285
- subagent({ workflowScript: `
286
- let pending = [
287
- { key: "analysis-a", promise: runs.run("analysis-a", { agent: "reviewer", task: "Analyze option A" }).then((result) => ({ key: "analysis-a", result })) },
288
- { key: "analysis-b", promise: runs.run("analysis-b", { agent: "reviewer", task: "Analyze option B" }).then((result) => ({ key: "analysis-b", result })) },
289
- { key: "critic", promise: runs.run("critic", { agent: "reviewer", task: "Find the strongest objection" }).then((result) => ({ key: "critic", result })) }
290
- ];
290
+ ```js workflow
291
+ let pending = [
292
+ { key: "analysis-a", promise: runs.run("analysis-a", { agent: "reviewer", task: "Analyze option A" }).then((result) => ({ key: "analysis-a", result })) },
293
+ { key: "analysis-b", promise: runs.run("analysis-b", { agent: "reviewer", task: "Analyze option B" }).then((result) => ({ key: "analysis-b", result })) },
294
+ { key: "critic", promise: runs.run("critic", { agent: "reviewer", task: "Find the strongest objection" }).then((result) => ({ key: "critic", result })) }
295
+ ];
291
296
 
292
- const first = await Promise.race(pending.map((child) => child.promise));
293
- pending = pending.filter((child) => child.key !== first.key);
297
+ const first = await Promise.race(pending.map((child) => child.promise));
298
+ pending = pending.filter((child) => child.key !== first.key);
294
299
 
295
- const target = pending.find((child) => child.key === "critic") ?? pending[0];
296
- const receipt = await runs.steer(target.key, "Challenge this early result:\n" + first.result.output, { mode: "auto" });
297
- const rest = await Promise.all(pending.map((child) => child.promise));
300
+ const target = pending.find((child) => child.key === "critic") ?? pending[0];
301
+ const receipt = await runs.steer(target.key, "Challenge this early result:\n" + first.result.output, { mode: "auto" });
302
+ const rest = await Promise.all(pending.map((child) => child.promise));
298
303
 
299
- return { first: first.result.output, rest: rest.map((child) => child.result.output), receipt };
300
- ` });
304
+ return { first: first.result.output, rest: rest.map((child) => child.result.output), receipt };
301
305
  ```
302
306
 
303
307
  The workflow trace records the run completions and steering receipt. Scripts cannot access the filesystem or control inboxes; returned run and artifact references are data only. 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.
304
308
 
305
309
  Use named outputs when later workflow steps need structured data or durable references:
306
310
 
307
- ```js
308
- subagent({ workflowScript: `
309
- const inventory = await runs.run("inventory", {
310
- agent: "scout",
311
- task: "List the files that need review.",
312
- outputSchema: {
313
- type: "object",
314
- properties: { files: { type: "array", items: { type: "string" } } },
315
- required: ["files"],
316
- additionalProperties: false
317
- }
318
- });
319
- return runs.run("review", {
320
- agent: "reviewer",
321
- task: "Review these files: " + inventory.structuredOutput.files.join(", ")
322
- });
323
- ` });
311
+ ```js workflow
312
+ const inventory = await runs.run("inventory", {
313
+ agent: "scout",
314
+ task: "List the files that need review.",
315
+ outputSchema: {
316
+ type: "object",
317
+ properties: { files: { type: "array", items: { type: "string" } } },
318
+ required: ["files"],
319
+ additionalProperties: false
320
+ }
321
+ });
322
+ return runs.run("review", {
323
+ agent: "reviewer",
324
+ task: "Review these files: " + inventory.structuredOutput.files.join(", ")
325
+ });
324
326
  ```
325
327
 
326
328
  For dynamic fanout, have one step return a structured list, check it in JavaScript, then map the bounded entries into `runs.all(...)`:
327
329
 
328
- ```js
329
- subagent({ workflowScript: `
330
- const targets = await runs.run("targets", {
331
- agent: "scout",
332
- task: "Return up to five source files that need review.",
333
- outputSchema: {
334
- type: "object",
335
- properties: { files: { type: "array", items: { type: "string" }, maxItems: 5 } },
336
- required: ["files"],
337
- additionalProperties: false
338
- }
339
- });
340
- const files = targets.structuredOutput.files.slice(0, 5);
341
- return runs.all(files.map((file, index) => ({
342
- key: "review-" + index,
343
- agent: "reviewer",
344
- task: "Review " + file
345
- })));
346
- ` });
330
+ ```js workflow
331
+ const targets = await runs.run("targets", {
332
+ agent: "scout",
333
+ task: "Return up to five source files that need review.",
334
+ outputSchema: {
335
+ type: "object",
336
+ properties: { files: { type: "array", items: { type: "string" }, maxItems: 5 } },
337
+ required: ["files"],
338
+ additionalProperties: false
339
+ }
340
+ });
341
+ const files = targets.structuredOutput.files.slice(0, 5);
342
+ return runs.all(files.map((file, index) => ({
343
+ key: "review-" + index,
344
+ agent: "reviewer",
345
+ task: "Review " + file
346
+ })));
347
347
  ```
348
348
 
349
349
  For intermediate data that only later steps need, prefer the prior child's returned output or `structuredOutput` instead of writing shared files:
350
350
 
351
- ```js
352
- subagent({ workflowScript: `
353
- const scan = await runs.run("scan", { agent: "scout", task: "Find the files that need fixes." });
354
- return runs.run("fix", { agent: "worker", task: "Implement these findings:\n" + scan.output });
355
- ` });
351
+ ```js workflow
352
+ const scan = await runs.run("scan", { agent: "scout", task: "Find the files that need fixes." });
353
+ return runs.run("fix", { agent: "worker", task: "Implement these findings:\n" + scan.output });
356
354
  ```
357
355
 
358
356
  `{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.
359
357
 
360
358
  ### Migrating old chain shapes
361
359
 
362
- 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:
360
+ 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. The exception is `disabledFeatures: ["workflow-scripts"]`, which replaces workflow scripts with top-level `chain` and `tasks` (see [configuration](configuration.md#chain-and-tasks-without-workflow-scripts)).
363
361
 
364
- ```js
362
+ ```js workflow
365
363
  // Old shape, no longer supported:
366
364
  // { chain: [{ agent: "scout", task: "Scan" }, { agent: "worker", task: "Fix from {previous}" }] }
367
365
 
368
- // Current shape:
369
- { workflowScript: `
370
- const scan = await runs.run("scan", { agent: "scout", task: "Scan" });
371
- return runs.run("fix", { agent: "worker", task: "Fix from: " + scan.output });
372
- ` }
366
+ // Current shape: this ```js workflow block plus subagent({ workflow: true })
367
+ const scan = await runs.run("scan", { agent: "scout", task: "Scan" });
368
+ return runs.run("fix", { agent: "worker", task: "Fix from: " + scan.output });
373
369
  ```
374
370
 
375
- ```js
371
+ ```js workflow
376
372
  // Old shape, no longer supported:
377
373
  // { tasks: [{ agent: "reviewer", task: "Review API" }, { agent: "reviewer", task: "Review UI" }] }
378
374
 
379
- // Current shape:
380
- { workflowScript: `
381
- return runs.all([
382
- { key: "api", agent: "reviewer", task: "Review API" },
383
- { key: "ui", agent: "reviewer", task: "Review UI" }
384
- ]);
385
- ` }
375
+ // Current shape: this ```js workflow block plus subagent({ workflow: true })
376
+ return runs.all([
377
+ { key: "api", agent: "reviewer", task: "Review API" },
378
+ { key: "ui", agent: "reviewer", task: "Review UI" }
379
+ ]);
386
380
  ```
387
381
 
388
- For long task text with Markdown fences or shell blocks, use quoted lines instead of a raw template literal:
382
+ For long task text with Markdown fences or shell blocks, use quoted lines instead of a raw template literal. In a reply block, a line containing only ```` ``` ```` would end the block:
389
383
 
390
384
  ````js
391
385
  const task = [
@@ -403,7 +397,7 @@ A plain workflow creates one enclosing mission by default. Its children do not c
403
397
 
404
398
  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.
405
399
 
406
- 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.
400
+ 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 a workflow script, so templates describe the work instead of embedding raw `subagent` tool calls. You can ask the parent agent to create or update these prompt files from a process described in natural language.
407
401
 
408
402
  ```md
409
403
  ---
@@ -450,11 +444,11 @@ return { api: api.artifactPaths, ui: ui.artifactPaths };
450
444
 
451
445
  Each child uses the existing worktree lifecycle: it branches from clean HEAD, journals ownership before launch, captures a patch and handoff manifest, then removes cleanly captured temporary worktrees and branches. The handoff manifest path remains available in the child's `artifactPaths`; return or emit it when the orchestrator needs to apply or inspect the patches. `runs.ref` stays concise and intentionally omits full paths.
452
446
 
453
- A top-level `{ workflowScript, worktree: true }` makes isolation the default for every workflow child. An individual child can override that default with `worktree: false`. Keep one writer when parallel writes are not intentionally isolated.
447
+ A top-level `{ workflow: true, worktree: true }` makes isolation the default for every workflow child. An individual child can override that default with `worktree: false`. Keep one writer when parallel writes are not intentionally isolated.
454
448
 
455
449
  Before a materialized `runs.run` or `runs.all` group dispatches fresh children, isolated sources must be Git repositories with clean working trees (excluding `.pi/subagents/` runtime state). A rejected group dispatches no children and spends no fan-out slots or child output claims; key-level failure traces can remain. Checks are shared only within that group, are cancellable, and run again at allocation because sources can change. Retained resumes keep their stored contracts. Select the correct cwd or arrange an operator-approved commit/stash; isolation is never dropped automatically.
456
450
 
457
- Use `baseRef` to branch managed worktrees from `HEAD` or a supported named ref such as `refs/heads/release`, `refs/tags/v1`, or `origin/main`. Full 40/64-character commit IDs and revision expressions such as `HEAD~1` are unsupported. For example, `{ workflowScript, worktree: true, baseRef: "refs/heads/release" }` applies the release ref to children unless a child supplies its own `baseRef`. If omitted, the default `HEAD` is resolved at worktree allocation, not when the script is validated or a schedule is created. The source checkout must still be clean, and the ref must resolve to a commit before any worktree is allocated.
451
+ Use `baseRef` to branch managed worktrees from `HEAD` or a supported named ref such as `refs/heads/release`, `refs/tags/v1`, or `origin/main`. Full 40/64-character commit IDs and revision expressions such as `HEAD~1` are unsupported. For example, `{ workflow: true, worktree: true, baseRef: "refs/heads/release" }` applies the release ref to children unless a child supplies its own `baseRef`. If omitted, the default `HEAD` is resolved at worktree allocation, not when the script is validated or a schedule is created. The source checkout must still be clean, and the ref must resolve to a commit before any worktree is allocated.
458
452
 
459
453
  Configure the worktree provider, native path layout, base directory, and setup hook in [configuration.md](configuration.md).
460
454
 
@@ -523,7 +517,7 @@ For A → B → C, C's request belongs to B, not A. B can escalate a separate qu
523
517
 
524
518
  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.
525
519
 
526
- 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.
520
+ If a workflow script 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.
527
521
 
528
522
  If messages do not show up, run `/subagents-doctor`. Advanced users can tune the bridge with `intercomBridge` in [configuration.md](configuration.md).
529
523
 
@@ -571,4 +565,4 @@ Then run it through the native adapter:
571
565
 
572
566
  The adapter delegates to the named subagent, applies `model`, `skill`, `cwd`, and fork/fresh context metadata, and supports runtime overrides such as `--subagent reviewer`, `--fork`, `--fresh`, and `--bg`.
573
567
 
574
- Prompt templates with `chain:` frontmatter are translated into `workflowScript` and launched through `/prompt-workflow`; `/chain-prompts` is no longer registered.
568
+ Prompt templates with `chain:` frontmatter are translated into a workflow script and launched through `/prompt-workflow`; `/chain-prompts` is no longer registered.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "pi-subagents",
4
- "version": "0.73.1",
4
+ "version": "0.74.0",
5
5
  "description": "Pi extension for single-agent delegation and scripted multi-agent workflows",
6
6
  "author": "Nico Bailon",
7
7
  "license": "MIT",
@@ -19,7 +19,7 @@
19
19
  "dependencies": {
20
20
  "acorn": "8.18.0",
21
21
  "jiti": "2.7.0",
22
- "undici": "8.10.0",
22
+ "undici": "8.10.2",
23
23
  "yaml": "2.8.3"
24
24
  },
25
25
  "peerDependencies": {
@@ -8,7 +8,7 @@ Use the `subagent` tool. Keep the parent session as the loop controller and fina
8
8
 
9
9
  Default to a maximum of 3 review rounds unless I specify a different cap. Count a review round each time fresh-context reviewers inspect the current diff after a worker pass. Stop early when reviewers find no P0 findings, no P1 fixes worth doing now, and no approved P2 notes that should be handled in this loop.
10
10
 
11
- If the invocation includes an implementation request, first launch one async `worker` to implement the approved scope. If the current diff is already the target, start with review. The sequence can be launched up front with `workflowScript` when it is already clear, or continued as follow-up single-agent runs after each async completion. For an initial workflowScript, pass `async: true` so the main chat is unblocked; do not set `clarify: true` unless I explicitly want the foreground clarify UI. Use only one writer against the active worktree at a time unless I explicitly ask for isolated worktrees.
11
+ If the invocation includes an implementation request, first launch one async `worker` to implement the approved scope. If the current diff is already the target, start with review. The sequence can be launched up front as a workflow script (a ```` ```js workflow ```` block plus `subagent({ workflow: true })`) when it is already clear, or continued as follow-up single-agent runs after each async completion. For an initial workflow script, pass `async: true` so the main chat is unblocked; do not set `clarify: true` unless I explicitly want the foreground clarify UI. Use only one writer against the active worktree at a time unless I explicitly ask for isolated worktrees.
12
12
 
13
13
  As a conservative orchestration policy, do not set a hard `toolBudget` or tight `usageBudget` on implementation or fix workers. A default tool budget blocks read/search tools rather than mutation tools, and reported usage has no reservation model, so count or usage limits still do not measure delivery safety. Give each writer a narrow delivery slice and an outer elapsed deadline with enough margin. Before that deadline, request a checkpoint after the current tool returns with changed files, build/test state, remaining work, and commit or PR state. An elapsed timeout is not a mutation-safe boundary and must not be the checkpoint trigger.
14
14
 
@@ -32,7 +32,7 @@ Pass 1 is independent reports. Pass 2 is one cross-exam. Run Pass 3 only when `-
32
32
 
33
33
  1. Write the council brief: question, scope, non-goals, evidence targets, roster, known advisor context modes, and pass cap.
34
34
  2. Tell the user the roster, context modes, and pass cap.
35
- 3. Launch one async `workflowScript` with `runs.all` for Pass 1. Use stable keys, `phase: "Council pass 1"`, concise labels, and `output: false` unless separate artifacts are useful. Set `context` only when the profile context is known or a fallback rule requires it.
35
+ 3. Launch one async workflow script (a ```` ```js workflow ```` block plus `subagent({ workflow: true, async: true })`) with `runs.all` for Pass 1. Use stable keys, `phase: "Council pass 1"`, concise labels, and `output: false` unless separate artifacts are useful. Set `context` only when the profile context is known or a fallback rule requires it.
36
36
  4. Return one aggregate Pass 1 receipt. On completion, tell the user completion count, agreement count, dispute count, and whether Pass 2 is needed.
37
37
  5. Synthesize the claim matrix in the parent: agreements, disputed claims, missing proof, owner decisions, and at most five material relay claims per advisor.
38
38
  6. For Pass 2, tell the user which claims are relayed and why they matter. Resume each advisor with a curated challenge packet. A resume needs a retained run id and task; it excludes `agent` and rejects `gate`. Record each new run id; Pass 3 resumes those latest ids with new stable keys.
@@ -29,19 +29,26 @@ and their resolved `tools` allow `subagent`.
29
29
  | Need | Use |
30
30
  | --- | --- |
31
31
  | One bounded task for one child | direct `{ agent, task }` |
32
- | JavaScript control flow or data-dependent branching; sequence, fanout, retry, rolling fanout, or aggregation | `workflowScript` with `runs.run(...)` / `runs.all(...)` |
33
- | A broad plan split into visible narrow stages per lane | `workflowScript` with `runs.lanes([{ key, stages: [...] }])` |
32
+ | JavaScript control flow or data-dependent branching; sequence, fanout, retry, rolling fanout, or aggregation | a workflow script with `runs.run(...)` / `runs.all(...)` |
33
+ | A broad plan split into visible narrow stages per lane | a workflow script with `runs.lanes([{ key, stages: [...] }])` |
34
34
  | Independent worktree or repository lanes | `references/multi-lane-orchestration.md` |
35
35
  | Council of advisors | `../council-mode/SKILL.md` |
36
36
  | Management, status, steering, authoring, or inspection | `action` |
37
37
 
38
- `workflowScript` is code-driven: `runs.run(...)` for keyed steps,
38
+ To launch a workflow script, write it as one ```` ```js workflow ```` fenced
39
+ block in your reply, then call `subagent({ workflow: true, ... })` in the same
40
+ reply. Use `workflow: "./path/to/script.js"` for a script file and
41
+ `workflow: "<name>"` for a named resource.
42
+
43
+ A workflow script is code-driven: `runs.run(...)` for keyed steps,
39
44
  `runs.all([...])` for fanout, plain JavaScript for branching and aggregation.
40
45
  Keep scripts portable: use top-level `await`, plain helpers, or explicit Promise
41
46
  chains, not nested async helpers. Legacy top-level `chain` / `tasks` inputs and
42
- durable `.chain.md` execution are inspection or migration material only.
47
+ durable `.chain.md` execution are inspection or migration material only, except
48
+ that `disabledFeatures: ["workflow-scripts"]` replaces scripts with top-level
49
+ `chain` and `tasks` (see [configuration](../../docs/configuration.md#chain-and-tasks-without-workflow-scripts)).
43
50
 
44
- Use `runs.lanes(...)` only inside a `workflowScript`, not as a top-level mode,
51
+ Use `runs.lanes(...)` only inside a workflow script, not as a top-level mode,
45
52
  when a broad, predeclared plan benefits from visible per-lane stages; otherwise
46
53
  use ordinary `runs.run(...)` / `runs.all(...)`. See the [canonical staged-lane
47
54
  example](../../docs/workflows.md#parallel-sequential-lanes). Keep assignments
@@ -34,8 +34,8 @@ For durable evidence, copy only the final summary to session memory, a PR body/c
34
34
 
35
35
  ## Best Practices
36
36
 
37
- - Run subagents asynchronously by default; direct one-child execution is enough for one bounded task, while `workflowScript` is the composition surface for JavaScript control flow and data-dependent branching. Use `async: false` only when the parent must block. See `references/execution-controls.md` → Async/background for wait semantics.
38
- - For a predeclared broad plan split into visible narrow stages, use `runs.lanes([...])` inside `workflowScript`; use raw `runs.run(...)`/`runs.all(...)` for conditional or rolling flows. See [`execution-controls.md`](execution-controls.md#parallel-sequential-lanes).
37
+ - Run subagents asynchronously by default; direct one-child execution is enough for one bounded task, while a workflow script (a ```` ```js workflow ```` block plus `subagent({ workflow: true })`) is the composition surface for JavaScript control flow and data-dependent branching. Use `async: false` only when the parent must block. See `references/execution-controls.md` → Async/background for wait semantics.
38
+ - For a predeclared broad plan split into visible narrow stages, use `runs.lanes([...])` inside a workflow script; use raw `runs.run(...)`/`runs.all(...)` for conditional or rolling flows. See [`execution-controls.md`](execution-controls.md#parallel-sequential-lanes).
39
39
  - Keep one writer per cwd/worktree. Parallelize reading, review, and validation; concurrent writers need isolated worktrees. Give every child a cold-start packet with its goal, target/ref, authority, context, success criteria, validation, output, and stop rules.
40
40
  - Keep tasks narrow and standalone; do not rely on issue numbers, broad globs, or supervisor round-trips to supply missing context.
41
41
  - Keep authority with the parent. Escalate unapproved product, scope, architecture, merge, credential, or release decisions; checks, receipts, and review bots are evidence, not authority.
@@ -64,7 +64,7 @@ its overhead. Recipes select a shape; they do not authorize delegation:
64
64
 
65
65
  ## Error Handling
66
66
 
67
- - **Unknown agent:** run `subagent({ action: "list" })`; check scope/precedence and author new orchestration with `workflowScript`, not legacy chains.
67
+ - **Unknown agent:** run `subagent({ action: "list" })`; check scope/precedence and author new orchestration as a workflow script, not legacy chains.
68
68
  - **Setup, discovery, or intercom confusion:** run `subagent({ action: "doctor" })`.
69
69
  - **Max subagent depth exceeded:** flatten the workflow or raise `maxSubagentDepth` in config.
70
70
  - **Missing session file for a fork:** persist the parent session before using `context: "fork"`.