pi-subagents 0.73.1 → 0.75.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 (299) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/README.md +1 -1
  3. package/docs/agents.md +30 -16
  4. package/docs/configuration.md +76 -9
  5. package/docs/extension-api.md +6 -5
  6. package/docs/missions.md +28 -14
  7. package/docs/models.md +8 -4
  8. package/docs/observability.md +10 -5
  9. package/docs/tool-reference.md +90 -88
  10. package/docs/workflows.md +166 -172
  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 +2 -0
  23. package/src/agents/agents.d.ts.map +1 -1
  24. package/src/agents/agents.js +73 -3
  25. package/src/agents/agents.js.map +1 -1
  26. package/src/api/preflight.d.ts +6 -2
  27. package/src/api/preflight.d.ts.map +1 -1
  28. package/src/api/preflight.js +46 -2
  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 +15 -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 +87 -179
  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 +73 -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 +4 -117
  63. package/src/extension/schemas.d.ts.map +1 -1
  64. package/src/extension/schemas.js +42 -127
  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/missions/goal-driver.d.ts +1 -0
  86. package/src/missions/goal-driver.d.ts.map +1 -1
  87. package/src/missions/goal-driver.js +49 -34
  88. package/src/missions/goal-driver.js.map +1 -1
  89. package/src/missions/store.d.ts.map +1 -1
  90. package/src/missions/store.js +11 -1
  91. package/src/missions/store.js.map +1 -1
  92. package/src/profiles/profiles.d.ts +1 -1
  93. package/src/profiles/profiles.d.ts.map +1 -1
  94. package/src/profiles/profiles.js +4 -0
  95. package/src/profiles/profiles.js.map +1 -1
  96. package/src/runs/background/async-execution.d.ts +16 -0
  97. package/src/runs/background/async-execution.d.ts.map +1 -1
  98. package/src/runs/background/async-execution.js +95 -8
  99. package/src/runs/background/async-execution.js.map +1 -1
  100. package/src/runs/background/async-job-tracker.d.ts +1 -0
  101. package/src/runs/background/async-job-tracker.d.ts.map +1 -1
  102. package/src/runs/background/async-job-tracker.js +67 -24
  103. package/src/runs/background/async-job-tracker.js.map +1 -1
  104. package/src/runs/background/async-retention.d.ts +1 -1
  105. package/src/runs/background/async-retention.d.ts.map +1 -1
  106. package/src/runs/background/async-retention.js +47 -15
  107. package/src/runs/background/async-retention.js.map +1 -1
  108. package/src/runs/background/await-async-run.d.ts +23 -0
  109. package/src/runs/background/await-async-run.d.ts.map +1 -0
  110. package/src/runs/background/await-async-run.js +82 -0
  111. package/src/runs/background/await-async-run.js.map +1 -0
  112. package/src/runs/background/chain-root-attachment.d.ts +1 -0
  113. package/src/runs/background/chain-root-attachment.d.ts.map +1 -1
  114. package/src/runs/background/chain-root-attachment.js +3 -3
  115. package/src/runs/background/chain-root-attachment.js.map +1 -1
  116. package/src/runs/background/control-channel.d.ts +10 -0
  117. package/src/runs/background/control-channel.d.ts.map +1 -1
  118. package/src/runs/background/control-channel.js +51 -0
  119. package/src/runs/background/control-channel.js.map +1 -1
  120. package/src/runs/background/notify.d.ts +3 -0
  121. package/src/runs/background/notify.d.ts.map +1 -1
  122. package/src/runs/background/notify.js +3 -0
  123. package/src/runs/background/notify.js.map +1 -1
  124. package/src/runs/background/run-child-session.d.ts.map +1 -1
  125. package/src/runs/background/run-child-session.js +24 -7
  126. package/src/runs/background/run-child-session.js.map +1 -1
  127. package/src/runs/background/run-status.d.ts.map +1 -1
  128. package/src/runs/background/run-status.js +6 -0
  129. package/src/runs/background/run-status.js.map +1 -1
  130. package/src/runs/background/runner-aliases.d.ts +1 -0
  131. package/src/runs/background/runner-aliases.d.ts.map +1 -1
  132. package/src/runs/background/runner-aliases.js +6 -2
  133. package/src/runs/background/runner-aliases.js.map +1 -1
  134. package/src/runs/background/runner-child-launch.d.ts +1 -0
  135. package/src/runs/background/runner-child-launch.d.ts.map +1 -1
  136. package/src/runs/background/runner-child-launch.js +2 -0
  137. package/src/runs/background/runner-child-launch.js.map +1 -1
  138. package/src/runs/background/scheduled-runs.d.ts +2 -1
  139. package/src/runs/background/scheduled-runs.d.ts.map +1 -1
  140. package/src/runs/background/scheduled-runs.js +33 -16
  141. package/src/runs/background/scheduled-runs.js.map +1 -1
  142. package/src/runs/background/stale-run-reconciler.d.ts +1 -1
  143. package/src/runs/background/stale-run-reconciler.d.ts.map +1 -1
  144. package/src/runs/background/stale-run-reconciler.js +15 -2
  145. package/src/runs/background/stale-run-reconciler.js.map +1 -1
  146. package/src/runs/background/subagent-runner.d.ts +3 -0
  147. package/src/runs/background/subagent-runner.d.ts.map +1 -1
  148. package/src/runs/background/subagent-runner.js +49 -3
  149. package/src/runs/background/subagent-runner.js.map +1 -1
  150. package/src/runs/foreground/async-stop-action.d.ts.map +1 -1
  151. package/src/runs/foreground/async-stop-action.js +15 -1
  152. package/src/runs/foreground/async-stop-action.js.map +1 -1
  153. package/src/runs/foreground/command-action.d.ts +13 -0
  154. package/src/runs/foreground/command-action.d.ts.map +1 -0
  155. package/src/runs/foreground/command-action.js +38 -0
  156. package/src/runs/foreground/command-action.js.map +1 -0
  157. package/src/runs/foreground/execution.d.ts.map +1 -1
  158. package/src/runs/foreground/execution.js +27 -6
  159. package/src/runs/foreground/execution.js.map +1 -1
  160. package/src/runs/foreground/subagent-executor.d.ts +7 -5
  161. package/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  162. package/src/runs/foreground/subagent-executor.js +356 -192
  163. package/src/runs/foreground/subagent-executor.js.map +1 -1
  164. package/src/runs/shared/child-commands.d.ts +28 -0
  165. package/src/runs/shared/child-commands.d.ts.map +1 -0
  166. package/src/runs/shared/child-commands.js +250 -0
  167. package/src/runs/shared/child-commands.js.map +1 -0
  168. package/src/runs/shared/child-launch.d.ts +5 -1
  169. package/src/runs/shared/child-launch.d.ts.map +1 -1
  170. package/src/runs/shared/child-launch.js +15 -1
  171. package/src/runs/shared/child-launch.js.map +1 -1
  172. package/src/runs/shared/child-session.d.ts +13 -0
  173. package/src/runs/shared/child-session.d.ts.map +1 -1
  174. package/src/runs/shared/child-session.js +182 -19
  175. package/src/runs/shared/child-session.js.map +1 -1
  176. package/src/runs/shared/child-tool-plan.d.ts +4 -0
  177. package/src/runs/shared/child-tool-plan.d.ts.map +1 -1
  178. package/src/runs/shared/child-tool-plan.js +12 -5
  179. package/src/runs/shared/child-tool-plan.js.map +1 -1
  180. package/src/runs/shared/claude-code-adapter.d.ts +56 -0
  181. package/src/runs/shared/claude-code-adapter.d.ts.map +1 -1
  182. package/src/runs/shared/claude-code-adapter.js +138 -2
  183. package/src/runs/shared/claude-code-adapter.js.map +1 -1
  184. package/src/runs/shared/dynamic-fanout.js +2 -2
  185. package/src/runs/shared/dynamic-fanout.js.map +1 -1
  186. package/src/runs/shared/external-cli-preflight.d.ts +10 -0
  187. package/src/runs/shared/external-cli-preflight.d.ts.map +1 -1
  188. package/src/runs/shared/external-cli-preflight.js +50 -6
  189. package/src/runs/shared/external-cli-preflight.js.map +1 -1
  190. package/src/runs/shared/external-cli-runner.d.ts.map +1 -1
  191. package/src/runs/shared/external-cli-runner.js +4 -2
  192. package/src/runs/shared/external-cli-runner.js.map +1 -1
  193. package/src/runs/shared/mcp-config-sources.d.ts +1 -0
  194. package/src/runs/shared/mcp-config-sources.d.ts.map +1 -1
  195. package/src/runs/shared/mcp-config-sources.js +1 -1
  196. package/src/runs/shared/mcp-config-sources.js.map +1 -1
  197. package/src/runs/shared/mcp-direct-tool-allowlist.d.ts +28 -0
  198. package/src/runs/shared/mcp-direct-tool-allowlist.d.ts.map +1 -1
  199. package/src/runs/shared/mcp-direct-tool-allowlist.js +79 -2
  200. package/src/runs/shared/mcp-direct-tool-allowlist.js.map +1 -1
  201. package/src/runs/shared/model-resolution.d.ts +16 -2
  202. package/src/runs/shared/model-resolution.d.ts.map +1 -1
  203. package/src/runs/shared/model-resolution.js +39 -11
  204. package/src/runs/shared/model-resolution.js.map +1 -1
  205. package/src/runs/shared/model-scope.d.ts +2 -1
  206. package/src/runs/shared/model-scope.d.ts.map +1 -1
  207. package/src/runs/shared/model-scope.js +26 -10
  208. package/src/runs/shared/model-scope.js.map +1 -1
  209. package/src/runs/shared/parallel-utils.d.ts +4 -0
  210. package/src/runs/shared/parallel-utils.d.ts.map +1 -1
  211. package/src/runs/shared/parallel-utils.js.map +1 -1
  212. package/src/runs/shared/pi-spawn.d.ts +1 -10
  213. package/src/runs/shared/pi-spawn.d.ts.map +1 -1
  214. package/src/runs/shared/pi-spawn.js +7 -86
  215. package/src/runs/shared/pi-spawn.js.map +1 -1
  216. package/src/runs/shared/run-history.d.ts +47 -0
  217. package/src/runs/shared/run-history.d.ts.map +1 -1
  218. package/src/runs/shared/run-history.js +70 -0
  219. package/src/runs/shared/run-history.js.map +1 -1
  220. package/src/runs/shared/subagent-control.d.ts.map +1 -1
  221. package/src/runs/shared/subagent-control.js +30 -17
  222. package/src/runs/shared/subagent-control.js.map +1 -1
  223. package/src/runs/shared/subagent-prompt-runtime.d.ts +1 -0
  224. package/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
  225. package/src/runs/shared/subagent-prompt-runtime.js +14 -5
  226. package/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
  227. package/src/runs/shared/worktree.d.ts +1 -1
  228. package/src/runs/shared/worktree.d.ts.map +1 -1
  229. package/src/runs/shared/worktree.js +1 -1
  230. package/src/runs/shared/worktree.js.map +1 -1
  231. package/src/shared/disabled-features.d.ts +92 -0
  232. package/src/shared/disabled-features.d.ts.map +1 -0
  233. package/src/shared/disabled-features.js +113 -0
  234. package/src/shared/disabled-features.js.map +1 -0
  235. package/src/shared/extension-context.d.ts +9 -0
  236. package/src/shared/extension-context.d.ts.map +1 -1
  237. package/src/shared/extension-context.js +7 -0
  238. package/src/shared/extension-context.js.map +1 -1
  239. package/src/shared/required-child-extensions.d.ts +16 -1
  240. package/src/shared/required-child-extensions.d.ts.map +1 -1
  241. package/src/shared/required-child-extensions.js +37 -5
  242. package/src/shared/required-child-extensions.js.map +1 -1
  243. package/src/shared/settings.d.ts +1 -1
  244. package/src/shared/types.d.ts +26 -1
  245. package/src/shared/types.d.ts.map +1 -1
  246. package/src/shared/types.js +1 -1
  247. package/src/shared/types.js.map +1 -1
  248. package/src/slash/prompt-template-bridge.js +2 -2
  249. package/src/slash/prompt-template-bridge.js.map +1 -1
  250. package/src/slash/slash-commands.d.ts +2 -0
  251. package/src/slash/slash-commands.d.ts.map +1 -1
  252. package/src/slash/slash-commands.js +2 -2
  253. package/src/slash/slash-commands.js.map +1 -1
  254. package/src/slash/subagent-cost.d.ts.map +1 -1
  255. package/src/slash/subagent-cost.js +8 -4
  256. package/src/slash/subagent-cost.js.map +1 -1
  257. package/src/tui/render.d.ts +1 -1
  258. package/src/tui/render.d.ts.map +1 -1
  259. package/src/tui/render.js +59 -21
  260. package/src/tui/render.js.map +1 -1
  261. package/src/watchdog/diff-tool.d.ts +3 -1
  262. package/src/watchdog/diff-tool.d.ts.map +1 -1
  263. package/src/watchdog/diff-tool.js +20 -11
  264. package/src/watchdog/diff-tool.js.map +1 -1
  265. package/src/watchdog/register-child.js +2 -2
  266. package/src/watchdog/register-child.js.map +1 -1
  267. package/src/watchdog/register-main.js +2 -2
  268. package/src/watchdog/register-main.js.map +1 -1
  269. package/src/watchdog/review.d.ts +1 -1
  270. package/src/watchdog/review.d.ts.map +1 -1
  271. package/src/watchdog/review.js +1 -1
  272. package/src/watchdog/review.js.map +1 -1
  273. package/src/watchdog/turn-delta.js +1 -1
  274. package/src/watchdog/turn-delta.js.map +1 -1
  275. package/src/workflows/chat-progress.js +2 -2
  276. package/src/workflows/chat-progress.js.map +1 -1
  277. package/src/workflows/scripted-workflow.d.ts +8 -0
  278. package/src/workflows/scripted-workflow.d.ts.map +1 -1
  279. package/src/workflows/scripted-workflow.js +10 -2
  280. package/src/workflows/scripted-workflow.js.map +1 -1
  281. package/src/workflows/structured-workflow-scripts.d.ts +10 -0
  282. package/src/workflows/structured-workflow-scripts.d.ts.map +1 -0
  283. package/src/workflows/structured-workflow-scripts.js +166 -0
  284. package/src/workflows/structured-workflow-scripts.js.map +1 -0
  285. package/src/workflows/workflow-receipt.d.ts.map +1 -1
  286. package/src/workflows/workflow-receipt.js +10 -4
  287. package/src/workflows/workflow-receipt.js.map +1 -1
  288. package/src/workflows/workflow-resources.d.ts +13 -0
  289. package/src/workflows/workflow-resources.d.ts.map +1 -1
  290. package/src/workflows/workflow-resources.js +46 -16
  291. package/src/workflows/workflow-resources.js.map +1 -1
  292. package/src/workflows/workflow-reuse.d.ts +51 -0
  293. package/src/workflows/workflow-reuse.d.ts.map +1 -0
  294. package/src/workflows/workflow-reuse.js +103 -0
  295. package/src/workflows/workflow-reuse.js.map +1 -0
  296. package/src/workflows/workflow-revival.d.ts +32 -0
  297. package/src/workflows/workflow-revival.d.ts.map +1 -0
  298. package/src/workflows/workflow-revival.js +111 -0
  299. package/src/workflows/workflow-revival.js.map +1 -0
package/docs/missions.md CHANGED
@@ -30,16 +30,11 @@ const created = subagent({
30
30
  action: "mission.create",
31
31
  mission: { title: "Ship auth refresh", objective: "Implement and validate token refresh" }
32
32
  })
33
- subagent({
34
- workflowScript: `return runs.run("main", { agent: "worker", task: "Implement the approved auth refresh plan" })`,
35
- missionId: "<mission-id>"
36
- })
33
+ // After a ```js workflow block that runs the approved auth refresh plan:
34
+ subagent({ workflow: true, missionId: "<mission-id>" })
37
35
 
38
36
  // Or create and attach in one launch
39
- subagent({
40
- workflowScript: `return runs.run("main", { agent: "worker", task: "Implement the approved plan" })`,
41
- mission: { title: "Ship auth refresh" }
42
- })
37
+ subagent({ workflow: true, mission: { title: "Ship auth refresh" } })
43
38
  ```
44
39
 
45
40
  ### Goal missions
@@ -89,6 +84,10 @@ Durable schedules are enabled by default and stored per project under `.pi/subag
89
84
 
90
85
  Create a one-shot schedule:
91
86
 
87
+ ```js workflow
88
+ return runs.run("main", { agent: "reviewer", task: "Review the current diff." });
89
+ ```
90
+
92
91
  ```ts
93
92
  subagent({
94
93
  action: "schedule.create",
@@ -96,29 +95,44 @@ subagent({
96
95
  name: "Evening review",
97
96
  at: "+30m",
98
97
  baseRef: "refs/heads/release",
99
- workflowScript: `return runs.run("main", { agent: "reviewer", task: "Review the current diff." })`
98
+ workflow: true
100
99
  })
101
100
  ```
102
101
 
103
- Create a fixed recurring workflow:
102
+ Create a fixed recurring workflow from a script file:
104
103
 
105
104
  ```ts
106
- subagent({ action: "schedule.create", id: "backlog", every: "6h", catchUp: "latest", workflowScript: "return runs.run('main', { agent: 'worker', task: args.task })", args: { task: "Maintain core" } })
105
+ // .pi/workflows/backlog.js: return runs.run('main', { agent: 'worker', task: args.task })
106
+ subagent({ action: "schedule.create", id: "backlog", every: "6h", catchUp: "latest", workflow: "./.pi/workflows/backlog.js", args: { task: "Maintain core" } })
107
107
  ```
108
108
 
109
- Fixed intervals support `m`, `h`, `d`, and `w` units and advance from the planned time without completion drift. Schedule arguments are normalized and persisted for exact replay after reload; do not put secrets in them.
109
+ Fixed intervals support `m`, `h`, `d`, and `w` units and advance from the planned time without completion drift. The schedule stores the script text read at creation, so later edits to the file do not change it. Schedule arguments are normalized and persisted for exact replay after reload; do not put secrets in them.
110
110
 
111
111
  Create a quiet recurring workflow whose successful completions stay visible but do not wake the parent session:
112
112
 
113
113
  ```ts
114
- subagent({ action: "schedule.create", id: "nightly-sweep", every: "24h", quiet: true, workflowScript: "..." })
114
+ subagent({ action: "schedule.create", id: "nightly-sweep", every: "24h", quiet: true, workflow: true })
115
115
  ```
116
116
 
117
117
  Manage schedules with `schedule.list`, `schedule.show`, `schedule.history`, `schedule.pause`, `schedule.resume`, `schedule.run`, `schedule.run-due`, and `schedule.delete`.
118
118
 
119
+ Attach an existing mission to give each scheduled workflow access to the same durable `state.get/set`:
120
+
121
+ ```ts
122
+ subagent({ action: "schedule.create", id: "backlog", every: "6h", workflow: "./.pi/workflows/backlog.js", missionId: "<mission-id>" })
123
+ ```
124
+
125
+ The mission must be readable in the store resolved from the schedule's target `cwd` and current mission configuration. Creation checks it without changing its status. Every fire uses the normal explicit mission launch path, including after session restoration; missing or invalid records fail before workflow execution. Schedules accept only an existing `missionId`, not mission creation or updates. The script's fixed `args` and its mutable mission state remain separate.
126
+
127
+ Attachment uses ordinary mission lifecycle and retention rules. A non-goal mission can become terminal after a run, and the next fire reactivates it. `mission.close` does not pause the schedule; use `schedule.pause` or `schedule.delete` to stop future fires. A schedule does not protect its mission from terminal retention (default 200 records). If it is removed, later fires record `failed_launch` rather than creating a replacement. Open mission decisions and goal notice pause/budget settings do not gate schedule launches. Multiple schedules sharing a mission still have independent overlap controls; attachment does not serialize their workflows.
128
+
129
+ `missions.enabled:false` still permits explicit attachment; `disabledFeatures:["missions"]` rejects it. Each fire resolves the mission store using its target `cwd`, current mission configuration and Pi agent directory; the schedule does not pin the creation-time store. Sessions must resolve the same effective store to reuse the same state. Attachment does not search other worktrees or copy mission records; an explicitly shared `missions.directory` follows the existing storage rules.
130
+
131
+ Mission-bound definitions use schedule schema version 2 so older versions reject them instead of dropping the attachment. Existing unbound definitions keep version 1 and need no migration. A project using mission attachment should use a version that supports it in every scheduler session.
132
+
119
133
  Behavior:
120
134
 
121
- - Runs always launch async with fresh context and disable automatic mission creation; mission attachment is deferred from this first slice.
135
+ - Runs always launch async with fresh context. Without `missionId`, they disable mission creation and have no `state` global.
122
136
  - An optional top-level `baseRef` selects the safe Git ref used by managed worktrees (default `HEAD`); it is persisted with the schedule and forwarded on every fire. The source checkout must still be clean.
123
137
  - Definitions, bounded history, append-only events, and per-run receipts are stored with mode `0600`.
124
138
  - `overlap` is currently fixed to `skip`; `catchUp` supports `latest` (default) and `none`.
package/docs/models.md CHANGED
@@ -13,6 +13,8 @@ Builtin agents inherit your current Pi default model. This keeps new installs fr
13
13
 
14
14
  Precedence, strongest first: per-run override → provider-scoped role override → `agentOverrides.<name>.model` → agent frontmatter `model` → `subagents.defaultModel` → the parent session model. A provider preference does not replace this order; it only resolves bare model ids when the active registry has more than one match. Fully qualified `provider/model` strings still win exactly.
15
15
 
16
+ On the two Claude Code adapters a model is Claude Code's own alias or id rather than a Pi child model, so a value set for one of them resolves nothing against the registry. `subagents.defaultModel` is ignored there, because it is a default for Pi children. An explicit `agentOverrides.<name>.model` or agent frontmatter `model` is passed on as given, and Claude Code reports it when it does not recognize the value. See [agents](agents.md).
17
+
16
18
  Each launch resolves one model. Provider errors, including HTTP 429 responses, are returned from that model rather than selecting another one. Separately, a verified compaction abort after useful progress may continue the retained child session once on the same resolved model; this lifecycle recovery preserves work and is not model fallback.
17
19
 
18
20
  Use `model: "inherit"` in agent frontmatter or `agentOverrides.<name>.model` to select the current parent session model explicitly.
@@ -122,7 +124,7 @@ If your provider rejects model IDs with thinking suffixes, set `subagents.disabl
122
124
 
123
125
  ### Thinking ceiling
124
126
 
125
- Set `subagents.maxThinking` to enforce a hard maximum for every native Pi child. The supported levels, from least to most thinking, are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`:
127
+ Set `subagents.maxThinking` to enforce a hard maximum for every child that can ask for a thinking level: native Pi children and the two Claude Code adapters. The supported levels, from least to most thinking, are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`:
126
128
 
127
129
  ```json
128
130
  {
@@ -133,7 +135,7 @@ Set `subagents.maxThinking` to enforce a hard maximum for every native Pi child.
133
135
  }
134
136
  ```
135
137
 
136
- Requests above the ceiling fail before child startup; the setting covers frontmatter, `agentOverrides`, per-run overrides, parallel/chain children, nested launches, and resumed children. Project settings take precedence over user settings. External runners retain their existing behavior.
138
+ Requests above the ceiling fail before child startup; the setting covers frontmatter, `agentOverrides`, per-run overrides, parallel/chain children, nested launches, and resumed children. Project settings take precedence over user settings. External runners keep their existing behavior, except the two Claude Code adapters, which translate the requested level into `--effort` and enforce the same ceiling. An enforced `subagents.modelScope` also covers them: the model id that will reach the CLI is checked, and a launch that pins no model fails closed.
137
139
 
138
140
  ## Extension defaults
139
141
 
@@ -206,7 +208,7 @@ To keep subagents inside a budget or compliance profile, enforce a model scope.
206
208
  }
207
209
  ```
208
210
 
209
- - `allow` is a list of glob patterns matched against the resolved `provider/id` (only `*` is special, case-insensitive). The literal `inherit` means the current parent session model.
211
+ - `allow` is a list of glob patterns matched against the resolved `provider/id` (only `*` is special, case-insensitive). The literal `inherit` means the current parent session model. The literal `scoped` means every model in the parent session's scoped-model set (pi's `/scoped-models`); when that set is empty, it means the same as `inherit`. Because `scoped` is reserved, it cannot match a provider literally named `scoped`.
210
212
  - `agents.<name>` adds a second allow-list for that agent. The model must pass both the global list and the matching agent list, so an agent rule cannot weaken the global rule. Agent rules inherit `enforce` and `strict` when those fields are absent.
211
213
  - A top-level `enforce: true` with only agent allow-lists restricts only those named agents. Unknown names are allowed so settings can be shared across projects and machines.
212
214
  - Models you pass explicitly — the tool-call `model`, `--model`, or a clarify pick — error and abort the run.
@@ -216,7 +218,7 @@ To keep subagents inside a budget or compliance profile, enforce a model scope.
216
218
 
217
219
  Model scope is policy only. It rejects or warns; it does not select a cheaper model. Set `agentOverrides.worker.model` to choose a worker model and use `modelScope.agents.worker` to prevent a per-run override from escaping that restriction.
218
220
 
219
- `inherit` expands in the parent process at each launch. It is never sent to the child as a model id. A nested child therefore inherits its immediate parent's current model, not the original top-level model. If no parent model is available, an enforced `inherit` entry does not match and fails closed.
221
+ `inherit` and `scoped` expand in the parent process at each launch. They are never sent to the child as model ids. A nested child therefore inherits its immediate parent's current model, not the original top-level model. `scoped` uses the scoped-model set at launch, so a mid-session `/scoped-models` change applies to the next child; background runs keep the set captured when the run started. Pi reports an empty set when the session is unscoped, either because nothing is configured or because no configured pattern matches an available model, and then lets the parent use every model. In that case `scoped` behaves exactly as `inherit`, which is narrower. If no parent model is available, an enforced `inherit` or `scoped` entry does not match and fails closed.
220
222
 
221
223
  Project `modelScope` settings replace the complete user `modelScope`, as with the existing project-over-user settings precedence. Project settings are trusted and can therefore replace user restrictions.
222
224
 
@@ -247,3 +249,5 @@ The workflow:
247
249
  - `/subagents-refresh-provider-models` writes a serialized provider model catalog with observed registry data, simple role-oriented classification, and live probe results from tiny one-shot `pi -p --model ... --no-tools` checks. The cache refreshes when missing or stale; use `--force` to ignore freshness and probe again immediately.
248
250
  - `/subagents-generate-profiles` uses the provider catalog to produce quota and quality profiles.
249
251
  - `/subagents-check-profile` re-checks each assigned model in a saved profile against the current registry and a live probe, so you can detect model removals, auth problems, or stale assignments.
252
+
253
+ Hand-authored profile entries can also set `machine` to a non-empty string or `false`. Loading and checking validate the whole saved profile, including machine syntax; malformed values are rejected before loading writes settings or checking probes models. Valid machine strings are trimmed and use the same limits as agent settings. `false` clears a machine pin, while an omitted field preserves an existing string pin when loading. Validation does not query the live machine catalog.
@@ -10,6 +10,8 @@ A foreground child is a pi session created inside the parent Pi process, not a s
10
10
 
11
11
  A background child is a pi session created inside the detached runner process. The runner mirrors session events into `events.jsonl`, `output-<index>.log`, and the transcript. Interrupt and stop abort the child session; steer requests are delivered with the session's `steer` or `followUp`.
12
12
 
13
+ Long-open `bash` attention notices identify the exact tool call and include status and child-specific transcript commands. Each distinct long-open call notifies once. Inspect the running command and recent output before intervening: development servers and watch commands may intentionally never return, while builds and tests may legitimately take time. A queued steer does not cancel the shell command. For local native Pi children explicitly granted `subagent_command`, `command.yield` returns control while the selected command remains managed; `command.cancel` stops that command without interrupting the child. Query `command.status` to confirm its terminal state. Unsupported children require the existing run-scoped interrupt/resume path, which may affect siblings. See [command controls](tool-reference.md#command-controls).
14
+
13
15
  Live progress shows compact detail for single, chain, and parallel modes: a bounded one-line task, current tool, recent output, token counts, aggregate cost, duration, activity freshness, current-tool duration, and chain graph metadata when available. Workflow `label` metadata wins over raw task text in compact multi-child cards.
14
16
 
15
17
  Press Pi's configured expand key (`Ctrl+O` by default) to expand the full streaming view with complete output per step.
@@ -53,9 +55,11 @@ or keyboard shortcuts is needed. The summary counts the widget's tracked runs,
53
55
  including workflow parents and children, rather than unique agents.
54
56
 
55
57
  Folding stays in effect across progress updates and does not change Pi's global
56
- expand setting, run execution, or completion notifications. Task rows, drag and
57
- wheel events, and modifier clicks are left unhandled. The state resets when the
58
- widget is removed or Pi reloads. Regular mode keeps the existing keyboard controls.
58
+ expand setting, run execution, or completion notifications. Set
59
+ `asyncWidgetCollapsed: true` in the extension configuration to start each newly
60
+ mounted widget folded. Task rows, drag and wheel events, and modifier clicks are left
61
+ unhandled. The state resets when the widget is removed or Pi reloads. Regular mode
62
+ keeps the existing keyboard controls.
59
63
 
60
64
  ### Reducing status display noise
61
65
 
@@ -74,6 +78,7 @@ For compact chat results with FleetView as the only live editor surface, merge t
74
78
  - `inlineToolDisplay: "summary"` keeps one static result row per call, alongside its call heading. A completed status query is not proof that the queried child has finished.
75
79
  - `fleetView: true` retains live progress. Open `/subagents-fleet` for details instead of repeatedly requesting status just to watch progress. Pi's expand key does not expand summary results; keep `"rich"` if you want expandable inline output.
76
80
  - `asyncWidget: false` hides only the additional under-editor async widget, leaving FleetView available. This configuration reduces visible surfaces; it does not guarantee ordering relative to other extensions.
81
+ - `asyncWidgetCollapsed: true` starts each newly mounted async widget as a one-line live status summary; click its header to expand it.
77
82
 
78
83
  Thanks to [DraconDev](https://github.com/DraconDev) for reporting the display noise and suggesting summary mode in [#1931](https://github.com/nicobailon/pi-subagents/issues/1931).
79
84
 
@@ -212,7 +217,7 @@ subscribe to the `subagent:child-status` event advertised by RPC `ping` as
212
217
  `version: 1`, `runId`, `childId`, `status` (`"started"`, `"stopping"`, or
213
218
  `"stopped"`), `ts`, and optional child metadata such as `asyncDir`, `stepIndex`,
214
219
  `agent`, `childRunId`, `workflowKey`, `phase`, and `label`. Async
215
- `workflowScript` roots emit `"started"` once the keyed child has a concrete
220
+ Workflow script roots emit `"started"` once the keyed child has a concrete
216
221
  launch identity; `childId` and `workflowKey` are the stable workflow key, while
217
222
  `stepIndex` is only a convenience projection for the current status snapshot.
218
223
  These events are observer hints only. They can duplicate across the live event
@@ -311,7 +316,7 @@ Async events:
311
316
  - `subagent:async-started`
312
317
  - `subagent:async-complete`
313
318
 
314
- For regular async starts, the `subagent:async-started` payload includes redacted `task` and `goal` prompt fields. Async `workflowScript` roots emit the same event with `mode: "workflow"` after their initial `status.json` is durable; workflow roots can omit `task`, and any included prompt fields are redacted. Their keyed children are then announced dynamically through `subagent:child-status`. Companion UI extensions can combine those hints with the authoritative live lifecycle artifacts under `asyncDir` without scraping terminal output.
319
+ For regular async starts, the `subagent:async-started` payload includes redacted `task` and `goal` prompt fields. Async workflow script roots emit the same event with `mode: "workflow"` after their initial `status.json` is durable; workflow roots can omit `task`, and any included prompt fields are redacted. Their keyed children are then announced dynamically through `subagent:child-status`. Companion UI extensions can combine those hints with the authoritative live lifecycle artifacts under `asyncDir` without scraping terminal output.
315
320
 
316
321
  Intercom delivery events:
317
322
 
@@ -8,13 +8,15 @@ Call `{ action: "guide", topic: "tool-reference" }` for this reference or `topic
8
8
 
9
9
  ## Execution examples
10
10
 
11
- Chaining is code-driven through `workflowScript`. Use `await runs.run(...)` for sequential steps and `await runs.all([{ key, agent, task }, ...])` for ordinary parallel fanout. `runs.all` resolves to an ordered array, not a key map, so use indexes, destructuring, or `.map(...)`, not `results.<key>`. 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. For permission-sensitive host calls, use an extension-owned named resource such as `{ workflow: "run-ci", args: { command: "npm test" } }`; raw public `workflowScript`/`workflowScriptPath` inputs have unknown resource provenance and cannot call `runs.host`. A resolved resource may internally use `runs.host(key, { kind: "command", command, timeoutMs, output?, role?, provider? })` within its authority ceiling; there is no per-step `cwd`, and commands and relative output paths use the workflow `cwd`. Set `cwd` on the outer `subagent({...})` request instead, or put a trusted directory change in the command (for example, `cd /path/to/worktree && npm test`).
11
+ Chaining is code-driven through a workflow script. Write the script as one ```` ```js workflow ```` fenced block in the reply, then call `subagent({ workflow: true, ... })` in the same reply. A `workflow` string containing `/` is a script file, and any other string is a named resource. `workflowScript` and `workflowScriptPath` were removed; use these forms instead.
12
12
 
13
- Use `{ action: "validate", workflowScript }` to check statically decidable syntax and structure without launching children. It returns `{ ok, errors }` and fails the tool call when `ok` is false. Literal child `baseRef` values are checked against the runtime ref policy. Dynamic keys and values remain subject to runtime checks; static validation does not guess them.
13
+ Use `await runs.run(...)` for sequential steps and `await runs.all([{ key, agent, task }, ...])` for ordinary parallel fanout. `runs.all` resolves to an ordered array, not a key map, so use indexes, destructuring, or `.map(...)`, not `results.<key>`. 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, except that `disabledFeatures: ["workflow-scripts"]` replaces workflow scripts with top-level `chain` and `tasks` (see [configuration](configuration.md#chain-and-tasks-without-workflow-scripts)). 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. For permission-sensitive host calls, use an extension-owned named resource such as `{ workflow: "run-ci", args: { command: "npm test" } }`; raw scripts (`workflow: true` or a script path) have unknown resource provenance and cannot call `runs.host`. A resolved resource may internally use `runs.host(key, { kind: "command", command, timeoutMs, output?, role?, provider? })` within its authority ceiling; there is no per-step `cwd`, and commands and relative output paths use the workflow `cwd`. Set `cwd` on the outer `subagent({...})` request instead, or put a trusted directory change in the command (for example, `cd /path/to/worktree && npm test`).
14
14
 
15
- Use `workflowScriptPath` instead of `workflowScript` to load the same JavaScript statement body from a file. The two fields are mutually exclusive. Relative paths resolve against the request `cwd`, and absolute paths pass through. The host reads the file before validation, scheduling, or sandbox execution. The workflow sandbox still has no filesystem access. Missing, unreadable, and empty files fail as file input errors.
15
+ Use `{ action: "validate", workflow: true }` (or a script path) to check statically decidable syntax and structure without launching children. It returns `{ ok, errors }` and fails the tool call when `ok` is false. Literal child `baseRef` values are checked against the runtime ref policy. Dynamic keys and values remain subject to runtime checks; static validation does not guess them.
16
16
 
17
- Raw inline and file-backed scripts accept bounded plain-JSON `args`, including during `validate` and `schedule.create`. Omitted raw args become `{}`; supplied args are deeply frozen in the sandbox. Normalized args persist in run and schedule evidence for diagnosis and exact replay, so never include secrets. Args are data only and do not grant `runs.host` authority.
17
+ Use `workflow: "./path/to/script.js"` to load the same JavaScript statement body from a file. Relative paths resolve against the request `cwd`, and absolute paths pass through. The host reads the file before validation, scheduling, or sandbox execution. The workflow sandbox still has no filesystem access. Missing, unreadable, and empty files fail as file input errors.
18
+
19
+ Raw reply-block and file-backed scripts accept bounded plain-JSON `args`, including during `validate` and `schedule.create`. Omitted raw args become `{}`; supplied args are deeply frozen in the sandbox. Normalized args persist in run and schedule evidence for diagnosis and exact replay, so never include secrets. Args are data only and do not grant `runs.host` authority.
18
20
 
19
21
  For permission-extension interoperability, use one of the package-owned named resources with bounded `args` instead of caller-supplied workflow text:
20
22
 
@@ -23,66 +25,41 @@ For permission-extension interoperability, use one of the package-owned named re
23
25
  { workflow: "run-ci", args: { command: "npm test" } }
24
26
  ```
25
27
 
26
- The host resolves the script and authority internally and records bounded provenance in workflow details and receipts. Named resources cannot be combined with `agent`, `task`, `workflowScript`, or `workflowScriptPath`; user/project resource registries are not part of this first slice.
28
+ The host resolves the script and authority internally and records bounded provenance in workflow details and receipts. Named resources cannot be combined with `agent` or `task`; user/project resource registries are not part of this first slice.
27
29
 
28
30
  ```js
29
- { workflowScriptPath: "workflows/review.js", args: { target: "src/workflows" }, cwd: "/path/to/project" }
30
- { action: "validate", workflowScriptPath: "workflows/review.js", args: { target: "src/workflows" } }
31
- { action: "schedule.create", every: "6h", workflowScriptPath: "workflows/review.js", args: { target: "src/workflows" } }
31
+ { workflow: "./workflows/review.js", args: { target: "src/workflows" }, cwd: "/path/to/project" }
32
+ { action: "validate", workflow: "./workflows/review.js", args: { target: "src/workflows" } }
33
+ { action: "schedule.create", every: "6h", workflow: "./workflows/review.js", args: { target: "src/workflows" } }
32
34
  ```
33
35
 
34
- ```js
36
+ Each script below is the ```` ```js workflow ```` block of a reply that then calls `subagent({ workflow: true })`:
37
+
38
+ ```js workflow
35
39
  // One child; return the child promise explicitly
36
- { workflowScript: `return runs.run("main", { agent: "scout", task: "Analyze the auth flow" })` }
40
+ return runs.run("main", { agent: "scout", task: "Analyze the auth flow" });
41
+ ```
37
42
 
43
+ ```js workflow
38
44
  // Sequential workflow
39
- { workflowScript: `
40
- const scan = await runs.run("scan", { agent: "scout", task: "Analyze auth" });
41
- return (await runs.run("implement", { agent: "worker", task: "Implement from: " + scan.output })).output;
42
- ` }
45
+ const scan = await runs.run("scan", { agent: "scout", task: "Analyze auth" });
46
+ return (await runs.run("implement", { agent: "worker", task: "Implement from: " + scan.output })).output;
47
+ ```
43
48
 
49
+ ```js workflow
44
50
  // Parallel workflow
45
- { workflowScript: `
46
- const results = await runs.all([
47
- { key: "backend", agent: "reviewer", task: "Review backend" },
48
- { key: "frontend", agent: "reviewer", task: "Review frontend" }
49
- ]);
50
- return results.map(result => result.output);
51
- ` }
51
+ const results = await runs.all([
52
+ { key: "backend", agent: "reviewer", task: "Review backend" },
53
+ { key: "frontend", agent: "reviewer", task: "Review frontend" }
54
+ ]);
55
+ return results.map(result => result.output);
52
56
  ```
53
57
 
54
58
  ### Parallel sequential lanes
55
59
 
56
- Use `runs.lanes(lanes)` inside a `workflowScript` when several independent lanes each have ordered stages. This helper composes the existing workflow child runner; it does not add a top-level `lanes` parameter or a second persistence/cleanup system.
60
+ Use `runs.lanes(lanes)` inside a workflow script when several independent lanes each have ordered stages. This helper composes the existing workflow child runner; it does not add a top-level `lanes` parameter or a second persistence/cleanup system.
57
61
 
58
- ```js
59
- { workflowScript: `
60
- const board = await runs.lanes([
61
- { key: "api", stages: [
62
- { key: "writer", agent: "worker", task: "Implement the API change" },
63
- { key: "challenge", resume: "previous", task: "Challenge the implementation" },
64
- { key: "review", agent: "reviewer", task: "Review the API lane" }
65
- ] },
66
- { key: "ui", stages: [
67
- { key: "writer", agent: "worker", task: "Implement the UI change" },
68
- { key: "review", agent: "reviewer", task: "Review the UI lane" }
69
- ] }
70
- ]);
71
- return board.map((lane) => ({
72
- key: lane.key,
73
- state: lane.state,
74
- failedStage: lane.failedStage,
75
- stages: lane.stages.map((stage) => ({
76
- key: stage.key,
77
- state: stage.state,
78
- ok: stage.ok,
79
- runId: stage.runId,
80
- outputReference: stage.outputReference,
81
- verdict: stage.verdict
82
- }))
83
- }));
84
- ` }
85
- ```
62
+ See the [staged-lane example](workflows.md#parallel-sequential-lanes) (guide topic `workflows`).
86
63
 
87
64
  The first stage of each lane is launched by one existing `runs.all(...)` batch. Later stages run in lane order. Set `resume: "previous"` on a later stage to continue the preceding retained child; the helper requires that child’s returned `runId` and delegates to the existing resume checks. Stage keys are local to the lane, and generated child keys are `<lane>.<stage>`.
88
65
 
@@ -98,8 +75,8 @@ The complete plain-JSON inventory is validated before the first launch (maximum
98
75
  | `topic` | `overview \| workflows \| agents \| missions \| observability \| tool-reference \| configuration \| models \| watchdog \| extension-api \| council` | `overview` | Packaged guide topic for `action: "guide"`. |
99
76
  | `config` | object/string | - | Agent config for management create/update. |
100
77
  | `context` | `fresh \| fork \| profile` | global or per-agent default, else `fresh` | Explicit `fresh` or `fork` overrides every workflow child. `profile` requires the selected agent's declared `defaultContext` and ignores config `defaultSubagentContext`; missing agent defaults fail. When omitted, [`defaultSubagentContext`](configuration.md#defaultsubagentcontext) wins over each agent's `defaultContext`; implicit fork falls back to fresh without a persisted parent session and leaf. Explicit fork is strict. Packaged `worker` defaults to `fresh`; packaged `oracle` and `advisor` default to `fork`. |
101
- | `model` | string | agent default | Call `{action:"models"}` first and copy an exact `provider/id`; bare ids resolve only if unique, and agent names are not model ids. A suffix such as `provider/id:high` (`off/minimal/low/medium/high/xhigh/max`) overrides agent thinking. The `thinking` field is only for `watchdog.configure`, ignored on dispatch. |
102
- | `missionId` | string | - | Attach a workflow to an existing project mission instead of creating its default enclosing mission. |
78
+ | `model` | string | agent default | Call `{action:"models"}` first and copy an exact `provider/id`; bare ids resolve only if unique, and agent names are not model ids. A suffix such as `provider/id:high` (`off/minimal/low/medium/high/xhigh/max`) overrides agent thinking. The `thinking` field is only for `watchdog.configure`, ignored on dispatch. On the `claude-code` and `claude-code-writer` adapters the model is Claude Code's own alias or id, and the same suffix becomes its `--effort`, bounded by `subagents.maxThinking` and by an enforced `subagents.modelScope`. |
79
+ | `missionId` | string | - | Attach a workflow, including each fire of `schedule.create`, to an existing project mission. Scheduled attachment uses ordinary lifecycle and retention rules. |
103
80
  | `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. |
104
81
  | `handoffPath` | string | - | Aggregate handoff manifest for `action: "worktree.discard"` or lane evidence actions, or optional explicit metadata for `action: "worktree.cleanup"`. |
105
82
  | `repo` | string | runtime cwd | Repository path for `action: "worktree.cleanup"`; plan mode only. The configured worktree base filters candidates by their per-project folder under it but never discovers them. |
@@ -114,19 +91,19 @@ The complete plain-JSON inventory is validated before the first launch (maximum
114
91
  | `agentScope` | `user \| project \| both` | `both` | Agent discovery scope. Project wins on collisions. |
115
92
  | `capabilities` | boolean | `false` | With `action: "list"`, return compact prompt-free rows and `details.agentCapabilities` machine-readable records for each agent's declared/default routing capabilities. External CLI rows also include their command and passive local availability. |
116
93
  | `async` | boolean | default-on | Background execution. Workflows default to background. `async:false` blocks the parent until completion. A local foreground child runs inside the parent Pi process and never loads the parent's ambient extensions, but it does inherit the providers those extensions registered. A pane-native remote foreground child instead uses the remote machine's provider discovery and configuration. Agents that need MCP tools (`mcpDirectTools`, or MCP tools from an ambient adapter such as pi-mcp-adapter) must run as background children, which load them inside the detached runner process. |
117
- | `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. |
94
+ | `chatProgress` | `auto \| off \| live-card` | `auto` | Workflow 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. |
118
95
  | `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. |
119
- | `baseRef` | string | `HEAD` | `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. The ref must resolve to a commit at worktree allocation; omitted values default to `HEAD` resolved at that time. Source-checkout cleanliness is still checked. For workflowScript, set it on the outer request as a default or on an individual `runs.run`/`runs.all` child to override it. |
96
+ | `baseRef` | string | `HEAD` | `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. The ref must resolve to a commit at worktree allocation; omitted values default to `HEAD` resolved at that time. Source-checkout cleanliness is still checked. For a workflow script, set it on the outer request as a default or on an individual `runs.run`/`runs.all` child to override it. |
120
97
  | `timeoutMs` / `maxRuntimeMs` | number | config `timeoutMs`, else 30 min foreground / single-agent async | Optional run-level max runtime in milliseconds. Both aliases may be supplied only when their values agree. 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. Expiration of this run-level deadline is terminal. |
121
98
  | `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 `bg_wait` are exempt. |
122
99
  | `checkpointBeforeDeadlineMs` | number | none | Async single-agent runs only. The runner requests that the child "checkpoint and stop" this many milliseconds before the run deadline (finish the current tool call, report changed files, build/test state, remaining work, commit/PR state; start no new work). This best-effort steer uses the normal steering lifecycle at the next tool boundary, so the receipt is visible in status and events; the ordinary deadline kill still applies. Precedence: call value → config `checkpointBeforeDeadlineMs`. Disarmed when the deadline leaves under one second before the checkpoint. |
123
100
  | `toolBudget` | object | none | Optional child tool-call budget `{ soft?, hard, block? }`: `hard` is a positive integer and `soft`, if set, is a positive integer no greater than `hard`. At `soft` the child is nudged to finalize. After `hard`, configured tools are blocked; `block` defaults to `read`, `grep`, `find`, and `ls`, and accepts either a nonempty array of tool names or `"*"` for every tool call. Final assistant text is never blocked. |
124
101
  | `usageBudget` | object | none | Optional root-only reported-usage budget `{ tokens?: { soft?, hard }, costUsd?: { soft?, hard } }`. Include at least one metric; each `hard` must be positive and each optional `soft` positive and no greater than its `hard`. Soft limits are status-only. Hard limits prevent later child launches after reported usage is reconciled; already-running children are not stopped and no reservations are made. |
125
102
  | `extensionBindings` | object | none | Child-only plain JSON keyed by namespaces like `package.name/1` (positive version): at most 16 namespaces, nesting depth 16, 256 total object properties, and 16 KiB of canonical UTF-8 JSON. Native Pi launches only; external runners and retained resumes reject it. |
126
- | `preflight` | object | none | Advisory lane hints for `workflowScript` or `workflowScriptPath` only; not accepted with named workflows or direct children. See [bounded workflows](workflows.md#opt-in-bounded-workflows). |
103
+ | `preflight` | object | none | Advisory lane hints for raw scripts (`workflow: true` or a script path) only; not accepted with named workflows or direct children. See [bounded workflows](workflows.md#opt-in-bounded-workflows). |
127
104
  | `cwd` | string | runtime cwd | Override working directory. With `machine`, the directory on that machine. |
128
105
  | `machine` | string | - | Herdr saved machine (label or profile id) for external-cli agents; see [agents.md](agents.md#running-external-cli-agents-on-a-herdr-saved-machine). |
129
- | `maxOutput` | object | none | Final output truncation limits `{ bytes?, lines? }`. For child runs it is only applied when set; there is no default cap on the inline path, so use `outputMode: "file-only"` for large outputs. For `workflowScript` results, the Return, Emitted, and Console sections are always capped, at 200 KB / 5000 lines unless `maxOutput` sets other limits. |
106
+ | `maxOutput` | object | none | Final output truncation limits `{ bytes?, lines? }`. For child runs it is only applied when set; there is no default cap on the inline path, so use `outputMode: "file-only"` for large outputs. For workflow script results, the Return, Emitted, and Console sections are always capped, at 200 KB / 5000 lines unless `maxOutput` sets other limits. |
130
107
  | `artifacts` | boolean | true | Write debug artifacts. |
131
108
  | `includeProgress` | boolean | false | Include full progress in result. |
132
109
  | `share` | boolean | false | Upload session export to GitHub Gist. |
@@ -156,26 +133,24 @@ The workflow trace records the attempt and receipt. Always await, return, or inc
156
133
 
157
134
  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.
158
135
 
159
- ```js
160
- { workflowScript: `
161
- let pending = [
162
- { key: "writer", promise: runs.run("writer", { agent: "worker", task: "Draft the fix" }).then((result) => ({ key: "writer", result })) },
163
- { key: "reviewer", promise: runs.run("reviewer", { agent: "reviewer", task: "Review likely risks" }).then((result) => ({ key: "reviewer", result })) }
164
- ];
165
- const first = await Promise.race(pending.map((child) => child.promise));
166
- pending = pending.filter((child) => child.key !== first.key);
167
- const target = pending[0];
168
- const receipt = await runs.steer(target.key, "Use this early review:\n" + first.result.output, { mode: "auto" });
169
- const rest = await Promise.all(pending.map((child) => child.promise));
170
- return { first: first.key, rest: rest.map((child) => child.key), receipt };
171
- ` }
136
+ ```js workflow
137
+ let pending = [
138
+ { key: "writer", promise: runs.run("writer", { agent: "worker", task: "Draft the fix" }).then((result) => ({ key: "writer", result })) },
139
+ { key: "reviewer", promise: runs.run("reviewer", { agent: "reviewer", task: "Review likely risks" }).then((result) => ({ key: "reviewer", result })) }
140
+ ];
141
+ const first = await Promise.race(pending.map((child) => child.promise));
142
+ pending = pending.filter((child) => child.key !== first.key);
143
+ const target = pending[0];
144
+ const receipt = await runs.steer(target.key, "Use this early review:\n" + first.result.output, { mode: "auto" });
145
+ const rest = await Promise.all(pending.map((child) => child.promise));
146
+ return { first: first.key, rest: rest.map((child) => child.key), receipt };
172
147
  ```
173
148
 
174
149
  ### Output mode details
175
150
 
176
151
  Use `outputMode: "file-only"` when a saved output may be large and the parent only needs a pointer. The returned text is a compact reference like `Output saved to: /abs/report.md (48.2 KB, 2847 lines). Read this file if needed.` Failed runs and save errors still return normal inline output for debugging.
177
152
 
178
- In workflowScript, give each child an explicit output path when later script steps need a durable file reference. A child with only read-only tools does not need direct filesystem access for `output`: it returns the complete artifact in its final response and the runtime persists it. Children with mutation-capable tools retain the direct-write instruction.
153
+ In a workflow script, give each child an explicit output path when later script steps need a durable file reference. A child with only read-only tools does not need direct filesystem access for `output`: it returns the complete artifact in its final response and the runtime persists it. Children with mutation-capable tools retain the direct-write instruction.
179
154
 
180
155
  The `output` field is the API binding; a filename mentioned in task text (for example, `Write your findings to exactly this path: report.md`) is only instruction and does not override runtime routing. When a later workflow step or parent needs a durable file, set `output` on `runs.run`/`runs.all` and return the child’s `outputReference`, `outputPathMapping`, or `artifactPaths`; arbitrary literal strings returned by workflow JavaScript are not rewritten. Omitted child output may use a managed aggregate-derived sibling path.
181
156
 
@@ -185,20 +160,18 @@ Workflows get `await state.get(key)` and `await state.set(key, value)` through t
185
160
 
186
161
  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. This workflow-only roster is not an exhaustive list of direct native children. Resume only rows reported `resumable`. When the exact run id of an intended direct child is known, inspect it with `{ action: "status", id: "<run-id>" }`; if status identifies the candidate, attempt `{ action: "resume", id: "<run-id>", message: "..." }`. Resume performs the authoritative eligibility check and may reject the attempt. Start a same-role fallback challenge, labeled as fallback, only when no known candidate exists or resume rejects eligibility. A later workflow continues a resumable child by passing `resume` instead of `agent`:
187
162
 
188
- ```js
189
- { workflowScript: `
190
- let writer = await runs.run("implement", { agent: "worker", task: "Implement the accepted contract" });
191
- for (const pass of [1, 2]) {
192
- if (!writer.runId) throw new Error("writer did not return a retained run id");
193
- writer = await runs.run("followup-" + pass, { resume: writer.runId, task: "Revisit pass " + pass + ": " + writer.output });
194
- }
195
- return writer;
196
- ` }
163
+ ```js workflow
164
+ let writer = await runs.run("implement", { agent: "worker", task: "Implement the accepted contract" });
165
+ for (const pass of [1, 2]) {
166
+ if (!writer.runId) throw new Error("writer did not return a retained run id");
167
+ writer = await runs.run("followup-" + pass, { resume: writer.runId, task: "Revisit pass " + pass + ": " + writer.output });
168
+ }
169
+ return writer;
197
170
  ```
198
171
 
199
172
  Each workflow key identifies one result lane. Use a new stable workflow key for every distinct retained resume pass; same-key calls are reused only when launch parameters are identical, and incompatible parameters are rejected.
200
173
 
201
- Inside `workflowScript`, `await runs.run(key, { resume, task })` waits for the revived child to finish and returns its completed output and new `runId`. Each resume can return a new retained run id, so loops must continue from the latest returned `runId`. Top-level `{ action: "resume" }` remains detached and returns a background-run receipt.
174
+ Inside a workflow script, `await runs.run(key, { resume, task })` waits for the revived child to finish and returns its completed output and new `runId`. Each resume can return a new retained run id, so loops must continue from the latest returned `runId`. Top-level `{ action: "resume" }` remains detached and returns a background-run receipt. When it revives a failed async workflow child (or a failed revival of one), the revived run becomes that key's latest run: workflow `status` adds `Revived → <run>: <state>` under the key, and keyed `resume: { workflowRunId, key, latest: true }` continues from it with lineage `[original, revived, ...]`. If the workflow was still running, the receipt it writes when it ends adds the revived runs to the key's lineage, and its completion notice keeps the child's failed result with the same `Revived →` line. The workflow's return value and state are not changed, notices already delivered are not rewritten, and a key accepts at most 16 chained revivals. Reviving a completed, paused, or stopped child does not change its key.
202
175
 
203
176
  For a simple implementation challenge outside a workflow script, send the challenge through `subagent({ action: "resume", id: "<retained-writer-run>", message: "Reconsider the implementation and make any better current-scope change." })` when `children.list` reports that retained writer as `resumable`, or attempt it after exact-id status inspection identifies a known direct-child candidate. Status is advisory; resume authoritatively checks eligibility and may reject missing recovery requirements. If there is no known candidate or resume rejects eligibility, start a same-role fallback challenge and record why it is a fallback. Use workflow `runs.run({ resume })` only when the script must await the revived writer output before the next step. Do not use `steer` as the sole challenge action for a completed retained child; `steer` with `mode: "follow_up"` only queues text for the next `resume`.
204
177
 
@@ -273,7 +246,7 @@ Rules:
273
246
 
274
247
  ### Schedule controls
275
248
 
276
- Use `schedule.create` with `workflowScript` or `workflowScriptPath`, not a direct child. `at` accepts a delay like `+10m` or an ISO timestamp with timezone; `every` accepts fixed intervals. `sessionOnly:true` binds restoration/execution to the creating session file; omitted/false is project-wide. Recurring `quiet:true` keeps successful automatic fires visible without a parent turn; failed, stopped or paused runs still wake the parent. One-shot `at` and manual `schedule.run` stay noisy unless that launch passes `quiet:true`. See [missions and schedules](missions.md#schedules) for examples and list/show/history/pause/resume/run/run-due/delete. Calendar selectors (`on`, `timezone`) and schedule mission attachment are deferred. `baseRef` resolves only at worktree allocation and still requires a clean source checkout.
249
+ Use `schedule.create` with `workflow: true` or a script path, not a direct child. `at` accepts a delay like `+10m` or an ISO timestamp with timezone; `every` accepts fixed intervals. `sessionOnly:true` binds restoration/execution to the creating session file; omitted/false is project-wide. Recurring `quiet:true` keeps successful automatic fires visible without a parent turn; failed, stopped or paused runs still wake the parent. One-shot `at` and manual `schedule.run` stay noisy unless that launch passes `quiet:true`. An optional `missionId` attaches an existing mission on every fire and exposes its workflow state, using ordinary mission lifecycle and retention rules; `mission.close` does not pause the schedule. See [missions and schedules](missions.md#schedules) for examples and list/show/history/pause/resume/run/run-due/delete. Calendar selectors (`on`, `timezone`) are deferred. `baseRef` resolves only at worktree allocation and still requires a clean source checkout.
277
250
 
278
251
  ## Lane merge evidence and cleanup eligibility
279
252
 
@@ -347,6 +320,31 @@ subagent({ action: "doctor" })
347
320
  - Inside child-safe fanout mode, bare `status` requires an id when no local foreground run is active, so children cannot enumerate unrelated top-level async runs.
348
321
  - Bare `interrupt` still targets only the visible top-level run; interrupting a nested run requires its explicit nested id.
349
322
 
323
+ ### Command controls
324
+
325
+ Command controls target direct local native Pi children that explicitly select both `bash` and `subagent_command`, in foreground or async runs. They require the owning parent session, a run `id`, an explicit `index` for multi-child runs, and the exact `toolCallId` for mutations. Nested, external, and custom shell backends do not expose this controller. Custom bash extensions keep their own registered `subagent_command` tool. If a custom bash backend does not provide that tool, an explicitly required `subagent_command` remains available and reports an unsupported-backend error. Native supervisor command controls are unavailable for custom backends.
326
+
327
+ ```ts
328
+ subagent({ action: "command.status", id: "<run-id>", index: 0 })
329
+ subagent({ action: "command.yield", id: "<run-id>", index: 0, toolCallId: "<call-id>" })
330
+ subagent({ action: "command.cancel", id: "<run-id>", index: 0, toolCallId: "<call-id>" })
331
+ subagent({ action: "command.status", id: "<run-id>", index: 0, toolCallId: "<call-id>" })
332
+ ```
333
+
334
+ `command.yield` releases the selected blocking tool call without restarting the process. `command.cancel` aborts only that command through Pi's native bash cancellation; the child receives a tool error and can continue. `cancel_requested` acknowledges the request, while `cancelled` confirms the backend settled. A stale id never targets a later command. Run-scoped `interrupt` remains separate.
335
+
336
+ Enable command controls by including both `bash` and `subagent_command` in the agent or call's `tools` allowlist. Tool ceilings and `excludeTools` still apply. Without this explicit selection, bash keeps its original tool schema and backend; no command controller, inbox watcher, or command-state writes are added. Inside an enabled child:
337
+
338
+ ```ts
339
+ bash({ command: "npm run dev", yieldTimeMs: 1000 })
340
+ subagent_command({ action: "status", toolCallId: "<returned-call-id>", waitMs: 1000 })
341
+ subagent_command({ action: "cancel", toolCallId: "<returned-call-id>", waitMs: 1000 })
342
+ ```
343
+
344
+ `yieldTimeMs` and `waitMs` range from 0 to 30,000 milliseconds. Omitting `yieldTimeMs` preserves completion-waiting behavior. Yielded handles are not successful exits. Status includes running/yielded/cancel-requested states and completed/failed/cancelled terminal states, a bounded output tail, and Pi's full-output file path when available. Recent terminal history retains 20 commands; active commands remain tracked.
345
+
346
+ Commands remain owned by the child session. Normal completion with unfinished commands cancels them and fails the run. Interrupt, stop, timeout, and disposal clean up owned commands. Controls do not transfer processes out of the child or create detached services. Native bash `timeout` remains command-scoped; `toolTimeoutMs` covers an open tool call and does not become a yielded process deadline.
347
+
350
348
  ### resume
351
349
 
352
350
  `resume` revives a paused, completed, or failed async/foreground child by starting a new child from its stored session file. Stopped runs remain non-resumable, and it does not interrupt a live top-level async child. Use `steer` for acknowledged live async guidance.
@@ -406,8 +404,8 @@ Prefer an inline JSON object. JSON-encoded object strings are tolerated only dur
406
404
 
407
405
  When one host-run command is the entire verification contract, use the `gate` shorthand instead of a full `acceptance` object:
408
406
 
409
- ```js
410
- { workflowScript: `return runs.run("impl", { agent: "worker", task: "Implement the fix", gate: "npm test" })` }
407
+ ```js workflow
408
+ return runs.run("impl", { agent: "worker", task: "Implement the fix", gate: "npm test" });
411
409
  ```
412
410
 
413
411
  `gate` normalizes to verified acceptance with that single command, so the runtime executes it on the host and records the result as evidence. Verification results are memoized per tracked workspace state and effective environment, so an unchanged tree does not rerun the same command. Use explicit `acceptance.verify` when you need multiple commands, timeouts, or custom criteria. `gate` rejects `acceptance` except `false` (treated as omitted), and rejects retained `resume` items. With `worktree: true`, the gate runs inside the child's managed worktree.
@@ -416,12 +414,12 @@ When one host-run command is the entire verification contract, use the `gate` sh
416
414
 
417
415
  A gate given as `{ command, output: "json" }` runs like a string gate, and then parses the command's stdout:
418
416
 
419
- ```js
420
- { workflowScript: `return runs.run("review", {
417
+ ```js workflow
418
+ return runs.run("review", {
421
419
  agent: "reviewer", task: "Review the change", output: "reports/review.md", outputMode: "file-only",
422
420
  gate: { command: "classify --report reports/review.md", output: "json",
423
421
  schema: { type: "object", properties: { verdict: { enum: ["ok", "blocked"] } }, required: ["verdict"] } }
424
- })` }
422
+ });
425
423
  ```
426
424
 
427
425
  - The command runs after the child's output file is saved, in the child's cwd or managed worktree, so it can read what the child wrote.
@@ -516,8 +514,12 @@ Intentionally unsupported: native Pi child options such as model override, struc
516
514
 
517
515
  Pass `share: true` to export a full session to HTML, upload it to a secret GitHub Gist through your `gh` credentials, and return a `https://shittycodingagent.ai/session/?<gistId>` URL.
518
516
 
519
- ```ts
520
- { workflowScript: `return runs.run("main", { agent: "scout", task: "..." })`, share: true }
517
+ ```js workflow
518
+ return runs.run("main", { agent: "scout", task: "..." });
519
+ ```
520
+
521
+ ```js
522
+ subagent({ workflow: true, share: true });
521
523
  ```
522
524
 
523
525
  This is disabled by default. Session data may contain source code, paths, environment variables, credentials, or other sensitive output. You need `gh` installed and authenticated.