pi-subagents 0.76.1 → 0.78.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 (391) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +1 -1
  3. package/agents/evidence-auditor.md +1 -0
  4. package/agents/oracle.md +9 -19
  5. package/agents/researcher.md +0 -3
  6. package/agents/reviewer.md +18 -61
  7. package/agents/scout.md +1 -4
  8. package/agents/worker.md +8 -25
  9. package/docs/agents.md +71 -4
  10. package/docs/configuration.md +75 -10
  11. package/docs/extension-api.md +38 -9
  12. package/docs/missions.md +24 -17
  13. package/docs/models.md +1 -1
  14. package/docs/observability.md +12 -5
  15. package/docs/tool-reference.md +103 -85
  16. package/docs/watchdog.md +8 -6
  17. package/docs/workflows.md +17 -8
  18. package/package.json +1 -1
  19. package/prompts/council.md +1 -1
  20. package/skills/pi-subagents/SKILL.md +2 -2
  21. package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  22. package/skills/pi-subagents/references/execution-controls.md +16 -16
  23. package/skills/pi-subagents/references/management-authoring-rpc.md +16 -12
  24. package/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
  25. package/skills/pi-subagents/references/prompting-and-roles.md +3 -3
  26. package/src/agents/advertised-agent-prompt.d.ts +5 -1
  27. package/src/agents/advertised-agent-prompt.d.ts.map +1 -1
  28. package/src/agents/advertised-agent-prompt.js +8 -2
  29. package/src/agents/advertised-agent-prompt.js.map +1 -1
  30. package/src/agents/agent-management.d.ts.map +1 -1
  31. package/src/agents/agent-management.js +5 -0
  32. package/src/agents/agent-management.js.map +1 -1
  33. package/src/agents/agent-scope.d.ts +1 -0
  34. package/src/agents/agent-scope.d.ts.map +1 -1
  35. package/src/agents/agent-scope.js +3 -0
  36. package/src/agents/agent-scope.js.map +1 -1
  37. package/src/agents/agent-serializer.d.ts.map +1 -1
  38. package/src/agents/agent-serializer.js +3 -0
  39. package/src/agents/agent-serializer.js.map +1 -1
  40. package/src/agents/agents.d.ts +14 -2
  41. package/src/agents/agents.d.ts.map +1 -1
  42. package/src/agents/agents.js +94 -36
  43. package/src/agents/agents.js.map +1 -1
  44. package/src/api/external-runs.d.ts.map +1 -1
  45. package/src/api/external-runs.js +5 -4
  46. package/src/api/external-runs.js.map +1 -1
  47. package/src/api/preflight.d.ts +2 -0
  48. package/src/api/preflight.d.ts.map +1 -1
  49. package/src/api/preflight.js +7 -3
  50. package/src/api/preflight.js.map +1 -1
  51. package/src/extension/active-runs-prompt.d.ts +8 -0
  52. package/src/extension/active-runs-prompt.d.ts.map +1 -0
  53. package/src/extension/active-runs-prompt.js +30 -0
  54. package/src/extension/active-runs-prompt.js.map +1 -0
  55. package/src/extension/config.d.ts.map +1 -1
  56. package/src/extension/config.js +29 -2
  57. package/src/extension/config.js.map +1 -1
  58. package/src/extension/control-notices.d.ts +0 -2
  59. package/src/extension/control-notices.d.ts.map +1 -1
  60. package/src/extension/control-notices.js +1 -7
  61. package/src/extension/control-notices.js.map +1 -1
  62. package/src/extension/declaration-pinning.d.ts +19 -0
  63. package/src/extension/declaration-pinning.d.ts.map +1 -0
  64. package/src/extension/declaration-pinning.js +154 -0
  65. package/src/extension/declaration-pinning.js.map +1 -0
  66. package/src/extension/fanout-child.d.ts.map +1 -1
  67. package/src/extension/fanout-child.js +6 -8
  68. package/src/extension/fanout-child.js.map +1 -1
  69. package/src/extension/index.d.ts +1 -2
  70. package/src/extension/index.d.ts.map +1 -1
  71. package/src/extension/index.js +104 -121
  72. package/src/extension/index.js.map +1 -1
  73. package/src/extension/rpc.d.ts.map +1 -1
  74. package/src/extension/rpc.js +12 -6
  75. package/src/extension/rpc.js.map +1 -1
  76. package/src/extension/schemas.d.ts +137 -1
  77. package/src/extension/schemas.d.ts.map +1 -1
  78. package/src/extension/schemas.js +22 -22
  79. package/src/extension/schemas.js.map +1 -1
  80. package/src/extension/subagent-guide.d.ts +1 -0
  81. package/src/extension/subagent-guide.d.ts.map +1 -1
  82. package/src/extension/subagent-guide.js +80 -6
  83. package/src/extension/subagent-guide.js.map +1 -1
  84. package/src/extension/subagent-options.d.ts +8 -0
  85. package/src/extension/subagent-options.d.ts.map +1 -0
  86. package/src/extension/subagent-options.js +52 -0
  87. package/src/extension/subagent-options.js.map +1 -0
  88. package/src/extension/tool-activation.d.ts.map +1 -1
  89. package/src/extension/tool-activation.js +7 -4
  90. package/src/extension/tool-activation.js.map +1 -1
  91. package/src/extension/tool-description.d.ts +4 -7
  92. package/src/extension/tool-description.d.ts.map +1 -1
  93. package/src/extension/tool-description.js +45 -59
  94. package/src/extension/tool-description.js.map +1 -1
  95. package/src/inspectors/actions.d.ts +2 -0
  96. package/src/inspectors/actions.d.ts.map +1 -1
  97. package/src/inspectors/actions.js +41 -13
  98. package/src/inspectors/actions.js.map +1 -1
  99. package/src/inspectors/plugins.d.ts.map +1 -1
  100. package/src/inspectors/plugins.js +3 -2
  101. package/src/inspectors/plugins.js.map +1 -1
  102. package/src/inspectors/tmux/actions.d.ts +26 -0
  103. package/src/inspectors/tmux/actions.d.ts.map +1 -0
  104. package/src/inspectors/tmux/actions.js +182 -0
  105. package/src/inspectors/tmux/actions.js.map +1 -0
  106. package/src/inspectors/tmux/client.d.ts +28 -0
  107. package/src/inspectors/tmux/client.d.ts.map +1 -0
  108. package/src/inspectors/tmux/client.js +65 -0
  109. package/src/inspectors/tmux/client.js.map +1 -0
  110. package/src/inspectors/tmux/plugin.d.ts +8 -0
  111. package/src/inspectors/tmux/plugin.d.ts.map +1 -0
  112. package/src/inspectors/tmux/plugin.js +15 -0
  113. package/src/inspectors/tmux/plugin.js.map +1 -0
  114. package/src/integrations/program-status.d.ts +31 -0
  115. package/src/integrations/program-status.d.ts.map +1 -0
  116. package/src/integrations/program-status.js +189 -0
  117. package/src/integrations/program-status.js.map +1 -0
  118. package/src/intercom/native-supervisor-channel.d.ts +16 -1
  119. package/src/intercom/native-supervisor-channel.d.ts.map +1 -1
  120. package/src/intercom/native-supervisor-channel.js +101 -8
  121. package/src/intercom/native-supervisor-channel.js.map +1 -1
  122. package/src/intercom/result-intercom.js +1 -1
  123. package/src/intercom/result-intercom.js.map +1 -1
  124. package/src/intercom/supervisor-ui.d.ts +2 -12
  125. package/src/intercom/supervisor-ui.d.ts.map +1 -1
  126. package/src/intercom/supervisor-ui.js +2 -154
  127. package/src/intercom/supervisor-ui.js.map +1 -1
  128. package/src/missions/store.d.ts.map +1 -1
  129. package/src/missions/store.js +27 -5
  130. package/src/missions/store.js.map +1 -1
  131. package/src/runs/background/active-async-capacity.d.ts +2 -1
  132. package/src/runs/background/active-async-capacity.d.ts.map +1 -1
  133. package/src/runs/background/active-async-capacity.js +10 -6
  134. package/src/runs/background/active-async-capacity.js.map +1 -1
  135. package/src/runs/background/async-execution.d.ts +9 -1
  136. package/src/runs/background/async-execution.d.ts.map +1 -1
  137. package/src/runs/background/async-execution.js +253 -144
  138. package/src/runs/background/async-execution.js.map +1 -1
  139. package/src/runs/background/async-job-tracker.d.ts +4 -1
  140. package/src/runs/background/async-job-tracker.d.ts.map +1 -1
  141. package/src/runs/background/async-job-tracker.js +3 -2
  142. package/src/runs/background/async-job-tracker.js.map +1 -1
  143. package/src/runs/background/async-resume.d.ts.map +1 -1
  144. package/src/runs/background/async-resume.js +9 -3
  145. package/src/runs/background/async-resume.js.map +1 -1
  146. package/src/runs/background/await-async-run.d.ts +8 -4
  147. package/src/runs/background/await-async-run.d.ts.map +1 -1
  148. package/src/runs/background/await-async-run.js +14 -8
  149. package/src/runs/background/await-async-run.js.map +1 -1
  150. package/src/runs/background/binary-bootstrap.d.ts.map +1 -1
  151. package/src/runs/background/binary-bootstrap.js +2 -1
  152. package/src/runs/background/binary-bootstrap.js.map +1 -1
  153. package/src/runs/background/chain-root-attachment.d.ts +2 -0
  154. package/src/runs/background/chain-root-attachment.d.ts.map +1 -1
  155. package/src/runs/background/chain-root-attachment.js +9 -6
  156. package/src/runs/background/chain-root-attachment.js.map +1 -1
  157. package/src/runs/background/control-channel.d.ts +1 -0
  158. package/src/runs/background/control-channel.d.ts.map +1 -1
  159. package/src/runs/background/control-channel.js +27 -3
  160. package/src/runs/background/control-channel.js.map +1 -1
  161. package/src/runs/background/fleet-view.js +5 -5
  162. package/src/runs/background/fleet-view.js.map +1 -1
  163. package/src/runs/background/notify.d.ts +6 -1
  164. package/src/runs/background/notify.d.ts.map +1 -1
  165. package/src/runs/background/notify.js +130 -14
  166. package/src/runs/background/notify.js.map +1 -1
  167. package/src/runs/background/result-files.d.ts +27 -0
  168. package/src/runs/background/result-files.d.ts.map +1 -1
  169. package/src/runs/background/result-files.js +100 -6
  170. package/src/runs/background/result-files.js.map +1 -1
  171. package/src/runs/background/result-watcher.d.ts.map +1 -1
  172. package/src/runs/background/result-watcher.js +60 -64
  173. package/src/runs/background/result-watcher.js.map +1 -1
  174. package/src/runs/background/resume-guidance.js +2 -2
  175. package/src/runs/background/resume-guidance.js.map +1 -1
  176. package/src/runs/background/run-child-session.d.ts.map +1 -1
  177. package/src/runs/background/run-child-session.js +9 -15
  178. package/src/runs/background/run-child-session.js.map +1 -1
  179. package/src/runs/background/run-id-resolver.d.ts.map +1 -1
  180. package/src/runs/background/run-id-resolver.js +9 -6
  181. package/src/runs/background/run-id-resolver.js.map +1 -1
  182. package/src/runs/background/run-status.d.ts.map +1 -1
  183. package/src/runs/background/run-status.js +19 -15
  184. package/src/runs/background/run-status.js.map +1 -1
  185. package/src/runs/background/runner-aliases.d.ts.map +1 -1
  186. package/src/runs/background/runner-aliases.js +11 -8
  187. package/src/runs/background/runner-aliases.js.map +1 -1
  188. package/src/runs/background/runner-child-launch.d.ts.map +1 -1
  189. package/src/runs/background/runner-child-launch.js +1 -0
  190. package/src/runs/background/runner-child-launch.js.map +1 -1
  191. package/src/runs/background/runner-http-dispatcher.d.ts +3 -0
  192. package/src/runs/background/runner-http-dispatcher.d.ts.map +1 -1
  193. package/src/runs/background/runner-http-dispatcher.js +2 -0
  194. package/src/runs/background/runner-http-dispatcher.js.map +1 -1
  195. package/src/runs/background/scheduled-runs.d.ts +9 -0
  196. package/src/runs/background/scheduled-runs.d.ts.map +1 -1
  197. package/src/runs/background/scheduled-runs.js +124 -36
  198. package/src/runs/background/scheduled-runs.js.map +1 -1
  199. package/src/runs/background/subagent-runner-bootstrap.d.ts.map +1 -1
  200. package/src/runs/background/subagent-runner-bootstrap.js +5 -1
  201. package/src/runs/background/subagent-runner-bootstrap.js.map +1 -1
  202. package/src/runs/background/subagent-runner.d.ts +3 -1
  203. package/src/runs/background/subagent-runner.d.ts.map +1 -1
  204. package/src/runs/background/subagent-runner.js +211 -283
  205. package/src/runs/background/subagent-runner.js.map +1 -1
  206. package/src/runs/background/subagent-wait.d.ts +2 -0
  207. package/src/runs/background/subagent-wait.d.ts.map +1 -1
  208. package/src/runs/background/subagent-wait.js +34 -20
  209. package/src/runs/background/subagent-wait.js.map +1 -1
  210. package/src/runs/background/terminal-run-index.d.ts +1 -0
  211. package/src/runs/background/terminal-run-index.d.ts.map +1 -1
  212. package/src/runs/background/terminal-run-index.js +47 -1
  213. package/src/runs/background/terminal-run-index.js.map +1 -1
  214. package/src/runs/background/wait-tool.d.ts.map +1 -1
  215. package/src/runs/background/wait-tool.js +36 -23
  216. package/src/runs/background/wait-tool.js.map +1 -1
  217. package/src/runs/background/workflow-terminal-proof.d.ts +1 -1
  218. package/src/runs/background/workflow-terminal-proof.d.ts.map +1 -1
  219. package/src/runs/background/workflow-terminal-proof.js +12 -3
  220. package/src/runs/background/workflow-terminal-proof.js.map +1 -1
  221. package/src/runs/foreground/async-stop-action.d.ts +7 -1
  222. package/src/runs/foreground/async-stop-action.d.ts.map +1 -1
  223. package/src/runs/foreground/async-stop-action.js +27 -23
  224. package/src/runs/foreground/async-stop-action.js.map +1 -1
  225. package/src/runs/foreground/execution.d.ts.map +1 -1
  226. package/src/runs/foreground/execution.js +91 -69
  227. package/src/runs/foreground/execution.js.map +1 -1
  228. package/src/runs/foreground/subagent-executor.d.ts +4 -1
  229. package/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  230. package/src/runs/foreground/subagent-executor.js +174 -79
  231. package/src/runs/foreground/subagent-executor.js.map +1 -1
  232. package/src/runs/shared/child-hooks.js +6 -5
  233. package/src/runs/shared/child-hooks.js.map +1 -1
  234. package/src/runs/shared/child-launch.d.ts +2 -0
  235. package/src/runs/shared/child-launch.d.ts.map +1 -1
  236. package/src/runs/shared/child-launch.js +1 -0
  237. package/src/runs/shared/child-launch.js.map +1 -1
  238. package/src/runs/shared/child-session.d.ts +4 -0
  239. package/src/runs/shared/child-session.d.ts.map +1 -1
  240. package/src/runs/shared/child-session.js +28 -23
  241. package/src/runs/shared/child-session.js.map +1 -1
  242. package/src/runs/shared/child-tool-plan.d.ts.map +1 -1
  243. package/src/runs/shared/child-tool-plan.js +11 -10
  244. package/src/runs/shared/child-tool-plan.js.map +1 -1
  245. package/src/runs/shared/dynamic-fanout.js +1 -1
  246. package/src/runs/shared/dynamic-fanout.js.map +1 -1
  247. package/src/runs/shared/effective-system-prompt.d.ts +4 -4
  248. package/src/runs/shared/effective-system-prompt.d.ts.map +1 -1
  249. package/src/runs/shared/effective-system-prompt.js +5 -4
  250. package/src/runs/shared/effective-system-prompt.js.map +1 -1
  251. package/src/runs/shared/external-cli-runner.d.ts.map +1 -1
  252. package/src/runs/shared/external-cli-runner.js +3 -2
  253. package/src/runs/shared/external-cli-runner.js.map +1 -1
  254. package/src/runs/shared/herdr-placed-run.d.ts +1 -0
  255. package/src/runs/shared/herdr-placed-run.d.ts.map +1 -1
  256. package/src/runs/shared/herdr-placed-run.js +10 -6
  257. package/src/runs/shared/herdr-placed-run.js.map +1 -1
  258. package/src/runs/shared/model-resolution-diagnostic.d.ts +17 -5
  259. package/src/runs/shared/model-resolution-diagnostic.d.ts.map +1 -1
  260. package/src/runs/shared/model-resolution-diagnostic.js +37 -10
  261. package/src/runs/shared/model-resolution-diagnostic.js.map +1 -1
  262. package/src/runs/shared/model-resolution.d.ts.map +1 -1
  263. package/src/runs/shared/model-resolution.js +29 -1
  264. package/src/runs/shared/model-resolution.js.map +1 -1
  265. package/src/runs/shared/nested-events.d.ts +2 -0
  266. package/src/runs/shared/nested-events.d.ts.map +1 -1
  267. package/src/runs/shared/nested-events.js +4 -0
  268. package/src/runs/shared/nested-events.js.map +1 -1
  269. package/src/runs/shared/orca-progress-tabs.js +1 -1
  270. package/src/runs/shared/orca-progress-tabs.js.map +1 -1
  271. package/src/runs/shared/parallel-handoff.js +2 -2
  272. package/src/runs/shared/parallel-handoff.js.map +1 -1
  273. package/src/runs/shared/parallel-utils.d.ts +2 -0
  274. package/src/runs/shared/parallel-utils.d.ts.map +1 -1
  275. package/src/runs/shared/parallel-utils.js.map +1 -1
  276. package/src/runs/shared/partial-output.d.ts +7 -0
  277. package/src/runs/shared/partial-output.d.ts.map +1 -1
  278. package/src/runs/shared/partial-output.js +7 -0
  279. package/src/runs/shared/partial-output.js.map +1 -1
  280. package/src/runs/shared/runner-launcher.d.ts +12 -0
  281. package/src/runs/shared/runner-launcher.d.ts.map +1 -0
  282. package/src/runs/shared/runner-launcher.js +25 -0
  283. package/src/runs/shared/runner-launcher.js.map +1 -0
  284. package/src/runs/shared/single-output.d.ts +31 -1
  285. package/src/runs/shared/single-output.d.ts.map +1 -1
  286. package/src/runs/shared/single-output.js +100 -21
  287. package/src/runs/shared/single-output.js.map +1 -1
  288. package/src/runs/shared/structured-output.d.ts +2 -0
  289. package/src/runs/shared/structured-output.d.ts.map +1 -1
  290. package/src/runs/shared/structured-output.js +26 -8
  291. package/src/runs/shared/structured-output.js.map +1 -1
  292. package/src/runs/shared/subagent-control.js +3 -3
  293. package/src/runs/shared/subagent-control.js.map +1 -1
  294. package/src/runs/shared/subagent-prompt-runtime.d.ts +37 -2
  295. package/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
  296. package/src/runs/shared/subagent-prompt-runtime.js +127 -20
  297. package/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
  298. package/src/runs/shared/tool-budget.js +1 -1
  299. package/src/runs/shared/tool-budget.js.map +1 -1
  300. package/src/runs/shared/workflow-async-child-guidance.js +1 -1
  301. package/src/runs/shared/workflow-async-child-guidance.js.map +1 -1
  302. package/src/runs/shared/worktree-cleanup-plan.js +1 -1
  303. package/src/runs/shared/worktree-cleanup-plan.js.map +1 -1
  304. package/src/shared/atomic-json.d.ts +4 -3
  305. package/src/shared/atomic-json.d.ts.map +1 -1
  306. package/src/shared/atomic-json.js +3 -1
  307. package/src/shared/atomic-json.js.map +1 -1
  308. package/src/shared/child-transcript.js +1 -1
  309. package/src/shared/child-transcript.js.map +1 -1
  310. package/src/shared/edit-distance.d.ts +5 -0
  311. package/src/shared/edit-distance.d.ts.map +1 -0
  312. package/src/shared/edit-distance.js +33 -0
  313. package/src/shared/edit-distance.js.map +1 -0
  314. package/src/shared/extension-context.d.ts +5 -0
  315. package/src/shared/extension-context.d.ts.map +1 -1
  316. package/src/shared/extension-context.js +7 -0
  317. package/src/shared/extension-context.js.map +1 -1
  318. package/src/shared/formatters.d.ts.map +1 -1
  319. package/src/shared/formatters.js +1 -6
  320. package/src/shared/formatters.js.map +1 -1
  321. package/src/shared/model-info.d.ts +8 -0
  322. package/src/shared/model-info.d.ts.map +1 -1
  323. package/src/shared/model-info.js +22 -0
  324. package/src/shared/model-info.js.map +1 -1
  325. package/src/shared/parent-wake.d.ts +7 -1
  326. package/src/shared/parent-wake.d.ts.map +1 -1
  327. package/src/shared/parent-wake.js +36 -12
  328. package/src/shared/parent-wake.js.map +1 -1
  329. package/src/shared/pruned-fork.d.ts +1 -1
  330. package/src/shared/pruned-fork.d.ts.map +1 -1
  331. package/src/shared/pruned-fork.js +6 -0
  332. package/src/shared/pruned-fork.js.map +1 -1
  333. package/src/shared/runtime-module-path.d.ts +11 -0
  334. package/src/shared/runtime-module-path.d.ts.map +1 -0
  335. package/src/shared/runtime-module-path.js +17 -0
  336. package/src/shared/runtime-module-path.js.map +1 -0
  337. package/src/shared/types.d.ts +33 -10
  338. package/src/shared/types.d.ts.map +1 -1
  339. package/src/shared/types.js +7 -4
  340. package/src/shared/types.js.map +1 -1
  341. package/src/shared/utils.d.ts +2 -0
  342. package/src/shared/utils.d.ts.map +1 -1
  343. package/src/shared/utils.js +10 -7
  344. package/src/shared/utils.js.map +1 -1
  345. package/src/slash/slash-commands.d.ts.map +1 -1
  346. package/src/slash/slash-commands.js +21 -10
  347. package/src/slash/slash-commands.js.map +1 -1
  348. package/src/slash/subagents-admin.d.ts.map +1 -1
  349. package/src/slash/subagents-admin.js +3 -1
  350. package/src/slash/subagents-admin.js.map +1 -1
  351. package/src/tui/fleet-status.d.ts +5 -0
  352. package/src/tui/fleet-status.d.ts.map +1 -1
  353. package/src/tui/fleet-status.js +24 -3
  354. package/src/tui/fleet-status.js.map +1 -1
  355. package/src/tui/fleet-transcript.d.ts +2 -0
  356. package/src/tui/fleet-transcript.d.ts.map +1 -1
  357. package/src/tui/fleet-transcript.js +7 -2
  358. package/src/tui/fleet-transcript.js.map +1 -1
  359. package/src/tui/fleet.d.ts.map +1 -1
  360. package/src/tui/fleet.js +11 -6
  361. package/src/tui/fleet.js.map +1 -1
  362. package/src/tui/render.d.ts +2 -2
  363. package/src/tui/render.d.ts.map +1 -1
  364. package/src/tui/render.js +19 -17
  365. package/src/tui/render.js.map +1 -1
  366. package/src/tui/subagent-messages.d.ts +23 -0
  367. package/src/tui/subagent-messages.d.ts.map +1 -0
  368. package/src/tui/subagent-messages.js +270 -0
  369. package/src/tui/subagent-messages.js.map +1 -0
  370. package/src/watchdog/change-signature.d.ts.map +1 -1
  371. package/src/watchdog/change-signature.js +1 -3
  372. package/src/watchdog/change-signature.js.map +1 -1
  373. package/src/watchdog/register-main.d.ts.map +1 -1
  374. package/src/watchdog/register-main.js +1 -19
  375. package/src/watchdog/register-main.js.map +1 -1
  376. package/src/watchdog/render.d.ts +2 -10
  377. package/src/watchdog/render.d.ts.map +1 -1
  378. package/src/watchdog/render.js +1 -19
  379. package/src/watchdog/render.js.map +1 -1
  380. package/src/watchdog/tool-actions.js +2 -2
  381. package/src/watchdog/tool-actions.js.map +1 -1
  382. package/src/workflows/scripted-workflow.d.ts.map +1 -1
  383. package/src/workflows/scripted-workflow.js +33 -10
  384. package/src/workflows/scripted-workflow.js.map +1 -1
  385. package/src/workflows/workflow-resources.d.ts.map +1 -1
  386. package/src/workflows/workflow-resources.js +12 -0
  387. package/src/workflows/workflow-resources.js.map +1 -1
  388. package/src/runs/shared/run-history.d.ts +0 -67
  389. package/src/runs/shared/run-history.d.ts.map +0 -1
  390. package/src/runs/shared/run-history.js +0 -270
  391. package/src/runs/shared/run-history.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { discoverAgents, findBlockingAgentDiagnostic, formatUnknownAgentError, resolveAgentName, unknownAgentDiagnosticContext } from "../../agents/agents.js";
4
+ import { discoverAgents, findBlockingAgentDiagnostic, formatAvailableAgentLines, formatUnknownAgentError, resolveAgentName, suggestAgentName, unknownAgentDiagnosticContext } from "../../agents/agents.js";
5
5
  import { getArtifactsDir, getProjectArtifactPackagingWarning, getProjectSubagentsDir } from "../../shared/artifacts.js";
6
6
  import { writeAtomicJson } from "../../shared/atomic-json.js";
7
7
  import { createCapacityResilientJsonWriter } from "../../shared/capacity-resilient-json.js";
@@ -10,7 +10,7 @@ import { resolveEffectiveThinking, toModelInfo } from "../../shared/model-info.j
10
10
  import { beginForegroundChild, finishForegroundChild, foregroundSchedulingSettled, settleForegroundSchedulingOwner, updateForegroundChild, } from "./foreground-control.js";
11
11
  import { getLivePromptAudit, rewritePromptWithGuidance, updateLiveEffectivePrompt } from "./prompt-audit.js";
12
12
  import { persistForegroundRunHistory, MAX_REMEMBERED_FOREGROUND_RUNS } from "./foreground-history.js";
13
- import { resolveExecutionAgentScope } from "../../agents/agent-scope.js";
13
+ import { projectScopeRequiresTrustMessage, resolveExecutionAgentScope } from "../../agents/agent-scope.js";
14
14
  import { handleManagementAction } from "../../agents/agent-management.js";
15
15
  import { handleRefinementAction } from "../../agents/agent-refinements.js";
16
16
  import { buildDoctorReport } from "../../extension/doctor.js";
@@ -27,7 +27,6 @@ import { normalizeParentModel, resolveEffectiveSubagentModel, resolveModelOrigin
27
27
  import { projectChainOutputSchemas, resolveEffectiveOutputSchema } from "../shared/child-launch-plan.js";
28
28
  import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.js";
29
29
  import { resolveModelScopesForAgent } from "../shared/model-scope.js";
30
- import { recordRun } from "../shared/run-history.js";
31
30
  import { getStepAgents, isParallelStep, isDynamicParallelStep, resolveExistingReadPaths, } from "../../shared/settings.js";
32
31
  import { normalizeSkillInput } from "../../agents/skills.js";
33
32
  import { buildAsyncRunnerSteps, DEFAULT_ASYNC_TIMEOUT_MS, executeAsyncChain, executeAsyncSingle, formatAsyncStartedMessage, isAsyncAvailable, workflowAwaitedAsyncResultPath } from "../background/async-execution.js";
@@ -56,7 +55,7 @@ import { assertAgentAllowedByCapabilityCeiling, intersectSubagentCapabilityCeili
56
55
  import { isAgentContract } from "../shared/agent-contract.js";
57
56
  import { normalizeExtensionBindings } from "../shared/extension-bindings.js";
58
57
  import { assertRequiredChildExtensionsAdmitted, hasMandatoryRequiredChildExtensions, readRetainedRequiredChildExtensions, resolveRequiredChildExtensions } from "../../shared/required-child-extensions.js";
59
- import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, outputPathMappingFromTask, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.js";
58
+ import { capInlineOutput, finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, outputPathMappingFromTask, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.js";
60
59
  import { assertJsonSchemaObject, cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.js";
61
60
  import { compactForegroundDetails, getSingleResultOutput, PROMPT_REDACTED, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage, toAgentToolUsage } from "../../shared/utils.js";
62
61
  import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, protectRetainedWorktreeForResume, formatStoredParallelHandoffCleanup, parallelHandoffPath, readParallelHandoffManifest, recordParallelHandoffMerge, recordParallelHandoffSupersession, writeParallelHandoffGroup, writeWorktreeSetupHandoff } from "../shared/parallel-handoff.js";
@@ -73,8 +72,8 @@ import { promotePausedWorkflowIfSettled, reconcileDetachedWorkflowChildCompletio
73
72
  import { reconcileAsyncRun } from "../background/stale-run-reconciler.js";
74
73
  import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.js";
75
74
  import { awaitExistingAsyncRun, claimWorkflowAwaitedResult } from "../background/await-async-run.js";
76
- import { fallbackResultPayloadPathForSessionRun, removeResultIndex, resultFilePath, writeAsyncResultFile } from "../background/result-files.js";
77
- import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, inheritedNestedParentAddressOf, inheritedNestedRouteOf, nestedRunScope, resolveNestedAsyncDir, retainNestedLookupRoute, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent } from "../shared/nested-events.js";
75
+ import { fallbackResultPayloadPathForSessionRun, resultFilePath, retireResultSnapshot, writeAsyncResultFile } from "../background/result-files.js";
76
+ import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, inheritedNestedParentAddressOf, inheritedNestedRouteOf, nestedRunNotActiveMessage, nestedRunScope, resolveNestedAsyncDir, retainNestedLookupRoute, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent } from "../shared/nested-events.js";
78
77
  import { resolveSubagentRunId } from "../background/run-id-resolver.js";
79
78
  import { formatNestedRunStatusLines } from "../shared/nested-render.js";
80
79
  import { isStoppableAsyncStatusStep, resolveAsyncStatusChild } from "../shared/child-identity.js";
@@ -83,6 +82,7 @@ import { getExternalJobProvider } from "../../api/external-job-provider.js";
83
82
  import { externalJobFollowUpRequestDigest, externalJobFollowUpRequestId, externalJobFollowUpRunId, externalJobPromptDigest, externalJobStableJson } from "../shared/external-job-runner.js";
84
83
  import { externalCliReceiptMetadata, normalizeExternalCliRunnerStatus } from "../shared/external-cli-contract.js";
85
84
  import { formatHerdrMachineRunnerUnsupported, resolveHerdrMachinePlacement } from "../shared/herdr-machine.js";
85
+ import { lookupRunnerLauncher, resolveAgentRunnerLauncher } from "../shared/runner-launcher.js";
86
86
  import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.js";
87
87
  import { handleMissionAction, MISSION_ACTIONS } from "../../missions/actions.js";
88
88
  import { attachMissionToLaunchResult, prepareMissionLaunch, writeMissionAsyncBinding } from "../../missions/lifecycle.js";
@@ -111,35 +111,12 @@ import { applyReviewedCleanupPlan, formatCleanupReceipt, loadReviewedCleanupPlan
111
111
  import { createWorktreeCleanupPlan, formatWorktreeCleanupPlan } from "../shared/worktree-cleanup-plan.js";
112
112
  import { DIRS, DEFAULT_ARTIFACT_CONFIG, DEFAULT_MAX_OUTPUT, DEFAULT_FORK_PREAMBLE, SUBAGENT_ACTIONS, SUBAGENT_ASYNC_STARTED_EVENT, SUBAGENT_CHILD_STATUS_EVENT, SUBAGENT_CONTROL_EVENT, SUBAGENT_CONTROL_INTERCOM_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT, SUBAGENT_LIFECYCLE_ARTIFACT_VERSION, checkSubagentDepth, resolveChildMaxSubagentDepth, resolveCurrentMaxSubagentDepth, resolveMaxSubagentSpawnsPerRun, truncateOutput, wrapForkTask, } from "../../shared/types.js";
113
113
  import { deriveChildSessionName } from "../../shared/child-session-name.js";
114
+ import { editDistance, hasSingleAdjacentTransposition } from "../../shared/edit-distance.js";
114
115
  const MUTATING_MANAGEMENT_ACTIONS = new Set(["command.yield", "command.cancel", "create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure", "mission.create", "mission.update", "mission.resolve-decision", "mission.attach-run", "mission.close", "inspector.open", "inspector.close", "project.open", "project.close", "worktree.discard", "worktree.cleanup", "lane.recordMerge", "lane.recordSupersession", "refine", "refine.rollback", "dismiss", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
115
116
  const DESTRUCTIVE_MANAGEMENT_ACTIONS = new Set(["command.cancel", "delete", "eject", "disable", "reset", "mission.close", "worktree.discard", "refine.rollback", "inspector.close", "project.close", "stop", "interrupt", "schedule.delete"]);
116
117
  function resolveSteerDeliveryMode(mode) {
117
118
  return mode === "steer" || mode === "follow_up" || mode === "auto" ? mode : undefined;
118
119
  }
119
- function editDistance(left, right) {
120
- const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
121
- for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
122
- let diagonal = previous[0];
123
- previous[0] = leftIndex;
124
- for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) {
125
- const above = previous[rightIndex];
126
- previous[rightIndex] = left[leftIndex - 1] === right[rightIndex - 1]
127
- ? diagonal
128
- : Math.min(diagonal, above, previous[rightIndex - 1]) + 1;
129
- diagonal = above;
130
- }
131
- }
132
- return previous[right.length];
133
- }
134
- function hasSingleAdjacentTransposition(left, right) {
135
- if (left.length !== right.length)
136
- return false;
137
- const mismatch = [...left].findIndex((character, index) => character !== right[index]);
138
- return mismatch >= 0
139
- && left[mismatch] === right[mismatch + 1]
140
- && left[mismatch + 1] === right[mismatch]
141
- && left.slice(mismatch + 2) === right.slice(mismatch + 2);
142
- }
143
120
  export function unknownSubagentActionMessage(action, disabled) {
144
121
  const requested = action.toLowerCase();
145
122
  const enabledActions = SUBAGENT_ACTIONS.filter((candidate) => !disabled?.actions.has(candidate));
@@ -304,6 +281,21 @@ function trustedSessionRootsForStatus(ctx, deps) {
304
281
  function sessionProjectTrust(ctx) {
305
282
  return typeof ctx.isProjectTrusted === "function" ? ctx.isProjectTrusted() : undefined;
306
283
  }
284
+ /** Agent and settings discovery follows the launching session's trust, like the child itself. */
285
+ function withSessionProjectTrust(deps, ctx) {
286
+ if (sessionProjectTrust(ctx) !== false)
287
+ return deps;
288
+ return { ...deps, discoverAgents: (cwd, scope, preferredModelProvider) => deps.discoverAgents(cwd, scope, preferredModelProvider, { projectTrusted: false }) };
289
+ }
290
+ function projectScopeTrustError(params, ctx) {
291
+ if (sessionProjectTrust(ctx) !== false || resolveExecutionAgentScope(params.agentScope) !== "project")
292
+ return undefined;
293
+ const action = typeof params.action === "string" ? params.action.trim() : undefined;
294
+ // These actions resolve agents in the requested scope; management actions are left to their own handlers.
295
+ if (action !== undefined && action !== "resume" && action !== "validate" && action !== "append-step")
296
+ return undefined;
297
+ return buildRequestedModeError(params, projectScopeRequiresTrustMessage(resolveRequestedCwd(ctx.cwd, params.cwd)));
298
+ }
307
299
  function spawnBudgetErrorResult(message, mode) {
308
300
  return {
309
301
  content: [{ type: "text", text: message }],
@@ -644,6 +636,7 @@ function resolveForegroundResumeTarget(params, state, options = {}) {
644
636
  ...(child.thinking ? { thinking: child.thinking } : {}),
645
637
  ...(child.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {}),
646
638
  ...(child.resumeContract ? { resumeContract: child.resumeContract } : {}),
639
+ ...(child.savedOutputPath ? { savedOutputPath: child.savedOutputPath } : {}),
647
640
  ...(child.extensionBindings ? { extensionBindings: normalizeExtensionBindings(child.extensionBindings).value } : {}),
648
641
  ...(child.requiredExtensions ? { requiredExtensions: child.requiredExtensions } : {}),
649
642
  ...(child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {}),
@@ -824,7 +817,7 @@ function emitControlNotification(input) {
824
817
  }
825
818
  }
826
819
  function interruptAsyncRun(state, runId, kill, location) {
827
- const target = getAsyncInterruptTarget(state, runId, location);
820
+ const target = getAsyncInterruptTarget(state, runId, location, { fallbackToNewest: runId === undefined });
828
821
  if (!target)
829
822
  return null;
830
823
  const status = reconcileAsyncRun(target.asyncDir, omitUndefinedProperties({ kill })).status;
@@ -1016,6 +1009,7 @@ function appendStepToAsyncChain(input) {
1016
1009
  cwd: input.ctx.cwd,
1017
1010
  currentSessionId: resolveCurrentSessionId(input.ctx.sessionManager),
1018
1011
  parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
1012
+ parentSessionFile: input.ctx.sessionManager.getSessionFile() ?? undefined,
1019
1013
  currentModelProvider: parentModel?.provider,
1020
1014
  currentModel: parentModel,
1021
1015
  scopedModelIds: scopedModelIdsFromContext(input.ctx),
@@ -1025,6 +1019,17 @@ function appendStepToAsyncChain(input) {
1025
1019
  permissions: input.deps.config.permissions,
1026
1020
  childRuntime: input.deps.childRuntime,
1027
1021
  });
1022
+ // Appended steps run inside the already-running runner, so they must use its launcher (or none).
1023
+ // Checked before building, which can write progress files. A mix is refused by the builder.
1024
+ const appendedLaunchers = new Set(chain.flatMap(getStepAgents).map((name) => agents.find((agent) => agent.name === name)?.launcher));
1025
+ const appendedLauncher = [...appendedLaunchers][0];
1026
+ if (appendedLaunchers.size === 1 && appendedLauncher !== status.launcher?.name) {
1027
+ return {
1028
+ content: [{ type: "text", text: `Cannot append step to run '${resolved.id}': its runner uses ${status.launcher ? `launcher '${status.launcher.name}'` : "no launcher"}, but the appended agents use ${appendedLauncher ? `launcher '${appendedLauncher}'` : "no launcher"}.` }],
1029
+ isError: true,
1030
+ details: { mode: "management", results: [] },
1031
+ };
1032
+ }
1028
1033
  const built = buildAsyncRunnerSteps(resolved.id, compactOptional({
1029
1034
  chain: wrapChainTasksForFork(chain, contextPolicy),
1030
1035
  task: input.params.task,
@@ -1332,11 +1337,18 @@ async function interruptNestedRun(target) {
1332
1337
  if (run.state === "paused")
1333
1338
  return { content: [{ type: "text", text: `Nested run ${run.id} is already paused.` }], isError: true, details: { mode: "management", results: [] } };
1334
1339
  const result = await sendNestedControlRequest(target, "interrupt");
1335
- if (result)
1340
+ // The owner's foreground map cannot address a detached async child. Only
1341
+ // that missing-route response may use the existing authorized async inbox;
1342
+ // other owner refusals must remain authoritative.
1343
+ const missingForegroundRoute = result?.ok === false && result.message === nestedRunNotActiveMessage(run.id);
1344
+ if (result && !missingForegroundRoute)
1336
1345
  return { content: [{ type: "text", text: result.message }], ...(result.ok ? {} : { isError: true }), details: { mode: "management", results: [] } };
1337
1346
  const direct = directNestedAsyncInterrupt(target);
1338
1347
  if (direct)
1339
1348
  return direct;
1349
+ // No async inbox either: the owner's own answer is the accurate one.
1350
+ if (result)
1351
+ return { content: [{ type: "text", text: result.message }], isError: true, details: { mode: "management", results: [] } };
1340
1352
  return { content: [{ type: "text", text: `Nested run ${run.id} owner is not reachable and no safe direct async interrupt fallback is available.` }], isError: true, details: { mode: "management", results: [] } };
1341
1353
  }
1342
1354
  async function resumeLiveNestedRun(input) {
@@ -1467,6 +1479,7 @@ async function resumeExternalJobFollowUp(input) {
1467
1479
  cwd: input.requestCwd,
1468
1480
  currentSessionId,
1469
1481
  parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
1482
+ parentSessionFile: input.ctx.sessionManager.getSessionFile() ?? undefined,
1470
1483
  currentModelProvider: parentModel?.provider,
1471
1484
  currentModel: parentModel,
1472
1485
  scopedModelIds: scopedModelIdsFromContext(input.ctx),
@@ -1587,7 +1600,7 @@ async function resumeAsyncRun(input) {
1587
1600
  type: "text",
1588
1601
  text: [
1589
1602
  `Async child '${target.runId}' index ${target.index} is still running. action='resume' only revives paused, completed, or failed children.`,
1590
- `Send live input with subagent({ action: "steer", id: "${target.runId}", index: ${target.index}, message: "..." }).`,
1603
+ `Send live input with subagent({ action: "steer", id: "${target.runId}", message: "...", options: { index: ${target.index} } }).`,
1591
1604
  ].join("\n"),
1592
1605
  }],
1593
1606
  isError: true,
@@ -1727,7 +1740,7 @@ async function resumeAsyncRun(input) {
1727
1740
  const chain = wrapChainTasksForFork(attachChain, contextPolicy);
1728
1741
  const normalized = normalizeSkillInput(input.params.skill);
1729
1742
  const parentModel = input.parentModel;
1730
- const result = executeAsyncChain(runId, compactOptional({
1743
+ const result = await executeAsyncChain(runId, compactOptional({
1731
1744
  chain,
1732
1745
  task: workflowTask,
1733
1746
  goal,
@@ -1746,11 +1759,13 @@ async function resumeAsyncRun(input) {
1746
1759
  cwd: input.requestCwd,
1747
1760
  currentSessionId: input.deps.state.currentSessionId,
1748
1761
  parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
1762
+ parentSessionFile: input.ctx.sessionManager.getSessionFile() ?? undefined,
1749
1763
  currentModelProvider: parentModel?.provider,
1750
1764
  currentModel: parentModel,
1751
1765
  scopedModelIds: scopedModelIdsFromContext(input.ctx),
1752
1766
  modelScope,
1753
1767
  modelResponseAliases: input.deps.config.modelResponseAliases,
1768
+ runnerLaunchers: input.deps.config.runnerLaunchers,
1754
1769
  interactive: input.ctx.hasUI,
1755
1770
  permissions: input.deps.config.permissions,
1756
1771
  childRuntime: input.deps.childRuntime,
@@ -1814,7 +1829,8 @@ async function resumeAsyncRun(input) {
1814
1829
  if (target.source === "nested" && !target.recoveryDescriptor) {
1815
1830
  return { content: [{ type: "text", text: `Nested child '${target.runId}' is missing its required recovery identity. Start a new run instead.` }], isError: true, details: { mode: "management", results: [] } };
1816
1831
  }
1817
- const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(baseAgentConfig, recoveryDescriptor) : baseAgentConfig;
1832
+ // Runs without a recovery descriptor were never wrapped; a launcher added to the agent file later does not apply.
1833
+ const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(baseAgentConfig, recoveryDescriptor) : { ...baseAgentConfig, launcher: undefined };
1818
1834
  const agentConfig = intercomBridge.active ? applyIntercomBridgeToAgent(recoveryAgentConfig, intercomBridge) : recoveryAgentConfig;
1819
1835
  const foregroundContract = target.source === "foreground" ? target.resumeContract : undefined;
1820
1836
  const outputSchema = Object.hasOwn(input.params, "outputSchema")
@@ -1831,6 +1847,10 @@ async function resumeAsyncRun(input) {
1831
1847
  if (acceptanceErrors.length > 0) {
1832
1848
  return { content: [{ type: "text", text: `Cannot resume: ${acceptanceErrors.join(" ")}` }], isError: true, details: { mode: "management", results: [] } };
1833
1849
  }
1850
+ // Refuse before capacity moves so the retained run stays resumable once the config is fixed.
1851
+ if (agentConfig.launcher !== undefined && !lookupRunnerLauncher(input.deps.config.runnerLaunchers, agentConfig.launcher)) {
1852
+ return { content: [{ type: "text", text: `Cannot resume: run '${target.runId}' was launched with launcher '${agentConfig.launcher}', which is no longer defined in runnerLaunchers in the user subagent config.` }], isError: true, details: { mode: "management", results: [] } };
1853
+ }
1834
1854
  const runId = randomUUID();
1835
1855
  const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
1836
1856
  let activeAsyncCapacity;
@@ -1862,6 +1882,13 @@ async function resumeAsyncRun(input) {
1862
1882
  const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
1863
1883
  const parentModel = input.parentModel;
1864
1884
  const revivalAsyncDir = path.join(DIRS.async, runId);
1885
+ const revivalOutputBaseDir = resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId);
1886
+ const inheritedOutput = foregroundContract?.output ?? recoveryDescriptor?.outputPath;
1887
+ // The original run owns its saved output, so a resume without its own output writes next to it instead of over it.
1888
+ // Workflow resumes are routed by the workflow's output claims instead.
1889
+ const inheritedOutputPath = input.params.output === undefined && input.params.workflowParentRunId === undefined
1890
+ ? (target.source === "foreground" ? target.savedOutputPath : undefined) ?? resolveSingleOutputPath(inheritedOutput, input.requestCwd, effectiveCwd, revivalOutputBaseDir)
1891
+ : undefined;
1865
1892
  const result = await executeAsyncSingle(runId, compactOptional({
1866
1893
  agent: target.agent,
1867
1894
  task: buildRevivedAsyncTask(target, effectiveFollowUp),
@@ -1873,12 +1900,14 @@ async function resumeAsyncRun(input) {
1873
1900
  cwd: input.requestCwd,
1874
1901
  currentSessionId: input.deps.state.currentSessionId,
1875
1902
  parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
1903
+ parentSessionFile: input.ctx.sessionManager.getSessionFile() ?? undefined,
1876
1904
  currentModelProvider: parentModel?.provider,
1877
1905
  currentModel: parentModel,
1878
1906
  scopedModelIds: scopedModelIdsFromContext(input.ctx),
1879
1907
  modelScope,
1880
1908
  // Absence in the retained contract is meaningful; never acquire current aliases.
1881
1909
  modelResponseAliases: recoveryDescriptor ? recoveryDescriptor.modelResponseAliases : foregroundContract?.modelResponseAliases,
1910
+ runnerLaunchers: input.deps.config.runnerLaunchers,
1882
1911
  interactive: input.ctx.hasUI,
1883
1912
  permissions: input.deps.config.permissions,
1884
1913
  childRuntime: input.deps.childRuntime,
@@ -1907,7 +1936,7 @@ async function resumeAsyncRun(input) {
1907
1936
  thinkingCeiling: recoveryDescriptor?.thinkingCeiling ?? ("thinkingCeiling" in target ? target.thinkingCeiling : undefined),
1908
1937
  extensionBindings: recoveryDescriptor?.extensionBindings ?? ("extensionBindings" in target ? target.extensionBindings : undefined),
1909
1938
  requiredExtensions: recoveryDescriptor?.requiredExtensions ?? target.requiredExtensions,
1910
- outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
1939
+ outputBaseDir: revivalOutputBaseDir,
1911
1940
  maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
1912
1941
  waitToolEnabled: input.deps.waitToolEnabled,
1913
1942
  waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
@@ -1926,7 +1955,7 @@ async function resumeAsyncRun(input) {
1926
1955
  controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
1927
1956
  childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
1928
1957
  availableModels,
1929
- output: input.params.output !== undefined ? input.params.output : foregroundContract?.output ?? recoveryDescriptor?.outputPath,
1958
+ output: input.params.output !== undefined ? input.params.output : inheritedOutputPath ? suffixedOutputPath(inheritedOutputPath, runId.slice(0, 8)) : inheritedOutput,
1930
1959
  outputMode: input.params.outputMode ?? foregroundContract?.outputMode ?? recoveryDescriptor?.outputMode,
1931
1960
  outputClaimPath: input.params.workflowOutputClaimPath,
1932
1961
  ...(agentContract ? { agentContract } : {}),
@@ -2028,10 +2057,12 @@ function formatFailedSingleRunOutput(result, displayOutput) {
2028
2057
  const error = result.error || "Failed";
2029
2058
  const output = displayOutput.trim();
2030
2059
  const lines = [error];
2060
+ let cappedOutput = output;
2031
2061
  if (output && output !== error.trim()) {
2032
- lines.push("", "Output:", output);
2062
+ cappedOutput = capInlineOutput(output, { artifactPath: result.artifactPaths?.outputPath, savedPath: result.savedOutputPath });
2063
+ lines.push("", "Output:", cappedOutput);
2033
2064
  }
2034
- if (result.artifactPaths?.outputPath && fs.existsSync(result.artifactPaths.outputPath)) {
2065
+ if (cappedOutput === output && result.artifactPaths?.outputPath && fs.existsSync(result.artifactPaths.outputPath)) {
2035
2066
  lines.push("", `Output artifact: ${result.artifactPaths.outputPath}`);
2036
2067
  }
2037
2068
  return lines.join("\n");
@@ -2156,6 +2187,8 @@ function workflowValidationOptions(deps, params, runtimeCwd, parentModelProvider
2156
2187
  const cwd = resolveRequestedCwd(runtimeCwd, params.cwd);
2157
2188
  const scope = resolveExecutionAgentScope(params.agentScope);
2158
2189
  let discovered;
2190
+ // Validation reports every bad agent name; only the first one carries the agent list.
2191
+ let listedAgents = false;
2159
2192
  return {
2160
2193
  maxSubagentSpawnsPerRun: params.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun),
2161
2194
  agentNameError: (name) => {
@@ -2164,12 +2197,13 @@ function workflowValidationOptions(deps, params, runtimeCwd, parentModelProvider
2164
2197
  const resolved = resolveAgentName(name, agents);
2165
2198
  if (resolved.agent || resolved.error)
2166
2199
  return canonicalizeAgentName(name, agents, discovered.agentDiagnostics, diagnosticContextFromDiscovery(discovered, cwd, scope)).error;
2167
- const requested = name.trim().toLowerCase();
2168
- const suggestion = [...new Set(agents.flatMap((agent) => [agent.name, ...(agent.localName ? [agent.localName] : []), ...(agent.aliases ?? [])]))]
2169
- .map((candidate) => ({ candidate, distance: editDistance(requested, candidate.toLowerCase()) }))
2170
- .filter(({ candidate, distance }) => distance <= Math.max(1, Math.floor(candidate.length / 4)) || hasSingleAdjacentTransposition(requested, candidate.toLowerCase()))
2171
- .sort((left, right) => left.distance - right.distance || left.candidate.localeCompare(right.candidate))[0]?.candidate;
2172
- return `Unknown agent '${name}'.${suggestion ? ` Did you mean '${suggestion}'?` : ""} Use subagent({ action: "list" }) to inspect agents.`;
2200
+ const disabled = discovered.disabledAgents?.includes(name.trim()) === true;
2201
+ const suggestion = disabled ? undefined : suggestAgentName(name, agents);
2202
+ const problem = disabled ? `Agent '${name}' is disabled by a settings override.` : `Unknown agent '${name}'.${suggestion ? ` Did you mean '${suggestion}'?` : ""}`;
2203
+ if (listedAgents)
2204
+ return problem;
2205
+ listedAgents = true;
2206
+ return [`${problem} Available agents:`, ...formatAvailableAgentLines(agents)].join("\n");
2173
2207
  },
2174
2208
  };
2175
2209
  }
@@ -2904,12 +2938,12 @@ function importedAsyncRootUsage(completed) {
2904
2938
  }
2905
2939
  function removeWorkflowAwaitedResult(asyncDir, resultPath, runId, publication) {
2906
2940
  try {
2907
- fs.rmSync(resultPath, { force: true });
2941
+ retireResultSnapshot(workflowAwaitedAsyncResultPath(asyncDir), { ...publication, runId }, resultPath);
2908
2942
  }
2909
- catch {
2910
- // Payload cleanup must not replace an already imported delivery.
2943
+ catch (error) {
2944
+ // Cleanup must not replace an already imported delivery.
2945
+ console.error(`Failed to remove imported workflow child result '${resultPath}'; leaving it in place:`, error);
2911
2946
  }
2912
- removeResultIndex(asyncDir, publication.sessionId, runId, publication.toolCallId);
2913
2947
  }
2914
2948
  function emitWorkflowAwaitedChildComplete(pi, state, runId, asyncDir, parentWorkflowRunId, completed) {
2915
2949
  let status;
@@ -3037,11 +3071,13 @@ async function runAsyncPath(data, deps) {
3037
3071
  cwd: ctx.cwd,
3038
3072
  currentSessionId: data.parentSessionId,
3039
3073
  parentSessionId: data.parentPiSessionId,
3074
+ parentSessionFile: ctx.sessionManager.getSessionFile() ?? undefined,
3040
3075
  currentModelProvider: parentModel?.provider,
3041
3076
  currentModel: parentModel,
3042
3077
  scopedModelIds: data.scopedModelIds,
3043
3078
  modelScope: data.modelScope,
3044
3079
  modelResponseAliases: deps.config.modelResponseAliases,
3080
+ runnerLaunchers: deps.config.runnerLaunchers,
3045
3081
  interactive: ctx.hasUI,
3046
3082
  permissions: deps.config.permissions,
3047
3083
  childRuntime: deps.childRuntime,
@@ -3091,6 +3127,7 @@ async function runAsyncPath(data, deps) {
3091
3127
  if (launchRuleError)
3092
3128
  return toExecutionErrorResult(params, new Error(launchRuleError), data.contextPolicy.contextSummary);
3093
3129
  const asyncResult = await executeAsyncSingle(id, compactOptional({
3130
+ toolCallId: data.toolCallId,
3094
3131
  agent: params.agent,
3095
3132
  task: shouldForkAgent(contextPolicy, params.agent) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
3096
3133
  goal: params.task ?? "",
@@ -3201,11 +3238,14 @@ export function sanitizeRunPathSegment(value, maxBytes = 120) {
3201
3238
  function resolveSingleRunOutputBaseDir(deps, artifactsDir, runId) {
3202
3239
  return resolveConfiguredSingleRunOutputBaseDir(deps) ?? path.join(artifactsDir, "outputs", sanitizeRunPathSegment(runId));
3203
3240
  }
3241
+ /** `<dir>/<name>.<suffix><ext>` next to outputPath, with `.md` when it has no extension. */
3242
+ function suffixedOutputPath(outputPath, suffix) {
3243
+ const parsed = path.parse(outputPath);
3244
+ return path.join(parsed.dir, `${parsed.name}.${suffix}${parsed.ext || ".md"}`);
3245
+ }
3204
3246
  function workflowChildDefaultOutput(aggregateOutputPath, artifactsDir, workflowRunId, workflowKey) {
3205
- if (aggregateOutputPath) {
3206
- const parsed = path.parse(aggregateOutputPath);
3207
- return path.join(parsed.dir, `${parsed.name}.${workflowKey}${parsed.ext || ".md"}`);
3208
- }
3247
+ if (aggregateOutputPath)
3248
+ return suffixedOutputPath(aggregateOutputPath, workflowKey);
3209
3249
  return path.join(artifactsDir, "outputs", sanitizeRunPathSegment(workflowRunId), `${workflowKey}.md`);
3210
3250
  }
3211
3251
  function workflowHostCommandRunner(input) {
@@ -3295,7 +3335,11 @@ function resolveWorkflowChildOutputPath(input) {
3295
3335
  id: input.params.resume.trim(),
3296
3336
  ...(typeof index === "number" && Number.isInteger(index) ? { index } : {}),
3297
3337
  }, input.state);
3298
- return { path: "recoveryDescriptor" in target ? target.recoveryDescriptor?.outputPath : undefined, inherited: false };
3338
+ const recoveryOutput = "recoveryDescriptor" in target ? target.recoveryDescriptor?.outputPath : undefined;
3339
+ if (hasExplicitOutput)
3340
+ return { path: recoveryOutput, inherited: false };
3341
+ const savedPath = recoveryOutput ?? (target.source === "foreground" ? target.savedOutputPath ?? resolveSingleOutputPath(target.resumeContract?.output, input.ctxCwd) : undefined);
3342
+ return { inherited: false, ...(savedPath ? { resumedSavedPath: savedPath } : {}) };
3299
3343
  }
3300
3344
  const childCwd = resolveWorkflowChildLocalCwd(input);
3301
3345
  let agentOutput;
@@ -3335,8 +3379,12 @@ function workflowChildOutputClaims(input) {
3335
3379
  }
3336
3380
  const overrides = new Map();
3337
3381
  for (const entry of resolvedEntries) {
3338
- if (entry.inherited && entry.path && (paths.get(resolveWorkflowHostOutputClaimPath(entry.path)) ?? 0) > 1) {
3339
- const output = workflowChildDefaultOutput(input.aggregateOutputPath, input.artifactsDir, input.workflowRunId, entry.key);
3382
+ // The resumed run owns the report it saved, inside or outside this workflow, so an output-less resume writes its own.
3383
+ if (entry.resumedSavedPath || (entry.inherited && entry.path && (paths.get(resolveWorkflowHostOutputClaimPath(entry.path)) ?? 0) > 1)) {
3384
+ let output = workflowChildDefaultOutput(input.aggregateOutputPath, input.artifactsDir, input.workflowRunId, entry.key);
3385
+ // A rerun of the same workflow and key generates the very file the resumed run saved.
3386
+ if (entry.resumedSavedPath && resolveWorkflowHostOutputClaimPath(output) === resolveWorkflowHostOutputClaimPath(entry.resumedSavedPath))
3387
+ output = suffixedOutputPath(output, input.workflowRunId.slice(0, 8));
3340
3388
  overrides.set(entry.key, output);
3341
3389
  entry.path = output;
3342
3390
  }
@@ -3363,7 +3411,7 @@ function applyWorkflowChildOutputClaims(target, claims) {
3363
3411
  function prepareWorkflowChildLaunchParams(input) {
3364
3412
  let childParams = input.childParams;
3365
3413
  const usesDefaultOutput = input.childParams.output === undefined && input.childParams.resume === undefined;
3366
- if (usesDefaultOutput && input.outputOverride !== undefined) {
3414
+ if (input.childParams.output === undefined && input.outputOverride !== undefined) {
3367
3415
  childParams = { ...input.childParams, output: input.outputOverride };
3368
3416
  }
3369
3417
  else if (usesDefaultOutput && input.aggregateOutputPath !== undefined) {
@@ -3380,7 +3428,7 @@ function prepareWorkflowChildLaunchParams(input) {
3380
3428
  const agent = typeof childParams.agent === "string"
3381
3429
  ? resolveAgentName(childParams.agent, discoveredAgents).agent ?? resolveAgentName(childParams.agent, input.agents).agent
3382
3430
  : undefined;
3383
- const externalAsyncRequired = agent?.runner?.type === "external-cli" || agent?.runner?.type === "external-job";
3431
+ const externalAsyncRequired = agent?.runner?.type === "external-cli" || agent?.runner?.type === "external-job" || agent?.launcher !== undefined;
3384
3432
  return prepareWorkflowLaunchParams(input.workflowDefaults, childParams, input.parentWorkflowRunId, input.workflowKey, { ...input.options, externalAsyncRequired, outputClaimPath: input.outputClaimPath });
3385
3433
  }
3386
3434
  async function finalizeSingleWorktreeHandoff(input) {
@@ -3575,7 +3623,6 @@ async function runSinglePath(data, deps) {
3575
3623
  if (shouldForkAgent(contextPolicy, params.agent)) {
3576
3624
  task = wrapForkTask(task);
3577
3625
  }
3578
- const cleanTask = task;
3579
3626
  const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, singleCwd, resolveSingleRunOutputBaseDir(deps, artifactsDir, runId));
3580
3627
  const validationError = validateFileOnlyOutputMode(effectiveOutputMode, outputPath, `Single run (${params.agent})`);
3581
3628
  if (validationError) {
@@ -3604,6 +3651,7 @@ async function runSinglePath(data, deps) {
3604
3651
  const interruptController = new AbortController();
3605
3652
  let detachForeground;
3606
3653
  let childSessionControls;
3654
+ let pendingRuntimeStop;
3607
3655
  const foregroundControl = deps.state.foregroundControls.get(runId);
3608
3656
  const syncHerdrForegroundChild = () => {
3609
3657
  if (!foregroundControl?.parentWorkflowRunId)
@@ -3679,6 +3727,7 @@ async function runSinglePath(data, deps) {
3679
3727
  ? new Promise((resolve) => { resolveDetachedWorkflowChild = resolve; })
3680
3728
  : undefined;
3681
3729
  try {
3730
+ const childSessionFile = sessionFileForTask(params.agent, 0, modelOverride, modelOverrideFromParent, modelOrigin);
3682
3731
  const launched = await runSync(ctx.cwd, agents, params.agent, task, compactOptional({
3683
3732
  machine: foregroundMachine,
3684
3733
  parentProviderRegistry: ctx.modelRegistry,
@@ -3687,9 +3736,14 @@ async function runSinglePath(data, deps) {
3687
3736
  permissions: deps.config.permissions,
3688
3737
  runtimeSnapshotHost: deps.pi,
3689
3738
  parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
3739
+ parentSessionFile: ctx.sessionManager.getSessionFile() ?? undefined,
3690
3740
  requiredExtensions,
3691
3741
  childRuntime: deps.childRuntime,
3692
- onChildSession: (controls) => { childSessionControls = controls; },
3742
+ onChildSession: (controls) => {
3743
+ childSessionControls = controls;
3744
+ if (pendingRuntimeStop)
3745
+ controls.stop(pendingRuntimeStop);
3746
+ },
3693
3747
  context: data.contextPolicy.contextForAgent(params.agent),
3694
3748
  unknownAgentDiagnosticContext: data.unknownAgentDiagnosticContext,
3695
3749
  runFanoutBudget: params.runFanoutAdmitted ? data.runFanoutBudget : { ...data.runFanoutBudget, parentPath: `${data.runFanoutBudget.parentPath ? `${data.runFanoutBudget.parentPath}/` : ""}single` },
@@ -3702,7 +3756,7 @@ async function runSinglePath(data, deps) {
3702
3756
  intercomEvents: deps.pi.events,
3703
3757
  runId,
3704
3758
  sessionDir: sessionDirForIndex(0),
3705
- sessionFile: sessionFileForTask(params.agent, 0, modelOverride, modelOverrideFromParent, modelOrigin),
3759
+ sessionFile: childSessionFile,
3706
3760
  share: shareEnabled,
3707
3761
  artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
3708
3762
  artifactConfig,
@@ -3743,6 +3797,8 @@ async function runSinglePath(data, deps) {
3743
3797
  detachForeground = detach;
3744
3798
  },
3745
3799
  onDetachedExit: async (result) => {
3800
+ if (foregroundControl)
3801
+ foregroundControl.stopForRuntimeReplacement = undefined;
3746
3802
  if (resolveDetachedWorkflowChild) {
3747
3803
  resolveDetachedWorkflowChild(result);
3748
3804
  return;
@@ -3783,7 +3839,6 @@ async function runSinglePath(data, deps) {
3783
3839
  }
3784
3840
  }
3785
3841
  }
3786
- recordRun(params.agent, cleanTask, result.exitCode, result.progressSummary?.durationMs ?? 0, result);
3787
3842
  },
3788
3843
  timeoutMs: data.timeoutMs,
3789
3844
  deadlineAt,
@@ -3794,6 +3849,20 @@ async function runSinglePath(data, deps) {
3794
3849
  capabilityCeiling: data.capabilityCeiling,
3795
3850
  allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
3796
3851
  }));
3852
+ if (launched.detached && foregroundControl) {
3853
+ // Record the stop before aborting so the next runtime's history restore finds it.
3854
+ foregroundControl.stopForRuntimeReplacement = () => {
3855
+ // The detach receipt predates the session file that resume needs.
3856
+ const sessionFile = childSessionFile && fs.existsSync(childSessionFile) ? childSessionFile : undefined;
3857
+ const stopped = { ...launched, detached: undefined, stopped: true, exitCode: 1, error: RUNTIME_REPLACED_STOP_ERROR, finalOutput: RUNTIME_REPLACED_STOP_ERROR, sessionFile };
3858
+ rememberForegroundRun(deps.state, { modelResponseAliases, runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: [stopped], params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings, requiredExtensions });
3859
+ // A child detached during creation has no controls yet; stop it as soon as they arrive.
3860
+ if (childSessionControls)
3861
+ childSessionControls.stop(RUNTIME_REPLACED_STOP_ERROR);
3862
+ else
3863
+ pendingRuntimeStop = RUNTIME_REPLACED_STOP_ERROR;
3864
+ };
3865
+ }
3797
3866
  r = launched.detached && detachedWorkflowChild ? await detachedWorkflowChild : launched;
3798
3867
  }
3799
3868
  catch (error) {
@@ -3810,9 +3879,6 @@ async function runSinglePath(data, deps) {
3810
3879
  finishTrackedForegroundChild();
3811
3880
  }
3812
3881
  }
3813
- if (!r.detached) {
3814
- recordRun(params.agent, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0, r);
3815
- }
3816
3882
  let worktreeHandoff;
3817
3883
  if (worktreeSetup) {
3818
3884
  worktreeHandoff = r.detached
@@ -3904,7 +3970,7 @@ async function runSinglePath(data, deps) {
3904
3970
  isError: true,
3905
3971
  };
3906
3972
  return {
3907
- content: [{ type: "text", text: `${finalizedOutput.displayOutput || "(no output)"}${worktreeSuffix}` }],
3973
+ content: [{ type: "text", text: `${capInlineOutput(finalizedOutput.displayOutput, { artifactPath: r.artifactPaths?.outputPath, savedPath: finalizedOutput.savedPath }) || "(no output)"}${worktreeSuffix}` }],
3908
3974
  details,
3909
3975
  };
3910
3976
  }
@@ -4107,7 +4173,7 @@ function workflowRunningChildrenSummary(children) {
4107
4173
  function workflowResultChildren(children, status, includeFailureFields) {
4108
4174
  return withWorkflowRevivals(DIRS.async, status.runId, children).map((child) => {
4109
4175
  const sessionName = status.steps?.find((step) => step.workflowKey === child.key)?.sessionName;
4110
- return { workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(child.revival ? { revival: formatWorkflowKeyRevival(child.revival) } : {}), ...(sessionName ? { sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, ...(child.state === "running" ? { state: "running" } : { success: child.ok }), ...(child.asyncDir ? { asyncDir: child.asyncDir } : {}), ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(includeFailureFields && child.terminalOutcome ? { terminalOutcome: child.terminalOutcome } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(child.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(includeFailureFields && child.detached && status.state !== "complete" ? { detached: true } : {}), ...(child.outputArtifactPath || child.outputReference ? { artifactPaths: { outputPath: child.outputArtifactPath ?? child.outputReference } } : {}) };
4176
+ return { workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(child.revival ? { revival: formatWorkflowKeyRevival(child.revival) } : {}), ...(sessionName ? { sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, ...(child.state === "running" ? { state: "running" } : { success: child.ok }), ...(!child.ok && child.error ? { error: child.error } : {}), ...(child.asyncDir ? { asyncDir: child.asyncDir } : {}), ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(includeFailureFields && child.terminalOutcome ? { terminalOutcome: child.terminalOutcome } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(child.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(includeFailureFields && child.detached && status.state !== "complete" ? { detached: true } : {}), ...(child.outputArtifactPath || child.outputReference ? { artifactPaths: { outputPath: child.outputArtifactPath ?? child.outputReference } } : {}) };
4111
4177
  });
4112
4178
  }
4113
4179
  function workflowOutputPathMappingSummary(children) {
@@ -4141,6 +4207,7 @@ function workflowSteerReceipt(key, result) {
4141
4207
  };
4142
4208
  }
4143
4209
  const CHILD_SESSION_NOT_RUNNING_YET = "Child session is not running yet.";
4210
+ const RUNTIME_REPLACED_STOP_ERROR = "Subagent stopped because the pi-subagents runtime that ran it was replaced (reload or session switch).";
4144
4211
  const MAX_WORKFLOW_RESUME_HINT_BYTES = 1024;
4145
4212
  const MAX_WORKFLOW_CHILD_RUN_ID_BYTES = 256;
4146
4213
  const WORKFLOW_RESUME_HINT_PARENT_STATES = new Set(["complete", "failed", "partial"]);
@@ -4634,6 +4701,7 @@ function createScheduledOwnerState(source, ownerSessionId, ctx) {
4634
4701
  };
4635
4702
  }
4636
4703
  export function createSubagentExecutor(deps) {
4704
+ const executorDeps = deps;
4637
4705
  const delegatedThinkingOverrides = new WeakMap();
4638
4706
  const delegatedZeroToolBudgets = new WeakSet();
4639
4707
  const delegatedExecutions = new WeakSet();
@@ -4657,6 +4725,10 @@ export function createSubagentExecutor(deps) {
4657
4725
  }
4658
4726
  };
4659
4727
  const execute = async (_id, params, signal, onUpdate, ctx, preserveActiveSession = false, parentModelOverride) => {
4728
+ const projectScopeError = projectScopeTrustError(params, ctx);
4729
+ if (projectScopeError)
4730
+ return projectScopeError;
4731
+ const deps = withSessionProjectTrust(executorDeps, ctx);
4660
4732
  const workflowLaunchObserver = workflowLaunchObservers.get(params);
4661
4733
  const inheritedUsageBudget = workflowOwnedUsageBudgets.get(params);
4662
4734
  const retainedRequiredExtensions = workflowRetainedRequiredExtensions.get(params);
@@ -4744,12 +4816,7 @@ export function createSubagentExecutor(deps) {
4744
4816
  if (launchBlockingErrors.length > 0) {
4745
4817
  return buildRequestedModeError(requestParams, `Workflow '${_id}' validation failed before child launch; no children launched. ${launchBlockingErrors.map((error) => error.message).join(" ")}`);
4746
4818
  }
4747
- // A package chain's per-step stop check defeats static launch counting; runtime fan-out enforcement still applies.
4748
- const structuredResource = workflowResource?.provenance.name === "chain" || workflowResource?.provenance.name === "tasks";
4749
- for (const warning of workflowValidation.warnings ?? []) {
4750
- if (!(structuredResource && warning.kind === "dynamic-spawn-count"))
4751
- console.warn(`[pi-subagents] ${warning.message}`);
4752
- }
4819
+ // Validation warnings belong to the validate action; a launch does not repeat them on every run.
4753
4820
  const acceptanceErrors = validateAcceptanceInput(requestParams.acceptance);
4754
4821
  if (acceptanceErrors.length > 0)
4755
4822
  return buildRequestedModeError(requestParams, acceptanceErrors.join(" "));
@@ -5529,6 +5596,16 @@ export function createSubagentExecutor(deps) {
5529
5596
  const journalFingerprint = workflowJournalFingerprints.get(notification.childKey);
5530
5597
  if (journalFingerprint)
5531
5598
  appendWorkflowChildJournal(asyncDir, { type: "settle", key: notification.childKey, fingerprint: journalFingerprint, result: notification.result });
5599
+ // Fresh, journal-reused and re-attached children all settle here, so their rows take usage from the same result.
5600
+ const settledResults = notification.result.results ?? [];
5601
+ const settledStep = settledResults.length ? status.steps?.find((candidate) => candidate.workflowKey === notification.childKey) : undefined;
5602
+ if (settledStep) {
5603
+ const usage = sumResultsUsage(settledResults);
5604
+ settledStep.tokens = { input: usage.input, output: usage.output, total: usage.input + usage.output };
5605
+ if (usage.turns > 0)
5606
+ settledStep.turnCount = usage.turns;
5607
+ persist({ tolerateStatusWriteFailure: true });
5608
+ }
5532
5609
  appendWorkflowEvent({
5533
5610
  type: "subagent.workflow.child_settled",
5534
5611
  childKey: notification.childKey,
@@ -5541,6 +5618,8 @@ export function createSubagentExecutor(deps) {
5541
5618
  customType: "subagent-incremental-child-notify",
5542
5619
  content: formatIncrementalChildCompletion(notification),
5543
5620
  display: notification.outcome !== "completed",
5621
+ // Render-only: Pi does not send details to the model.
5622
+ details: { childKey: notification.childKey, outcome: notification.outcome },
5544
5623
  }, { triggerTurn: incrementalChildCompletionTriggersTurn(notification, requestParams.scheduleOrigin) });
5545
5624
  }
5546
5625
  catch (sendError) {
@@ -5600,7 +5679,7 @@ export function createSubagentExecutor(deps) {
5600
5679
  appendWorkflowChildJournal(asyncDir, { type: "start", key, fingerprint: journalFingerprint, runId: priorRunId });
5601
5680
  const stopListener = stopAwaitedAsyncChildOnAbort(workflowSignal, deps.state, priorRunId, priorAsyncDir, deps.kill);
5602
5681
  const reattached = await awaitExistingAsyncRun(priorAsyncDir, priorRunId, workflowSignal).finally(stopListener.remove);
5603
- const claimedPath = reattached.status === "settled" ? claimWorkflowAwaitedResult(priorAsyncDir, workflowRunId) : undefined;
5682
+ const claimedPath = reattached.status === "settled" ? claimWorkflowAwaitedResult(priorAsyncDir, workflowRunId, { ...reattached.result.importedPublication, runId: priorRunId }) : undefined;
5604
5683
  if (reattached.status === "settled" && claimedPath) {
5605
5684
  const imported = importWorkflowAwaitedChildResult(reattached.result, { runId: priorRunId, asyncDir: priorAsyncDir, resultPath: claimedPath, task: typeof childParams.task === "string" ? childParams.task : "", parentWorkflowRunId: workflowReuseSource?.runId, details: { mode: "single", asyncId: priorRunId, asyncDir: priorAsyncDir, results: [] }, state: deps.state, pi: deps.pi });
5606
5685
  const reattachedResult = workflowChildResult(key, imported, childParams, deps.state);
@@ -6408,7 +6487,10 @@ export function createSubagentExecutor(deps) {
6408
6487
  if (action === "guide") {
6409
6488
  try {
6410
6489
  const guide = readSubagentGuide(paramsWithResolvedCwd.topic);
6411
- const notice = paramsWithResolvedCwd.topic === "tool-reference" ? disabledFeatureNotice(disabledFeatures) : undefined;
6490
+ const [topic, section] = paramsWithResolvedCwd.topic?.split("/") ?? [];
6491
+ const fullNotice = topic === "tool-reference" ? disabledFeatureNotice(disabledFeatures) : undefined;
6492
+ // Sections are capped at 8,000 chars, so they get a one-line pointer instead of the full list.
6493
+ const notice = fullNotice && section !== undefined ? "Some options and actions documented here are disabled by config in this session; guide topic tool-reference lists them." : fullNotice;
6412
6494
  return {
6413
6495
  content: [{ type: "text", text: notice ? `${notice}\n\n${guide}` : guide }],
6414
6496
  details: { mode: "management", results: [] },
@@ -6970,16 +7052,28 @@ export function createSubagentExecutor(deps) {
6970
7052
  : hasTasks
6971
7053
  ? (effectiveParams.tasks ?? []).map((task) => task.agent)
6972
7054
  : (effectiveParams.chain ?? []).flatMap((step) => getStepAgents(step));
6973
- const externalAgent = selectedAgentNames
6974
- .map((name) => agents.find((agent) => agent.name === name))
6975
- .find((agent) => agent?.runner?.type === "external-cli" || agent?.runner?.type === "external-job");
6976
- const externalAsyncRequired = Boolean(externalAgent) && effectiveParams.async === undefined && effectiveParams.clarify !== true && effectiveParams.foregroundOnly !== true;
7055
+ const selectedAgents = selectedAgentNames.map((name) => agents.find((agent) => agent.name === name));
7056
+ const externalAgent = selectedAgents.find((agent) => agent?.runner?.type === "external-cli" || agent?.runner?.type === "external-job");
7057
+ const launcherAgent = selectedAgents.find((agent) => agent?.launcher !== undefined);
7058
+ // A launcher agent goes to the background whenever the caller omitted async, even if the agent file defaults to foreground.
7059
+ const externalAsyncRequired = effectiveParams.foregroundOnly !== true && ((Boolean(externalAgent) && effectiveParams.async === undefined && effectiveParams.clarify !== true)
7060
+ || (Boolean(launcherAgent) && normalizedParams.async === undefined && normalizedParams.clarify !== true));
6977
7061
  const requestedAsync = externalAsyncRequired ? true : effectiveParams.async ?? deps.asyncByDefault;
6978
7062
  const backgroundRequestedWhileClarifying = (hasChain || hasTasks) && requestedAsync && effectiveParams.clarify === true;
6979
7063
  const effectiveAsync = requestedAsync && effectiveParams.clarify !== true;
6980
7064
  if (externalAgent && (!effectiveAsync || effectiveParams.foregroundOnly === true)) {
6981
7065
  return buildRequestedModeError(effectiveParams, `Agent '${externalAgent.name}' uses runner.type='${externalAgent.runner?.type}', which currently supports async/background execution only. Omit async or pass async:true; clarify and foregroundOnly are unsupported.`);
6982
7066
  }
7067
+ // forceTopLevelAsync rewrites async:false and clarify, so check what the caller asked for.
7068
+ if (launcherAgent && (!effectiveAsync || normalizedParams.async === false || normalizedParams.clarify === true || effectiveParams.foregroundOnly === true)) {
7069
+ return buildRequestedModeError(effectiveParams, `Agent '${launcherAgent.name}' uses launcher '${launcherAgent.launcher}', which wraps the background runner only. Foreground children run inside the parent process, so a launcher cannot wrap them. Omit async or pass async:true; clarify and foregroundOnly are unsupported.`);
7070
+ }
7071
+ // Refuse a launcher launch that cannot start before fan-out admission or a session directory exists.
7072
+ for (const agent of selectedAgents) {
7073
+ const launcherError = agent ? resolveAgentRunnerLauncher(agent, deps.config.runnerLaunchers, effectiveParams.machine ?? agent.machine).error : undefined;
7074
+ if (launcherError)
7075
+ return buildRequestedModeError(effectiveParams, launcherError);
7076
+ }
6983
7077
  if (effectiveAsync && hasSingle && effectiveParams.resume === undefined && effectiveParams.worktree === true) {
6984
7078
  try {
6985
7079
  await preflightWorktreeSource(effectiveCwd, { signal });
@@ -7173,6 +7267,7 @@ export function createSubagentExecutor(deps) {
7173
7267
  backgroundRequestedWhileClarifying,
7174
7268
  effectiveAsync,
7175
7269
  asyncRunId,
7270
+ toolCallId: _id,
7176
7271
  controlConfig,
7177
7272
  ...(delegatedExecution ? { suppressUnchangedDelegationUpdates: true } : {}),
7178
7273
  intercomBridge,