@selesai/code 0.6.2 → 0.7.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 (997) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/README.md +1 -1
  3. package/dist/bun/cli.d.ts +0 -1
  4. package/dist/bun/cli.js +0 -1
  5. package/dist/bun/register-bedrock.d.ts +0 -1
  6. package/dist/bun/register-bedrock.js +0 -1
  7. package/dist/bun/restore-sandbox-env.d.ts +0 -1
  8. package/dist/bun/restore-sandbox-env.js +0 -1
  9. package/dist/cli/args.d.ts +0 -1
  10. package/dist/cli/args.js +0 -1
  11. package/dist/cli/config-selector.d.ts +0 -1
  12. package/dist/cli/config-selector.js +0 -1
  13. package/dist/cli/credential-print.d.ts +0 -1
  14. package/dist/cli/credential-print.js +0 -1
  15. package/dist/cli/file-processor.d.ts +0 -1
  16. package/dist/cli/file-processor.js +0 -1
  17. package/dist/cli/initial-message.d.ts +0 -1
  18. package/dist/cli/initial-message.js +0 -1
  19. package/dist/cli/list-models.d.ts +0 -1
  20. package/dist/cli/list-models.js +0 -1
  21. package/dist/cli/project-trust.d.ts +0 -1
  22. package/dist/cli/project-trust.js +0 -1
  23. package/dist/cli/session-picker.d.ts +0 -1
  24. package/dist/cli/session-picker.js +0 -1
  25. package/dist/cli/startup-ui.d.ts +0 -1
  26. package/dist/cli/startup-ui.js +0 -1
  27. package/dist/cli.d.ts +0 -1
  28. package/dist/cli.js +0 -1
  29. package/dist/config.d.ts +0 -1
  30. package/dist/config.js +0 -1
  31. package/dist/core/agent-session-auto-handoff.test.d.ts +0 -1
  32. package/dist/core/agent-session-auto-handoff.test.js +0 -1
  33. package/dist/core/agent-session-runtime.d.ts +0 -1
  34. package/dist/core/agent-session-runtime.js +0 -1
  35. package/dist/core/agent-session-services.d.ts +0 -1
  36. package/dist/core/agent-session-services.js +0 -1
  37. package/dist/core/agent-session-skill-block.test.d.ts +0 -1
  38. package/dist/core/agent-session-skill-block.test.js +0 -1
  39. package/dist/core/agent-session.d.ts +0 -1
  40. package/dist/core/agent-session.js +0 -1
  41. package/dist/core/agents.d.ts +0 -1
  42. package/dist/core/agents.js +0 -1
  43. package/dist/core/auth-guidance.d.ts +0 -1
  44. package/dist/core/auth-guidance.js +0 -1
  45. package/dist/core/auth-storage.d.ts +0 -1
  46. package/dist/core/auth-storage.js +0 -1
  47. package/dist/core/bash-executor.d.ts +0 -1
  48. package/dist/core/bash-executor.js +0 -1
  49. package/dist/core/built-in-extensions.d.ts +0 -1
  50. package/dist/core/built-in-extensions.js +0 -1
  51. package/dist/core/cache-stats.d.ts +0 -1
  52. package/dist/core/cache-stats.js +0 -1
  53. package/dist/core/compaction/branch-summarization.d.ts +0 -1
  54. package/dist/core/compaction/branch-summarization.js +0 -1
  55. package/dist/core/compaction/compaction.d.ts +0 -1
  56. package/dist/core/compaction/compaction.js +0 -1
  57. package/dist/core/compaction/index.d.ts +0 -1
  58. package/dist/core/compaction/index.js +0 -1
  59. package/dist/core/compaction/utils.d.ts +0 -1
  60. package/dist/core/compaction/utils.js +0 -1
  61. package/dist/core/defaults.d.ts +0 -1
  62. package/dist/core/defaults.js +0 -1
  63. package/dist/core/diagnostics.d.ts +0 -1
  64. package/dist/core/diagnostics.js +0 -1
  65. package/dist/core/event-bus.d.ts +0 -1
  66. package/dist/core/event-bus.js +0 -1
  67. package/dist/core/exec.d.ts +0 -1
  68. package/dist/core/exec.js +0 -1
  69. package/dist/core/experimental.d.ts +0 -1
  70. package/dist/core/experimental.js +0 -1
  71. package/dist/core/export-html/ansi-to-html.d.ts +0 -1
  72. package/dist/core/export-html/ansi-to-html.js +0 -1
  73. package/dist/core/export-html/index.d.ts +0 -1
  74. package/dist/core/export-html/index.js +0 -1
  75. package/dist/core/export-html/tool-renderer.d.ts +0 -1
  76. package/dist/core/export-html/tool-renderer.js +0 -1
  77. package/dist/core/extensions/index.d.ts +0 -1
  78. package/dist/core/extensions/index.js +0 -1
  79. package/dist/core/extensions/loader.d.ts +0 -1
  80. package/dist/core/extensions/loader.js +0 -1
  81. package/dist/core/extensions/runner.d.ts +0 -1
  82. package/dist/core/extensions/runner.js +0 -1
  83. package/dist/core/extensions/types.d.ts +0 -1
  84. package/dist/core/extensions/types.js +0 -1
  85. package/dist/core/extensions/wrapper.d.ts +0 -1
  86. package/dist/core/extensions/wrapper.js +0 -1
  87. package/dist/core/footer-data-provider.d.ts +0 -1
  88. package/dist/core/footer-data-provider.js +0 -1
  89. package/dist/core/git-command.d.ts +0 -1
  90. package/dist/core/git-command.js +0 -1
  91. package/dist/core/http-dispatcher.d.ts +0 -1
  92. package/dist/core/http-dispatcher.js +0 -1
  93. package/dist/core/index.d.ts +0 -1
  94. package/dist/core/index.js +0 -1
  95. package/dist/core/keybindings.d.ts +0 -1
  96. package/dist/core/keybindings.js +0 -1
  97. package/dist/core/llama/client.d.ts +0 -1
  98. package/dist/core/llama/client.js +0 -1
  99. package/dist/core/llama/huggingface.d.ts +0 -1
  100. package/dist/core/llama/huggingface.js +0 -1
  101. package/dist/core/llama/index.d.ts +0 -1
  102. package/dist/core/llama/index.js +0 -1
  103. package/dist/core/llama/provider.d.ts +0 -1
  104. package/dist/core/llama/provider.js +0 -1
  105. package/dist/core/llama/ui.d.ts +0 -1
  106. package/dist/core/llama/ui.js +0 -1
  107. package/dist/core/messages.d.ts +0 -1
  108. package/dist/core/messages.js +0 -1
  109. package/dist/core/model-config.d.ts +0 -1
  110. package/dist/core/model-config.js +0 -1
  111. package/dist/core/model-registry.d.ts +0 -1
  112. package/dist/core/model-registry.js +0 -1
  113. package/dist/core/model-resolver.d.ts +0 -1
  114. package/dist/core/model-resolver.js +0 -1
  115. package/dist/core/model-runtime.d.ts +0 -1
  116. package/dist/core/model-runtime.js +0 -1
  117. package/dist/core/models-store.d.ts +0 -1
  118. package/dist/core/models-store.js +0 -1
  119. package/dist/core/output-guard.d.ts +0 -1
  120. package/dist/core/output-guard.js +0 -1
  121. package/dist/core/package-manager.d.ts +0 -1
  122. package/dist/core/package-manager.js +0 -1
  123. package/dist/core/package-manager.test.d.ts +0 -1
  124. package/dist/core/package-manager.test.js +0 -1
  125. package/dist/core/project-trust.d.ts +0 -1
  126. package/dist/core/project-trust.js +0 -1
  127. package/dist/core/prompt-templates.d.ts +0 -1
  128. package/dist/core/prompt-templates.js +0 -1
  129. package/dist/core/provider-attribution.d.ts +0 -1
  130. package/dist/core/provider-attribution.js +0 -1
  131. package/dist/core/provider-composer.d.ts +0 -1
  132. package/dist/core/provider-composer.js +0 -1
  133. package/dist/core/radius.d.ts +0 -1
  134. package/dist/core/radius.js +0 -1
  135. package/dist/core/remote-catalog-provider.d.ts +0 -1
  136. package/dist/core/remote-catalog-provider.js +0 -1
  137. package/dist/core/resolve-config-value.d.ts +0 -1
  138. package/dist/core/resolve-config-value.js +0 -1
  139. package/dist/core/resource-loader.d.ts +0 -1
  140. package/dist/core/resource-loader.js +0 -1
  141. package/dist/core/runtime-credentials.d.ts +0 -1
  142. package/dist/core/runtime-credentials.js +0 -1
  143. package/dist/core/sdk.d.ts +0 -1
  144. package/dist/core/sdk.js +0 -1
  145. package/dist/core/session-cwd.d.ts +0 -1
  146. package/dist/core/session-cwd.js +0 -1
  147. package/dist/core/session-manager.d.ts +0 -1
  148. package/dist/core/session-manager.js +0 -1
  149. package/dist/core/settings-manager-auto-handoff.test.d.ts +0 -1
  150. package/dist/core/settings-manager-auto-handoff.test.js +0 -1
  151. package/dist/core/settings-manager.d.ts +0 -4
  152. package/dist/core/settings-manager.js +0 -12
  153. package/dist/core/skills.d.ts +0 -1
  154. package/dist/core/skills.js +0 -1
  155. package/dist/core/slash-commands.d.ts +0 -1
  156. package/dist/core/slash-commands.js +0 -1
  157. package/dist/core/source-info.d.ts +0 -1
  158. package/dist/core/source-info.js +0 -1
  159. package/dist/core/system-prompt.d.ts +0 -1
  160. package/dist/core/system-prompt.js +0 -1
  161. package/dist/core/system-prompt.test.d.ts +0 -1
  162. package/dist/core/system-prompt.test.js +0 -1
  163. package/dist/core/telemetry.d.ts +0 -1
  164. package/dist/core/telemetry.js +0 -1
  165. package/dist/core/timings.d.ts +0 -1
  166. package/dist/core/timings.js +0 -1
  167. package/dist/core/tools/bash.d.ts +0 -1
  168. package/dist/core/tools/bash.js +0 -1
  169. package/dist/core/tools/edit-diff.d.ts +0 -1
  170. package/dist/core/tools/edit-diff.js +0 -1
  171. package/dist/core/tools/edit.d.ts +0 -1
  172. package/dist/core/tools/edit.js +0 -1
  173. package/dist/core/tools/file-mutation-queue.d.ts +0 -1
  174. package/dist/core/tools/file-mutation-queue.js +0 -1
  175. package/dist/core/tools/find.d.ts +0 -1
  176. package/dist/core/tools/find.js +0 -1
  177. package/dist/core/tools/grep.d.ts +0 -1
  178. package/dist/core/tools/grep.js +0 -1
  179. package/dist/core/tools/index.d.ts +0 -1
  180. package/dist/core/tools/index.js +0 -1
  181. package/dist/core/tools/ls.d.ts +0 -1
  182. package/dist/core/tools/ls.js +0 -1
  183. package/dist/core/tools/output-accumulator.d.ts +0 -1
  184. package/dist/core/tools/output-accumulator.js +0 -1
  185. package/dist/core/tools/path-utils.d.ts +0 -1
  186. package/dist/core/tools/path-utils.js +0 -1
  187. package/dist/core/tools/read.d.ts +0 -1
  188. package/dist/core/tools/read.js +0 -1
  189. package/dist/core/tools/render-utils.d.ts +0 -1
  190. package/dist/core/tools/render-utils.js +0 -1
  191. package/dist/core/tools/tool-definition-wrapper.d.ts +0 -1
  192. package/dist/core/tools/tool-definition-wrapper.js +0 -1
  193. package/dist/core/tools/truncate.d.ts +0 -1
  194. package/dist/core/tools/truncate.js +0 -1
  195. package/dist/core/tools/write.d.ts +0 -1
  196. package/dist/core/tools/write.js +0 -1
  197. package/dist/core/trust-manager.d.ts +0 -1
  198. package/dist/core/trust-manager.js +0 -1
  199. package/dist/core/usage-totals.d.ts +0 -1
  200. package/dist/core/usage-totals.js +0 -1
  201. package/dist/defaults/models.json +8 -0
  202. package/dist/defaults/settings.json +6 -7
  203. package/dist/extensions/inline-skills.test.ts +13 -13
  204. package/dist/extensions/inline-skills.ts +6 -6
  205. package/dist/extensions/pi-subagents/CHANGELOG.md +89 -12
  206. package/dist/extensions/pi-subagents/README.md +339 -604
  207. package/dist/extensions/pi-subagents/package-lock.json +190 -2079
  208. package/dist/extensions/pi-subagents/package.json +13 -5
  209. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +1 -1
  210. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +1 -1
  211. package/dist/extensions/pi-subagents/prompts/review-loop.md +1 -1
  212. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +10 -10
  213. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +32 -43
  214. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +106 -105
  215. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +3 -3
  216. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +28 -35
  217. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +167 -124
  218. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +19 -0
  219. package/dist/extensions/pi-subagents/src/agents/agents.ts +163 -75
  220. package/dist/extensions/pi-subagents/src/agents/chain-serializer.ts +10 -7
  221. package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +5 -3
  222. package/dist/extensions/pi-subagents/src/agents/identity.ts +1 -1
  223. package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +14 -11
  224. package/dist/extensions/pi-subagents/src/agents/skills.ts +23 -6
  225. package/dist/extensions/pi-subagents/src/agents/task-aware-routing.ts +8 -1
  226. package/dist/extensions/pi-subagents/src/api/control-channel.ts +4 -0
  227. package/dist/extensions/pi-subagents/src/api/delegation.ts +26 -194
  228. package/dist/extensions/pi-subagents/src/api/external-runs.ts +129 -0
  229. package/dist/extensions/pi-subagents/src/api/intercom-bridge.ts +3 -0
  230. package/dist/extensions/pi-subagents/src/api/pi-args.ts +5 -0
  231. package/dist/extensions/pi-subagents/src/api/preflight.ts +4 -4
  232. package/dist/extensions/pi-subagents/src/api/shared-types.ts +19 -0
  233. package/dist/extensions/pi-subagents/src/extension/config.ts +10 -0
  234. package/dist/extensions/pi-subagents/src/extension/control-notices.ts +5 -39
  235. package/dist/extensions/pi-subagents/src/extension/doctor.ts +10 -9
  236. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +7 -4
  237. package/dist/extensions/pi-subagents/src/extension/index.ts +243 -77
  238. package/dist/extensions/pi-subagents/src/extension/rpc.ts +18 -6
  239. package/dist/extensions/pi-subagents/src/extension/schemas.ts +50 -39
  240. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +36 -103
  241. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +229 -0
  242. package/dist/extensions/pi-subagents/src/inspectors/herdr/client.ts +130 -0
  243. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +141 -0
  244. package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +154 -0
  245. package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +330 -0
  246. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +3 -2
  247. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +5 -1
  248. package/dist/extensions/pi-subagents/src/missions/actions.ts +372 -0
  249. package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +314 -0
  250. package/dist/extensions/pi-subagents/src/missions/store.ts +442 -0
  251. package/dist/extensions/pi-subagents/src/missions/types.ts +135 -0
  252. package/dist/extensions/pi-subagents/src/policy/authority.ts +46 -0
  253. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +32 -6
  254. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +103 -72
  255. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +10 -2
  256. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +6 -6
  257. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +29 -1
  258. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +3 -3
  259. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +3 -2
  260. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +9 -7
  261. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +3 -4
  262. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +3 -28
  263. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +5 -5
  264. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +18 -68
  265. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +3 -3
  266. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +35 -8
  267. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +602 -375
  268. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -3
  269. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +612 -464
  270. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +50 -9
  271. package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +253 -0
  272. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +12 -4
  273. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +3 -3
  274. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +8 -4
  275. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +74 -102
  276. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +21 -22
  277. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1050 -396
  278. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +44 -18
  279. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +0 -1
  280. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +302 -22
  281. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
  282. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +130 -0
  283. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +42 -1
  284. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +1 -1
  285. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +59 -5
  286. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +9 -4
  287. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +86 -2
  288. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +11 -2
  289. package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +95 -0
  290. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +11 -1
  291. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +15 -6
  292. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +1 -1
  293. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +9 -63
  294. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +37 -6
  295. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +5 -2
  296. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +2 -23
  297. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +6 -6
  298. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +124 -13
  299. package/dist/extensions/pi-subagents/src/shared/accessible-dir.ts +29 -7
  300. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +18 -1
  301. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +3 -2
  302. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +1 -0
  303. package/dist/extensions/pi-subagents/src/shared/settings.ts +10 -0
  304. package/dist/extensions/pi-subagents/src/shared/types.ts +158 -55
  305. package/dist/extensions/pi-subagents/src/shared/utils.ts +16 -66
  306. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +33 -199
  307. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +43 -126
  308. package/dist/extensions/pi-subagents/src/slash/inline-subagents.ts +103 -0
  309. package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +158 -205
  310. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +22 -58
  311. package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +14 -0
  312. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +44 -642
  313. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +18 -14
  314. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +156 -21
  315. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +110 -5
  316. package/dist/extensions/pi-subagents/src/tui/fleet.ts +60 -24
  317. package/dist/extensions/pi-subagents/src/tui/render.ts +296 -140
  318. package/dist/extensions/pi-subagents/src/types/pi-runtime-compat.d.ts +14 -0
  319. package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +1 -1
  320. package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +13 -7
  321. package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +2 -2
  322. package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +145 -0
  323. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +1 -1
  324. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +1 -1
  325. package/dist/extensions/pi-subagents/src/watchdog/review.ts +4 -1
  326. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +3 -2
  327. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +140 -0
  328. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +415 -0
  329. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +41 -144
  330. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +16 -16
  331. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +415 -298
  332. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +149 -82
  333. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +40 -40
  334. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +15 -15
  335. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +193 -133
  336. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +1 -49
  337. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +62 -0
  338. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +56 -58
  339. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +81 -68
  340. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +20 -25
  341. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +425 -317
  342. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +73 -73
  343. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +123 -98
  344. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +653 -240
  345. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +20 -1661
  346. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +9 -9
  347. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +3 -3
  348. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +2 -2
  349. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +8 -5
  350. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +6 -6
  351. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +4 -20
  352. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +25 -4
  353. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +1 -1
  354. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +280 -295
  355. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +47 -207
  356. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +26 -26
  357. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +89 -89
  358. package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +9 -9
  359. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +10 -0
  360. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +33 -9
  361. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +24 -2
  362. package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +11 -9
  363. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +2 -2
  364. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +40 -40
  365. package/dist/extensions/pi-subagents/test/unit/authority-policy.test.ts +22 -0
  366. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +1 -1
  367. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +5 -39
  368. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +19 -19
  369. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +9 -9
  370. package/dist/extensions/pi-subagents/test/unit/chain-serializer.test.ts +20 -20
  371. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +99 -0
  372. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +9 -9
  373. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +41 -0
  374. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +2 -2
  375. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +35 -5
  376. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +7 -0
  377. package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +16 -82
  378. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +162 -620
  379. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
  380. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +25 -25
  381. package/dist/extensions/pi-subagents/test/unit/ensure-accessible-dir.test.ts +125 -0
  382. package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +94 -0
  383. package/dist/extensions/pi-subagents/test/unit/external-runs.test.ts +66 -0
  384. package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +3 -3
  385. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +242 -52
  386. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +127 -0
  387. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +197 -36
  388. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +4 -4
  389. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +245 -0
  390. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +494 -0
  391. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +261 -31
  392. package/dist/extensions/pi-subagents/test/unit/inline-subagents.test.ts +52 -0
  393. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +6 -6
  394. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +217 -0
  395. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +182 -0
  396. package/dist/extensions/pi-subagents/test/unit/mock-pi.test.ts +22 -0
  397. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +1 -1
  398. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +10 -10
  399. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +20 -12
  400. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +47 -5
  401. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +49 -55
  402. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +20 -3
  403. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +37 -7
  404. package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +10 -10
  405. package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +3 -3
  406. package/dist/extensions/pi-subagents/test/unit/permissions.test.ts +46 -0
  407. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +31 -10
  408. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +40 -16
  409. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +42 -44
  410. package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +13 -13
  411. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +4 -4
  412. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +75 -9
  413. package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +20 -84
  414. package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +15 -16
  415. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +28 -28
  416. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +19 -12
  417. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +37 -21
  418. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +1 -1
  419. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +50 -50
  420. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +396 -433
  421. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +76 -153
  422. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +411 -0
  423. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +1 -91
  424. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +139 -0
  425. package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +29 -0
  426. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +13 -13
  427. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -2
  428. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +2 -2
  429. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +35 -35
  430. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +53 -18
  431. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +13 -1
  432. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +44 -11
  433. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +2 -26
  434. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +24 -148
  435. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +11 -9
  436. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +351 -0
  437. package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +13 -13
  438. package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +1 -1
  439. package/dist/extensions/pi-subagents/test/unit/watchdog-permission-arbiter.test.ts +79 -0
  440. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +1 -1
  441. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +8 -8
  442. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +45 -9
  443. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +197 -0
  444. package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +8 -8
  445. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +142 -12
  446. package/dist/extensions/pi-subagents/tsconfig.json +15 -0
  447. package/dist/extensions/pi-web-agent/package.json +1 -1
  448. package/dist/extensions/rtk.test.ts +126 -0
  449. package/dist/extensions/rtk.ts +56 -20
  450. package/dist/index.d.ts +1 -1
  451. package/dist/index.js +1 -1
  452. package/dist/main.d.ts +0 -1
  453. package/dist/main.js +0 -1
  454. package/dist/migrations.d.ts +0 -1
  455. package/dist/migrations.js +0 -1
  456. package/dist/modes/index.d.ts +0 -1
  457. package/dist/modes/index.js +0 -1
  458. package/dist/modes/interactive/components/armin.d.ts +0 -1
  459. package/dist/modes/interactive/components/armin.js +0 -1
  460. package/dist/modes/interactive/components/assistant-message.d.ts +0 -1
  461. package/dist/modes/interactive/components/assistant-message.js +0 -1
  462. package/dist/modes/interactive/components/bash-execution.d.ts +0 -1
  463. package/dist/modes/interactive/components/bash-execution.js +0 -1
  464. package/dist/modes/interactive/components/bordered-loader.d.ts +0 -1
  465. package/dist/modes/interactive/components/bordered-loader.js +0 -1
  466. package/dist/modes/interactive/components/branch-summary-message.d.ts +0 -1
  467. package/dist/modes/interactive/components/branch-summary-message.js +0 -1
  468. package/dist/modes/interactive/components/compaction-summary-message.d.ts +0 -1
  469. package/dist/modes/interactive/components/compaction-summary-message.js +0 -1
  470. package/dist/modes/interactive/components/config-selector.d.ts +0 -1
  471. package/dist/modes/interactive/components/config-selector.js +0 -1
  472. package/dist/modes/interactive/components/countdown-timer.d.ts +0 -1
  473. package/dist/modes/interactive/components/countdown-timer.js +0 -1
  474. package/dist/modes/interactive/components/custom-editor.d.ts +0 -1
  475. package/dist/modes/interactive/components/custom-editor.js +0 -1
  476. package/dist/modes/interactive/components/custom-entry.d.ts +0 -1
  477. package/dist/modes/interactive/components/custom-entry.js +0 -1
  478. package/dist/modes/interactive/components/custom-message.d.ts +0 -1
  479. package/dist/modes/interactive/components/custom-message.js +0 -1
  480. package/dist/modes/interactive/components/daxnuts.d.ts +0 -1
  481. package/dist/modes/interactive/components/daxnuts.js +0 -1
  482. package/dist/modes/interactive/components/diff.d.ts +0 -1
  483. package/dist/modes/interactive/components/diff.js +0 -1
  484. package/dist/modes/interactive/components/dynamic-border.d.ts +0 -1
  485. package/dist/modes/interactive/components/dynamic-border.js +0 -1
  486. package/dist/modes/interactive/components/earendil-announcement.d.ts +0 -1
  487. package/dist/modes/interactive/components/earendil-announcement.js +0 -1
  488. package/dist/modes/interactive/components/extension-editor.d.ts +0 -1
  489. package/dist/modes/interactive/components/extension-editor.js +0 -1
  490. package/dist/modes/interactive/components/extension-input.d.ts +0 -1
  491. package/dist/modes/interactive/components/extension-input.js +0 -1
  492. package/dist/modes/interactive/components/extension-selector.d.ts +0 -1
  493. package/dist/modes/interactive/components/extension-selector.js +0 -1
  494. package/dist/modes/interactive/components/first-time-setup.d.ts +0 -1
  495. package/dist/modes/interactive/components/first-time-setup.js +0 -1
  496. package/dist/modes/interactive/components/footer.d.ts +0 -1
  497. package/dist/modes/interactive/components/footer.js +0 -1
  498. package/dist/modes/interactive/components/index.d.ts +0 -1
  499. package/dist/modes/interactive/components/index.js +0 -1
  500. package/dist/modes/interactive/components/keybinding-hints.d.ts +0 -1
  501. package/dist/modes/interactive/components/keybinding-hints.js +0 -1
  502. package/dist/modes/interactive/components/login-dialog.d.ts +0 -1
  503. package/dist/modes/interactive/components/login-dialog.js +0 -1
  504. package/dist/modes/interactive/components/model-selector.d.ts +0 -1
  505. package/dist/modes/interactive/components/model-selector.js +0 -1
  506. package/dist/modes/interactive/components/oauth-selector.d.ts +0 -1
  507. package/dist/modes/interactive/components/oauth-selector.js +0 -1
  508. package/dist/modes/interactive/components/scoped-models-selector.d.ts +0 -1
  509. package/dist/modes/interactive/components/scoped-models-selector.js +0 -1
  510. package/dist/modes/interactive/components/session-selector-search.d.ts +0 -1
  511. package/dist/modes/interactive/components/session-selector-search.js +0 -1
  512. package/dist/modes/interactive/components/session-selector.d.ts +0 -1
  513. package/dist/modes/interactive/components/session-selector.js +0 -1
  514. package/dist/modes/interactive/components/settings-selector.d.ts +0 -3
  515. package/dist/modes/interactive/components/settings-selector.js +1 -14
  516. package/dist/modes/interactive/components/show-images-selector.d.ts +0 -1
  517. package/dist/modes/interactive/components/show-images-selector.js +0 -1
  518. package/dist/modes/interactive/components/skill-invocation-message.d.ts +0 -1
  519. package/dist/modes/interactive/components/skill-invocation-message.js +0 -1
  520. package/dist/modes/interactive/components/status-indicator.d.ts +0 -1
  521. package/dist/modes/interactive/components/status-indicator.js +0 -1
  522. package/dist/modes/interactive/components/theme-selector.d.ts +0 -1
  523. package/dist/modes/interactive/components/theme-selector.js +0 -1
  524. package/dist/modes/interactive/components/thinking-selector.d.ts +0 -1
  525. package/dist/modes/interactive/components/thinking-selector.js +0 -1
  526. package/dist/modes/interactive/components/tool-execution.d.ts +0 -1
  527. package/dist/modes/interactive/components/tool-execution.js +0 -1
  528. package/dist/modes/interactive/components/tree-selector.d.ts +0 -1
  529. package/dist/modes/interactive/components/tree-selector.js +0 -1
  530. package/dist/modes/interactive/components/trust-selector.d.ts +0 -1
  531. package/dist/modes/interactive/components/trust-selector.js +0 -1
  532. package/dist/modes/interactive/components/user-message-selector.d.ts +0 -1
  533. package/dist/modes/interactive/components/user-message-selector.js +0 -1
  534. package/dist/modes/interactive/components/user-message.d.ts +0 -1
  535. package/dist/modes/interactive/components/user-message.js +0 -1
  536. package/dist/modes/interactive/components/visual-truncate.d.ts +0 -1
  537. package/dist/modes/interactive/components/visual-truncate.js +0 -1
  538. package/dist/modes/interactive/external-editor.d.ts +0 -1
  539. package/dist/modes/interactive/external-editor.js +0 -1
  540. package/dist/modes/interactive/interactive-mode.d.ts +0 -2
  541. package/dist/modes/interactive/interactive-mode.js +1 -22
  542. package/dist/modes/interactive/model-search.d.ts +0 -1
  543. package/dist/modes/interactive/model-search.js +0 -1
  544. package/dist/modes/interactive/theme/theme-controller.d.ts +0 -1
  545. package/dist/modes/interactive/theme/theme-controller.js +0 -1
  546. package/dist/modes/interactive/theme/theme.d.ts +0 -1
  547. package/dist/modes/interactive/theme/theme.js +0 -1
  548. package/dist/modes/print-mode.d.ts +0 -1
  549. package/dist/modes/print-mode.js +0 -1
  550. package/dist/modes/rpc/jsonl.d.ts +0 -1
  551. package/dist/modes/rpc/jsonl.js +0 -1
  552. package/dist/modes/rpc/rpc-client.d.ts +1 -2
  553. package/dist/modes/rpc/rpc-client.js +1 -2
  554. package/dist/modes/rpc/rpc-mode.d.ts +0 -1
  555. package/dist/modes/rpc/rpc-mode.js +0 -9
  556. package/dist/modes/rpc/rpc-types.d.ts +0 -1
  557. package/dist/modes/rpc/rpc-types.js +0 -1
  558. package/dist/package-manager-cli.d.ts +0 -1
  559. package/dist/package-manager-cli.js +0 -1
  560. package/dist/rpc-entry.d.ts +0 -1
  561. package/dist/rpc-entry.js +0 -1
  562. package/dist/utils/ansi.d.ts +0 -1
  563. package/dist/utils/ansi.js +0 -1
  564. package/dist/utils/changelog.d.ts +0 -1
  565. package/dist/utils/changelog.js +0 -1
  566. package/dist/utils/child-process.d.ts +0 -1
  567. package/dist/utils/child-process.js +0 -1
  568. package/dist/utils/clipboard-image.d.ts +0 -1
  569. package/dist/utils/clipboard-image.js +0 -1
  570. package/dist/utils/clipboard-native.d.ts +0 -1
  571. package/dist/utils/clipboard-native.js +0 -1
  572. package/dist/utils/clipboard.d.ts +0 -1
  573. package/dist/utils/clipboard.js +0 -1
  574. package/dist/utils/deprecation.d.ts +0 -1
  575. package/dist/utils/deprecation.js +0 -1
  576. package/dist/utils/exif-orientation.d.ts +0 -1
  577. package/dist/utils/exif-orientation.js +0 -1
  578. package/dist/utils/frontmatter.d.ts +0 -1
  579. package/dist/utils/frontmatter.js +0 -1
  580. package/dist/utils/fs-watch.d.ts +0 -1
  581. package/dist/utils/fs-watch.js +0 -1
  582. package/dist/utils/git.d.ts +0 -1
  583. package/dist/utils/git.js +0 -1
  584. package/dist/utils/html.d.ts +0 -1
  585. package/dist/utils/html.js +0 -1
  586. package/dist/utils/image-convert.d.ts +0 -1
  587. package/dist/utils/image-convert.js +0 -1
  588. package/dist/utils/image-process.d.ts +0 -1
  589. package/dist/utils/image-process.js +0 -1
  590. package/dist/utils/image-resize-core.d.ts +0 -1
  591. package/dist/utils/image-resize-core.js +0 -1
  592. package/dist/utils/image-resize-worker.d.ts +0 -1
  593. package/dist/utils/image-resize-worker.js +0 -1
  594. package/dist/utils/image-resize.d.ts +0 -1
  595. package/dist/utils/image-resize.js +0 -1
  596. package/dist/utils/json.d.ts +0 -1
  597. package/dist/utils/json.js +0 -1
  598. package/dist/utils/mime.d.ts +0 -1
  599. package/dist/utils/mime.js +0 -1
  600. package/dist/utils/open-browser.d.ts +0 -1
  601. package/dist/utils/open-browser.js +0 -1
  602. package/dist/utils/paths.d.ts +0 -1
  603. package/dist/utils/paths.js +0 -1
  604. package/dist/utils/photon.d.ts +0 -1
  605. package/dist/utils/photon.js +0 -1
  606. package/dist/utils/pi-user-agent.d.ts +0 -1
  607. package/dist/utils/pi-user-agent.js +0 -1
  608. package/dist/utils/shell.d.ts +0 -1
  609. package/dist/utils/shell.js +0 -1
  610. package/dist/utils/sleep.d.ts +0 -1
  611. package/dist/utils/sleep.js +0 -1
  612. package/dist/utils/syntax-highlight.d.ts +0 -1
  613. package/dist/utils/syntax-highlight.js +0 -1
  614. package/dist/utils/thinking-tags.d.ts +0 -1
  615. package/dist/utils/thinking-tags.js +0 -1
  616. package/dist/utils/tools-manager.d.ts +3 -3
  617. package/dist/utils/tools-manager.js +125 -42
  618. package/dist/utils/version-check.d.ts +0 -1
  619. package/dist/utils/version-check.js +0 -1
  620. package/dist/utils/windows-self-update.d.ts +0 -1
  621. package/dist/utils/windows-self-update.js +0 -1
  622. package/docs/rpc.md +1 -2
  623. package/docs/settings.md +0 -1
  624. package/docs/skills.md +10 -18
  625. package/package.json +4 -3
  626. package/dist/bun/cli.d.ts.map +0 -1
  627. package/dist/bun/cli.js.map +0 -1
  628. package/dist/bun/register-bedrock.d.ts.map +0 -1
  629. package/dist/bun/register-bedrock.js.map +0 -1
  630. package/dist/bun/restore-sandbox-env.d.ts.map +0 -1
  631. package/dist/bun/restore-sandbox-env.js.map +0 -1
  632. package/dist/cli/args.d.ts.map +0 -1
  633. package/dist/cli/args.js.map +0 -1
  634. package/dist/cli/config-selector.d.ts.map +0 -1
  635. package/dist/cli/config-selector.js.map +0 -1
  636. package/dist/cli/credential-print.d.ts.map +0 -1
  637. package/dist/cli/credential-print.js.map +0 -1
  638. package/dist/cli/file-processor.d.ts.map +0 -1
  639. package/dist/cli/file-processor.js.map +0 -1
  640. package/dist/cli/initial-message.d.ts.map +0 -1
  641. package/dist/cli/initial-message.js.map +0 -1
  642. package/dist/cli/list-models.d.ts.map +0 -1
  643. package/dist/cli/list-models.js.map +0 -1
  644. package/dist/cli/project-trust.d.ts.map +0 -1
  645. package/dist/cli/project-trust.js.map +0 -1
  646. package/dist/cli/session-picker.d.ts.map +0 -1
  647. package/dist/cli/session-picker.js.map +0 -1
  648. package/dist/cli/startup-ui.d.ts.map +0 -1
  649. package/dist/cli/startup-ui.js.map +0 -1
  650. package/dist/cli.d.ts.map +0 -1
  651. package/dist/cli.js.map +0 -1
  652. package/dist/config.d.ts.map +0 -1
  653. package/dist/config.js.map +0 -1
  654. package/dist/core/agent-session-auto-handoff.test.d.ts.map +0 -1
  655. package/dist/core/agent-session-auto-handoff.test.js.map +0 -1
  656. package/dist/core/agent-session-runtime.d.ts.map +0 -1
  657. package/dist/core/agent-session-runtime.js.map +0 -1
  658. package/dist/core/agent-session-services.d.ts.map +0 -1
  659. package/dist/core/agent-session-services.js.map +0 -1
  660. package/dist/core/agent-session-skill-block.test.d.ts.map +0 -1
  661. package/dist/core/agent-session-skill-block.test.js.map +0 -1
  662. package/dist/core/agent-session.d.ts.map +0 -1
  663. package/dist/core/agent-session.js.map +0 -1
  664. package/dist/core/agents.d.ts.map +0 -1
  665. package/dist/core/agents.js.map +0 -1
  666. package/dist/core/auth-guidance.d.ts.map +0 -1
  667. package/dist/core/auth-guidance.js.map +0 -1
  668. package/dist/core/auth-storage.d.ts.map +0 -1
  669. package/dist/core/auth-storage.js.map +0 -1
  670. package/dist/core/bash-executor.d.ts.map +0 -1
  671. package/dist/core/bash-executor.js.map +0 -1
  672. package/dist/core/built-in-extensions.d.ts.map +0 -1
  673. package/dist/core/built-in-extensions.js.map +0 -1
  674. package/dist/core/cache-stats.d.ts.map +0 -1
  675. package/dist/core/cache-stats.js.map +0 -1
  676. package/dist/core/compaction/branch-summarization.d.ts.map +0 -1
  677. package/dist/core/compaction/branch-summarization.js.map +0 -1
  678. package/dist/core/compaction/compaction.d.ts.map +0 -1
  679. package/dist/core/compaction/compaction.js.map +0 -1
  680. package/dist/core/compaction/index.d.ts.map +0 -1
  681. package/dist/core/compaction/index.js.map +0 -1
  682. package/dist/core/compaction/utils.d.ts.map +0 -1
  683. package/dist/core/compaction/utils.js.map +0 -1
  684. package/dist/core/defaults.d.ts.map +0 -1
  685. package/dist/core/defaults.js.map +0 -1
  686. package/dist/core/diagnostics.d.ts.map +0 -1
  687. package/dist/core/diagnostics.js.map +0 -1
  688. package/dist/core/event-bus.d.ts.map +0 -1
  689. package/dist/core/event-bus.js.map +0 -1
  690. package/dist/core/exec.d.ts.map +0 -1
  691. package/dist/core/exec.js.map +0 -1
  692. package/dist/core/experimental.d.ts.map +0 -1
  693. package/dist/core/experimental.js.map +0 -1
  694. package/dist/core/export-html/ansi-to-html.d.ts.map +0 -1
  695. package/dist/core/export-html/ansi-to-html.js.map +0 -1
  696. package/dist/core/export-html/index.d.ts.map +0 -1
  697. package/dist/core/export-html/index.js.map +0 -1
  698. package/dist/core/export-html/tool-renderer.d.ts.map +0 -1
  699. package/dist/core/export-html/tool-renderer.js.map +0 -1
  700. package/dist/core/extensions/index.d.ts.map +0 -1
  701. package/dist/core/extensions/index.js.map +0 -1
  702. package/dist/core/extensions/loader.d.ts.map +0 -1
  703. package/dist/core/extensions/loader.js.map +0 -1
  704. package/dist/core/extensions/runner.d.ts.map +0 -1
  705. package/dist/core/extensions/runner.js.map +0 -1
  706. package/dist/core/extensions/types.d.ts.map +0 -1
  707. package/dist/core/extensions/types.js.map +0 -1
  708. package/dist/core/extensions/wrapper.d.ts.map +0 -1
  709. package/dist/core/extensions/wrapper.js.map +0 -1
  710. package/dist/core/footer-data-provider.d.ts.map +0 -1
  711. package/dist/core/footer-data-provider.js.map +0 -1
  712. package/dist/core/git-command.d.ts.map +0 -1
  713. package/dist/core/git-command.js.map +0 -1
  714. package/dist/core/http-dispatcher.d.ts.map +0 -1
  715. package/dist/core/http-dispatcher.js.map +0 -1
  716. package/dist/core/index.d.ts.map +0 -1
  717. package/dist/core/index.js.map +0 -1
  718. package/dist/core/keybindings.d.ts.map +0 -1
  719. package/dist/core/keybindings.js.map +0 -1
  720. package/dist/core/llama/client.d.ts.map +0 -1
  721. package/dist/core/llama/client.js.map +0 -1
  722. package/dist/core/llama/huggingface.d.ts.map +0 -1
  723. package/dist/core/llama/huggingface.js.map +0 -1
  724. package/dist/core/llama/index.d.ts.map +0 -1
  725. package/dist/core/llama/index.js.map +0 -1
  726. package/dist/core/llama/provider.d.ts.map +0 -1
  727. package/dist/core/llama/provider.js.map +0 -1
  728. package/dist/core/llama/ui.d.ts.map +0 -1
  729. package/dist/core/llama/ui.js.map +0 -1
  730. package/dist/core/messages.d.ts.map +0 -1
  731. package/dist/core/messages.js.map +0 -1
  732. package/dist/core/model-config.d.ts.map +0 -1
  733. package/dist/core/model-config.js.map +0 -1
  734. package/dist/core/model-registry.d.ts.map +0 -1
  735. package/dist/core/model-registry.js.map +0 -1
  736. package/dist/core/model-resolver.d.ts.map +0 -1
  737. package/dist/core/model-resolver.js.map +0 -1
  738. package/dist/core/model-runtime.d.ts.map +0 -1
  739. package/dist/core/model-runtime.js.map +0 -1
  740. package/dist/core/models-store.d.ts.map +0 -1
  741. package/dist/core/models-store.js.map +0 -1
  742. package/dist/core/output-guard.d.ts.map +0 -1
  743. package/dist/core/output-guard.js.map +0 -1
  744. package/dist/core/package-manager.d.ts.map +0 -1
  745. package/dist/core/package-manager.js.map +0 -1
  746. package/dist/core/package-manager.test.d.ts.map +0 -1
  747. package/dist/core/package-manager.test.js.map +0 -1
  748. package/dist/core/project-trust.d.ts.map +0 -1
  749. package/dist/core/project-trust.js.map +0 -1
  750. package/dist/core/prompt-templates.d.ts.map +0 -1
  751. package/dist/core/prompt-templates.js.map +0 -1
  752. package/dist/core/provider-attribution.d.ts.map +0 -1
  753. package/dist/core/provider-attribution.js.map +0 -1
  754. package/dist/core/provider-composer.d.ts.map +0 -1
  755. package/dist/core/provider-composer.js.map +0 -1
  756. package/dist/core/radius.d.ts.map +0 -1
  757. package/dist/core/radius.js.map +0 -1
  758. package/dist/core/remote-catalog-provider.d.ts.map +0 -1
  759. package/dist/core/remote-catalog-provider.js.map +0 -1
  760. package/dist/core/resolve-config-value.d.ts.map +0 -1
  761. package/dist/core/resolve-config-value.js.map +0 -1
  762. package/dist/core/resource-loader.d.ts.map +0 -1
  763. package/dist/core/resource-loader.js.map +0 -1
  764. package/dist/core/runtime-credentials.d.ts.map +0 -1
  765. package/dist/core/runtime-credentials.js.map +0 -1
  766. package/dist/core/sdk.d.ts.map +0 -1
  767. package/dist/core/sdk.js.map +0 -1
  768. package/dist/core/session-cwd.d.ts.map +0 -1
  769. package/dist/core/session-cwd.js.map +0 -1
  770. package/dist/core/session-manager.d.ts.map +0 -1
  771. package/dist/core/session-manager.js.map +0 -1
  772. package/dist/core/settings-manager-auto-handoff.test.d.ts.map +0 -1
  773. package/dist/core/settings-manager-auto-handoff.test.js.map +0 -1
  774. package/dist/core/settings-manager.d.ts.map +0 -1
  775. package/dist/core/settings-manager.js.map +0 -1
  776. package/dist/core/skills.d.ts.map +0 -1
  777. package/dist/core/skills.js.map +0 -1
  778. package/dist/core/slash-commands.d.ts.map +0 -1
  779. package/dist/core/slash-commands.js.map +0 -1
  780. package/dist/core/source-info.d.ts.map +0 -1
  781. package/dist/core/source-info.js.map +0 -1
  782. package/dist/core/system-prompt.d.ts.map +0 -1
  783. package/dist/core/system-prompt.js.map +0 -1
  784. package/dist/core/system-prompt.test.d.ts.map +0 -1
  785. package/dist/core/system-prompt.test.js.map +0 -1
  786. package/dist/core/telemetry.d.ts.map +0 -1
  787. package/dist/core/telemetry.js.map +0 -1
  788. package/dist/core/timings.d.ts.map +0 -1
  789. package/dist/core/timings.js.map +0 -1
  790. package/dist/core/tools/bash.d.ts.map +0 -1
  791. package/dist/core/tools/bash.js.map +0 -1
  792. package/dist/core/tools/edit-diff.d.ts.map +0 -1
  793. package/dist/core/tools/edit-diff.js.map +0 -1
  794. package/dist/core/tools/edit.d.ts.map +0 -1
  795. package/dist/core/tools/edit.js.map +0 -1
  796. package/dist/core/tools/file-mutation-queue.d.ts.map +0 -1
  797. package/dist/core/tools/file-mutation-queue.js.map +0 -1
  798. package/dist/core/tools/find.d.ts.map +0 -1
  799. package/dist/core/tools/find.js.map +0 -1
  800. package/dist/core/tools/grep.d.ts.map +0 -1
  801. package/dist/core/tools/grep.js.map +0 -1
  802. package/dist/core/tools/index.d.ts.map +0 -1
  803. package/dist/core/tools/index.js.map +0 -1
  804. package/dist/core/tools/ls.d.ts.map +0 -1
  805. package/dist/core/tools/ls.js.map +0 -1
  806. package/dist/core/tools/output-accumulator.d.ts.map +0 -1
  807. package/dist/core/tools/output-accumulator.js.map +0 -1
  808. package/dist/core/tools/path-utils.d.ts.map +0 -1
  809. package/dist/core/tools/path-utils.js.map +0 -1
  810. package/dist/core/tools/read.d.ts.map +0 -1
  811. package/dist/core/tools/read.js.map +0 -1
  812. package/dist/core/tools/render-utils.d.ts.map +0 -1
  813. package/dist/core/tools/render-utils.js.map +0 -1
  814. package/dist/core/tools/tool-definition-wrapper.d.ts.map +0 -1
  815. package/dist/core/tools/tool-definition-wrapper.js.map +0 -1
  816. package/dist/core/tools/truncate.d.ts.map +0 -1
  817. package/dist/core/tools/truncate.js.map +0 -1
  818. package/dist/core/tools/write.d.ts.map +0 -1
  819. package/dist/core/tools/write.js.map +0 -1
  820. package/dist/core/trust-manager.d.ts.map +0 -1
  821. package/dist/core/trust-manager.js.map +0 -1
  822. package/dist/core/usage-totals.d.ts.map +0 -1
  823. package/dist/core/usage-totals.js.map +0 -1
  824. package/dist/extensions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  825. package/dist/extensions/pi-subagents/test/unit/slash-chain-groups.test.ts +0 -464
  826. package/dist/index.d.ts.map +0 -1
  827. package/dist/index.js.map +0 -1
  828. package/dist/main.d.ts.map +0 -1
  829. package/dist/main.js.map +0 -1
  830. package/dist/migrations.d.ts.map +0 -1
  831. package/dist/migrations.js.map +0 -1
  832. package/dist/modes/index.d.ts.map +0 -1
  833. package/dist/modes/index.js.map +0 -1
  834. package/dist/modes/interactive/components/armin.d.ts.map +0 -1
  835. package/dist/modes/interactive/components/armin.js.map +0 -1
  836. package/dist/modes/interactive/components/assistant-message.d.ts.map +0 -1
  837. package/dist/modes/interactive/components/assistant-message.js.map +0 -1
  838. package/dist/modes/interactive/components/bash-execution.d.ts.map +0 -1
  839. package/dist/modes/interactive/components/bash-execution.js.map +0 -1
  840. package/dist/modes/interactive/components/bordered-loader.d.ts.map +0 -1
  841. package/dist/modes/interactive/components/bordered-loader.js.map +0 -1
  842. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +0 -1
  843. package/dist/modes/interactive/components/branch-summary-message.js.map +0 -1
  844. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +0 -1
  845. package/dist/modes/interactive/components/compaction-summary-message.js.map +0 -1
  846. package/dist/modes/interactive/components/config-selector.d.ts.map +0 -1
  847. package/dist/modes/interactive/components/config-selector.js.map +0 -1
  848. package/dist/modes/interactive/components/countdown-timer.d.ts.map +0 -1
  849. package/dist/modes/interactive/components/countdown-timer.js.map +0 -1
  850. package/dist/modes/interactive/components/custom-editor.d.ts.map +0 -1
  851. package/dist/modes/interactive/components/custom-editor.js.map +0 -1
  852. package/dist/modes/interactive/components/custom-entry.d.ts.map +0 -1
  853. package/dist/modes/interactive/components/custom-entry.js.map +0 -1
  854. package/dist/modes/interactive/components/custom-message.d.ts.map +0 -1
  855. package/dist/modes/interactive/components/custom-message.js.map +0 -1
  856. package/dist/modes/interactive/components/daxnuts.d.ts.map +0 -1
  857. package/dist/modes/interactive/components/daxnuts.js.map +0 -1
  858. package/dist/modes/interactive/components/diff.d.ts.map +0 -1
  859. package/dist/modes/interactive/components/diff.js.map +0 -1
  860. package/dist/modes/interactive/components/dynamic-border.d.ts.map +0 -1
  861. package/dist/modes/interactive/components/dynamic-border.js.map +0 -1
  862. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +0 -1
  863. package/dist/modes/interactive/components/earendil-announcement.js.map +0 -1
  864. package/dist/modes/interactive/components/extension-editor.d.ts.map +0 -1
  865. package/dist/modes/interactive/components/extension-editor.js.map +0 -1
  866. package/dist/modes/interactive/components/extension-input.d.ts.map +0 -1
  867. package/dist/modes/interactive/components/extension-input.js.map +0 -1
  868. package/dist/modes/interactive/components/extension-selector.d.ts.map +0 -1
  869. package/dist/modes/interactive/components/extension-selector.js.map +0 -1
  870. package/dist/modes/interactive/components/first-time-setup.d.ts.map +0 -1
  871. package/dist/modes/interactive/components/first-time-setup.js.map +0 -1
  872. package/dist/modes/interactive/components/footer.d.ts.map +0 -1
  873. package/dist/modes/interactive/components/footer.js.map +0 -1
  874. package/dist/modes/interactive/components/index.d.ts.map +0 -1
  875. package/dist/modes/interactive/components/index.js.map +0 -1
  876. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +0 -1
  877. package/dist/modes/interactive/components/keybinding-hints.js.map +0 -1
  878. package/dist/modes/interactive/components/login-dialog.d.ts.map +0 -1
  879. package/dist/modes/interactive/components/login-dialog.js.map +0 -1
  880. package/dist/modes/interactive/components/model-selector.d.ts.map +0 -1
  881. package/dist/modes/interactive/components/model-selector.js.map +0 -1
  882. package/dist/modes/interactive/components/oauth-selector.d.ts.map +0 -1
  883. package/dist/modes/interactive/components/oauth-selector.js.map +0 -1
  884. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +0 -1
  885. package/dist/modes/interactive/components/scoped-models-selector.js.map +0 -1
  886. package/dist/modes/interactive/components/session-selector-search.d.ts.map +0 -1
  887. package/dist/modes/interactive/components/session-selector-search.js.map +0 -1
  888. package/dist/modes/interactive/components/session-selector.d.ts.map +0 -1
  889. package/dist/modes/interactive/components/session-selector.js.map +0 -1
  890. package/dist/modes/interactive/components/settings-selector.d.ts.map +0 -1
  891. package/dist/modes/interactive/components/settings-selector.js.map +0 -1
  892. package/dist/modes/interactive/components/show-images-selector.d.ts.map +0 -1
  893. package/dist/modes/interactive/components/show-images-selector.js.map +0 -1
  894. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +0 -1
  895. package/dist/modes/interactive/components/skill-invocation-message.js.map +0 -1
  896. package/dist/modes/interactive/components/status-indicator.d.ts.map +0 -1
  897. package/dist/modes/interactive/components/status-indicator.js.map +0 -1
  898. package/dist/modes/interactive/components/theme-selector.d.ts.map +0 -1
  899. package/dist/modes/interactive/components/theme-selector.js.map +0 -1
  900. package/dist/modes/interactive/components/thinking-selector.d.ts.map +0 -1
  901. package/dist/modes/interactive/components/thinking-selector.js.map +0 -1
  902. package/dist/modes/interactive/components/tool-execution.d.ts.map +0 -1
  903. package/dist/modes/interactive/components/tool-execution.js.map +0 -1
  904. package/dist/modes/interactive/components/tree-selector.d.ts.map +0 -1
  905. package/dist/modes/interactive/components/tree-selector.js.map +0 -1
  906. package/dist/modes/interactive/components/trust-selector.d.ts.map +0 -1
  907. package/dist/modes/interactive/components/trust-selector.js.map +0 -1
  908. package/dist/modes/interactive/components/user-message-selector.d.ts.map +0 -1
  909. package/dist/modes/interactive/components/user-message-selector.js.map +0 -1
  910. package/dist/modes/interactive/components/user-message.d.ts.map +0 -1
  911. package/dist/modes/interactive/components/user-message.js.map +0 -1
  912. package/dist/modes/interactive/components/visual-truncate.d.ts.map +0 -1
  913. package/dist/modes/interactive/components/visual-truncate.js.map +0 -1
  914. package/dist/modes/interactive/external-editor.d.ts.map +0 -1
  915. package/dist/modes/interactive/external-editor.js.map +0 -1
  916. package/dist/modes/interactive/interactive-mode.d.ts.map +0 -1
  917. package/dist/modes/interactive/interactive-mode.js.map +0 -1
  918. package/dist/modes/interactive/model-search.d.ts.map +0 -1
  919. package/dist/modes/interactive/model-search.js.map +0 -1
  920. package/dist/modes/interactive/theme/theme-controller.d.ts.map +0 -1
  921. package/dist/modes/interactive/theme/theme-controller.js.map +0 -1
  922. package/dist/modes/interactive/theme/theme.d.ts.map +0 -1
  923. package/dist/modes/interactive/theme/theme.js.map +0 -1
  924. package/dist/modes/print-mode.d.ts.map +0 -1
  925. package/dist/modes/print-mode.js.map +0 -1
  926. package/dist/modes/rpc/jsonl.d.ts.map +0 -1
  927. package/dist/modes/rpc/jsonl.js.map +0 -1
  928. package/dist/modes/rpc/rpc-client.d.ts.map +0 -1
  929. package/dist/modes/rpc/rpc-client.js.map +0 -1
  930. package/dist/modes/rpc/rpc-mode.d.ts.map +0 -1
  931. package/dist/modes/rpc/rpc-mode.js.map +0 -1
  932. package/dist/modes/rpc/rpc-types.d.ts.map +0 -1
  933. package/dist/modes/rpc/rpc-types.js.map +0 -1
  934. package/dist/package-manager-cli.d.ts.map +0 -1
  935. package/dist/package-manager-cli.js.map +0 -1
  936. package/dist/rpc-entry.d.ts.map +0 -1
  937. package/dist/rpc-entry.js.map +0 -1
  938. package/dist/utils/ansi.d.ts.map +0 -1
  939. package/dist/utils/ansi.js.map +0 -1
  940. package/dist/utils/changelog.d.ts.map +0 -1
  941. package/dist/utils/changelog.js.map +0 -1
  942. package/dist/utils/child-process.d.ts.map +0 -1
  943. package/dist/utils/child-process.js.map +0 -1
  944. package/dist/utils/clipboard-image.d.ts.map +0 -1
  945. package/dist/utils/clipboard-image.js.map +0 -1
  946. package/dist/utils/clipboard-native.d.ts.map +0 -1
  947. package/dist/utils/clipboard-native.js.map +0 -1
  948. package/dist/utils/clipboard.d.ts.map +0 -1
  949. package/dist/utils/clipboard.js.map +0 -1
  950. package/dist/utils/deprecation.d.ts.map +0 -1
  951. package/dist/utils/deprecation.js.map +0 -1
  952. package/dist/utils/exif-orientation.d.ts.map +0 -1
  953. package/dist/utils/exif-orientation.js.map +0 -1
  954. package/dist/utils/frontmatter.d.ts.map +0 -1
  955. package/dist/utils/frontmatter.js.map +0 -1
  956. package/dist/utils/fs-watch.d.ts.map +0 -1
  957. package/dist/utils/fs-watch.js.map +0 -1
  958. package/dist/utils/git.d.ts.map +0 -1
  959. package/dist/utils/git.js.map +0 -1
  960. package/dist/utils/html.d.ts.map +0 -1
  961. package/dist/utils/html.js.map +0 -1
  962. package/dist/utils/image-convert.d.ts.map +0 -1
  963. package/dist/utils/image-convert.js.map +0 -1
  964. package/dist/utils/image-process.d.ts.map +0 -1
  965. package/dist/utils/image-process.js.map +0 -1
  966. package/dist/utils/image-resize-core.d.ts.map +0 -1
  967. package/dist/utils/image-resize-core.js.map +0 -1
  968. package/dist/utils/image-resize-worker.d.ts.map +0 -1
  969. package/dist/utils/image-resize-worker.js.map +0 -1
  970. package/dist/utils/image-resize.d.ts.map +0 -1
  971. package/dist/utils/image-resize.js.map +0 -1
  972. package/dist/utils/json.d.ts.map +0 -1
  973. package/dist/utils/json.js.map +0 -1
  974. package/dist/utils/mime.d.ts.map +0 -1
  975. package/dist/utils/mime.js.map +0 -1
  976. package/dist/utils/open-browser.d.ts.map +0 -1
  977. package/dist/utils/open-browser.js.map +0 -1
  978. package/dist/utils/paths.d.ts.map +0 -1
  979. package/dist/utils/paths.js.map +0 -1
  980. package/dist/utils/photon.d.ts.map +0 -1
  981. package/dist/utils/photon.js.map +0 -1
  982. package/dist/utils/pi-user-agent.d.ts.map +0 -1
  983. package/dist/utils/pi-user-agent.js.map +0 -1
  984. package/dist/utils/shell.d.ts.map +0 -1
  985. package/dist/utils/shell.js.map +0 -1
  986. package/dist/utils/sleep.d.ts.map +0 -1
  987. package/dist/utils/sleep.js.map +0 -1
  988. package/dist/utils/syntax-highlight.d.ts.map +0 -1
  989. package/dist/utils/syntax-highlight.js.map +0 -1
  990. package/dist/utils/thinking-tags.d.ts.map +0 -1
  991. package/dist/utils/thinking-tags.js.map +0 -1
  992. package/dist/utils/tools-manager.d.ts.map +0 -1
  993. package/dist/utils/tools-manager.js.map +0 -1
  994. package/dist/utils/version-check.d.ts.map +0 -1
  995. package/dist/utils/version-check.js.map +0 -1
  996. package/dist/utils/windows-self-update.d.ts.map +0 -1
  997. package/dist/utils/windows-self-update.js.map +0 -1
@@ -4,7 +4,8 @@ import * as path from "node:path";
4
4
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
5
5
  import type { ExtensionAPI, ExtensionContext } from "@selesai/code";
6
6
  import { resolveAgentName, type AgentConfig, type AgentScope } from "../../agents/agents.ts";
7
- import { getArtifactsDir, getProjectChainRunsDir } from "../../shared/artifacts.ts";
7
+ import { getArtifactsDir, getChainRunsDir } from "../../shared/artifacts.ts";
8
+ import { writeAtomicJson } from "../../shared/atomic-json.ts";
8
9
  import { ChainClarifyComponent, type ChainClarifyResult } from "./chain-clarify.ts";
9
10
  import { resolveEffectiveThinking, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
10
11
  import { executeChain } from "./chain-execution.ts";
@@ -19,12 +20,12 @@ import {
19
20
  import { resolveExecutionAgentScope } from "../../agents/agent-scope.ts";
20
21
  import { handleManagementAction } from "../../agents/agent-management.ts";
21
22
  import { buildDoctorReport } from "../../extension/doctor.ts";
22
- import { clearPendingForegroundControlNotices } from "../../extension/control-notices.ts";
23
23
  import { runSync } from "./execution.ts";
24
24
  import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
25
25
  import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
26
26
  import { buildModelCandidates, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelCandidate, type ParentModel } from "../shared/model-fallback.ts";
27
27
  import type { ModelScopeConfig } from "../shared/model-scope.ts";
28
+ import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
28
29
  import { recordRun } from "../shared/run-history.ts";
29
30
  import {
30
31
  buildChainInstructions,
@@ -36,6 +37,9 @@ import {
36
37
  suppressProgressForReadOnlyTask,
37
38
  taskDisallowsFileUpdates,
38
39
  type ChainStep,
40
+ type DynamicParallelStep,
41
+ type ParallelStep,
42
+ type ParallelTaskItem,
39
43
  type ResolvedStepBehavior,
40
44
  type SequentialStep,
41
45
  type StepOverrides,
@@ -56,11 +60,11 @@ import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
56
60
  import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig } from "../shared/usage-budget.ts";
57
61
  import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
58
62
  import { isAgentContractV1 } from "../shared/agent-contract.ts";
59
- import { finalizeSingleOutput, formatBoundedPersistenceFallback, formatSavedOutputReference, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
63
+ import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
60
64
  import { cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
61
65
  import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage } from "../../shared/utils.ts";
62
66
  import { DEFAULT_GLOBAL_CONCURRENCY_LIMIT, Semaphore } from "../shared/parallel-utils.ts";
63
- import { formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup } from "../shared/parallel-handoff.ts";
67
+ import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup, writePendingParallelHandoff } from "../shared/parallel-handoff.ts";
64
68
  import { summarizeContextModes, type ContextMode, type ContextSummary } from "../shared/context-mode.ts";
65
69
  import {
66
70
  attachNestedChildrenToResultChildren,
@@ -82,6 +86,13 @@ import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/
82
86
  import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
83
87
  import { inspectSubagentStatus } from "../background/run-status.ts";
84
88
  import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.ts";
89
+ import { handleMissionAction, MISSION_ACTIONS } from "../../missions/actions.ts";
90
+ import { attachMissionToLaunchResult, prepareMissionLaunch, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
91
+ import { resolveAuthorityDecision } from "../../policy/authority.ts";
92
+ import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspectors/herdr/actions.ts";
93
+ import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
94
+ import { runWorkflowScript, WorkflowScriptError, type WorkflowScriptChildResult } from "../../workflows/scripted-workflow.ts";
95
+ import { resolveWorkflowChatProgress, type WorkflowChatProgressProjection } from "../../workflows/chat-progress.ts";
85
96
  import {
86
97
  cleanupWorktrees,
87
98
  createWorktrees,
@@ -93,6 +104,7 @@ import {
93
104
  } from "../shared/worktree.ts";
94
105
  import {
95
106
  type AgentProgress,
107
+ type AsyncJobState,
96
108
  type AsyncStatus,
97
109
  type AcceptanceInput,
98
110
  type AgentContract,
@@ -117,10 +129,9 @@ import {
117
129
  type UsageBudgetConfig,
118
130
  type SubagentRunMode,
119
131
  type SubagentState,
120
- ASYNC_DIR,
132
+ DIRS,
121
133
  DEFAULT_ARTIFACT_CONFIG,
122
134
  DEFAULT_FORK_PREAMBLE,
123
- RESULTS_DIR,
124
135
  SUBAGENT_ACTIONS,
125
136
  SUBAGENT_CONTROL_EVENT,
126
137
  SUBAGENT_CONTROL_INTERCOM_EVENT,
@@ -133,7 +144,39 @@ import {
133
144
  wrapForkTask,
134
145
  } from "../../shared/types.ts";
135
146
 
136
- const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure"]);
147
+ const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure", "mission.create", "mission.update", "mission.attach-run", "mission.close", "inspector.open", "inspector.close", "project.open", "project.close", "worktree.discard", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
148
+
149
+ type UndefinedOmitted<T extends object> = {
150
+ [K in keyof T as undefined extends T[K] ? never : K]: T[K];
151
+ } & {
152
+ [K in keyof T as undefined extends T[K] ? K : never]?: Exclude<T[K], undefined>;
153
+ };
154
+
155
+ // These helpers mutate their argument, so keep calls scoped to fresh object literals or shallow copies.
156
+ function omitUndefinedProperties<T extends object>(value: T): UndefinedOmitted<T> {
157
+ for (const key of Object.keys(value) as Array<keyof T>) {
158
+ if (value[key] === undefined) delete value[key];
159
+ }
160
+ return value as UndefinedOmitted<T>;
161
+ }
162
+
163
+ type WithUndefinedOptionals<T extends object> = {
164
+ [K in keyof T]: {} extends Pick<T, K> ? T[K] | undefined : T[K];
165
+ };
166
+
167
+ type RequiredKeysAllowingUndefined<T extends object> = {
168
+ [K in keyof T]-?: {} extends Pick<T, K> ? never : undefined extends T[K] ? K : never;
169
+ }[keyof T];
170
+
171
+ function compactOptional<T extends object>(
172
+ value: WithUndefinedOptionals<T> & (RequiredKeysAllowingUndefined<T> extends never ? unknown : never),
173
+ ): T {
174
+ for (const key of Object.keys(value) as Array<keyof T>) {
175
+ if (value[key] === undefined) delete value[key];
176
+ }
177
+ return value as T;
178
+ }
179
+
137
180
  interface TaskParam {
138
181
  agent: string;
139
182
  task: string;
@@ -156,13 +199,26 @@ export interface SubagentParamsLike {
156
199
  id?: string;
157
200
  runId?: string;
158
201
  dir?: string;
202
+ handoffPath?: string;
159
203
  index?: number;
160
204
  view?: "fleet" | "transcript";
161
205
  lines?: number;
206
+ chainName?: string;
207
+ config?: unknown;
208
+ name?: string;
209
+ type?: string;
162
210
  agent?: string;
163
211
  task?: string;
164
212
  message?: string;
165
213
  steeringRecovery?: boolean;
214
+ workflowScript?: string;
215
+ chatProgress?: "auto" | "off" | "terminal" | "milestones" | "live-card";
216
+ step?: ChainStep;
217
+ /** Internal workflow ownership metadata; not part of the public schema. */
218
+ workflowParentRunId?: string;
219
+ workflowKey?: string;
220
+ suppressRoutineResultIntercom?: boolean;
221
+ /** Internal durable-run compatibility fields. Public callers must use workflowScript. */
166
222
  chain?: ChainStep[];
167
223
  tasks?: TaskParam[];
168
224
  concurrency?: number;
@@ -189,6 +245,7 @@ export interface SubagentParamsLike {
189
245
  thinking?: string | false;
190
246
  scope?: string;
191
247
  target?: string;
248
+ focus?: boolean;
192
249
  skill?: string | string[] | boolean;
193
250
  output?: string | boolean;
194
251
  outputMode?: "inline" | "file-only";
@@ -199,11 +256,25 @@ export interface SubagentParamsLike {
199
256
  agentContract?: AgentContract;
200
257
  schedule?: string;
201
258
  scheduleName?: string;
259
+ at?: string;
260
+ every?: string;
261
+ on?: string | number;
262
+ timezone?: string;
263
+ overlap?: "skip";
264
+ catchUp?: "none" | "latest";
202
265
  additional?: number;
266
+ missionId?: string;
267
+ mission?: unknown;
268
+ missionUpdate?: unknown;
269
+ missionStatus?: string;
270
+ missionScope?: string;
271
+ runMode?: string;
272
+ runStatus?: string;
273
+ summary?: string;
203
274
  }
204
275
 
205
276
  function rememberParentModel(state: { currentSessionId?: string | null; lastParentModel?: ParentModel }, sessionId: string | null, model: unknown): ParentModel | undefined {
206
- if (state.currentSessionId !== sessionId) state.lastParentModel = undefined;
277
+ if (state.currentSessionId !== sessionId) delete state.lastParentModel;
207
278
  state.currentSessionId = sessionId;
208
279
  const parentModel = normalizeParentModel(model);
209
280
  if (!sessionId) return parentModel;
@@ -262,6 +333,7 @@ interface ExecutionContextData {
262
333
  modelScope?: ModelScopeConfig;
263
334
  parentModel?: ParentModel;
264
335
  parentSessionId: string | null;
336
+ parentPiSessionId?: string;
265
337
  capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
266
338
  }
267
339
 
@@ -272,7 +344,6 @@ function resolveRequestedCwd(runtimeCwd: string, requestedCwd: string | undefine
272
344
  function removeForegroundControlIfIdle(state: SubagentState, runId: string): boolean {
273
345
  const control = state.foregroundControls.get(runId);
274
346
  if (control && (!foregroundSchedulingSettled(control) || (control.activeChildren?.size ?? 0) > 0)) return false;
275
- clearPendingForegroundControlNotices(state, runId);
276
347
  state.foregroundControls.delete(runId);
277
348
  if (state.lastForegroundControlId === runId) state.lastForegroundControlId = null;
278
349
  return true;
@@ -428,7 +499,7 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
428
499
  agent: result.agent,
429
500
  index,
430
501
  ...(result.context ? { context: result.context } : {}),
431
- status: resolveSubagentResultStatus({
502
+ status: resolveSubagentResultStatus(omitUndefinedProperties({
432
503
  exitCode: result.exitCode,
433
504
  interrupted: result.interrupted,
434
505
  detached: result.detached,
@@ -436,7 +507,7 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
436
507
  timedOut: result.timedOut,
437
508
  stopped: result.stopped,
438
509
  turnBudgetExceeded: result.turnBudgetExceeded,
439
- }),
510
+ })),
440
511
  ...foregroundChildActivityFromProgress(result.progress),
441
512
  updatedAt,
442
513
  ...(result.exitCode !== undefined ? { exitCode: result.exitCode } : {}),
@@ -490,7 +561,7 @@ function applyControlEventToRememberedForegroundRun(state: SubagentState, event:
490
561
  };
491
562
  }
492
563
 
493
- function updateRememberedForegroundChild(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; index: number; result: SingleResult; events: IntercomEventBus }): void {
564
+ function updateRememberedForegroundChild(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; index: number; result: SingleResult; events: IntercomEventBus; notify?: boolean }): void {
494
565
  state.foregroundRuns ??= new Map();
495
566
  const updatedAt = Date.now();
496
567
  let run = state.foregroundRuns.get(input.runId);
@@ -499,7 +570,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
499
570
  state.foregroundRuns.set(input.runId, run);
500
571
  }
501
572
  run.updatedAt = updatedAt;
502
- const terminalStatus = resolveSubagentResultStatus({
573
+ const terminalStatus = resolveSubagentResultStatus(omitUndefinedProperties({
503
574
  exitCode: input.result.exitCode,
504
575
  ...(input.result.acceptance?.status === "rejected" ? { success: false } : {}),
505
576
  interrupted: input.result.interrupted,
@@ -508,9 +579,9 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
508
579
  timedOut: input.result.timedOut,
509
580
  stopped: input.result.stopped,
510
581
  turnBudgetExceeded: input.result.turnBudgetExceeded,
511
- });
582
+ }));
512
583
  const child = run.children[input.index] ?? { agent: input.result.agent, index: input.index, status: "detached" as const };
513
- run.children[input.index] = {
584
+ run.children[input.index] = omitUndefinedProperties({
514
585
  ...child,
515
586
  agent: input.result.agent,
516
587
  index: input.index,
@@ -520,12 +591,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
520
591
  updatedAt,
521
592
  ...(input.result.exitCode !== undefined ? { exitCode: input.result.exitCode } : {}),
522
593
  ...(input.result.error ? { error: input.result.error } : {}),
523
- // Reference-first remembered state: when an authoritative saved output path
524
- // exists, do not retain the full final output in memory; status/transcript
525
- // views recover it from `savedOutputPath` via `rememberedForegroundChildOutput`
526
- // (runs/background/run-status.ts) and the TUI via `resultOutputForUi`
527
- // (tui/render.ts).
528
- ...(input.result.finalOutput && !input.result.savedOutputPath ? { finalOutput: input.result.finalOutput } : {}),
594
+ ...(input.result.finalOutput ? { finalOutput: input.result.finalOutput } : {}),
529
595
  outputState: input.result.outputState,
530
596
  outputMode: input.result.outputMode,
531
597
  savedOutputPath: input.result.savedOutputPath,
@@ -543,17 +609,16 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
543
609
  ...(input.result.runtimeAcknowledgedExtensions ? { runtimeAcknowledgedExtensions: input.result.runtimeAcknowledgedExtensions } : {}),
544
610
  ...(input.result.capabilityCeiling ? { capabilityCeiling: input.result.capabilityCeiling } : {}),
545
611
  ...(input.result.capabilityAudit ? { capabilityAudit: input.result.capabilityAudit } : {}),
546
- };
612
+ });
547
613
  trimRememberedForegroundRuns(state);
548
- const reference = input.result.outputReference?.message;
549
- const output = reference ?? getSingleResultOutput(input.result).trim();
614
+ const output = getSingleResultOutput(input.result).trim();
550
615
  const success = terminalStatus === "completed";
551
616
  const summary = !success && input.result.error
552
- ? `${input.result.error}${output ? `\n\n${output}` : ""}`
617
+ ? `${input.result.error}${output ? `\n\nOutput:\n${output}` : ""}`
553
618
  : output || input.result.error || "Detached child exited without final output.";
554
619
  // A detached callback may outlive its extension runtime. Stale sessions are
555
620
  // intentionally dropped rather than routed through a replacement runtime.
556
- if (!input.sessionId || input.sessionId !== state.currentSessionId) return;
621
+ if (input.notify === false || !input.sessionId || input.sessionId !== state.currentSessionId) return;
557
622
  input.events.emit(SUBAGENT_FOREGROUND_COMPLETE_EVENT, {
558
623
  id: `${input.runId}:${input.index}`,
559
624
  runId: input.runId,
@@ -577,7 +642,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
577
642
  });
578
643
  }
579
644
 
580
- function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: "single" | "parallel" | "chain"; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string; model?: string; thinking?: string; launchContractDigest?: string; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } | undefined {
645
+ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: SubagentRunMode; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string; model?: string; thinking?: string; launchContractDigest?: string; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } | undefined {
581
646
  const requested = (params.id ?? params.runId)?.trim();
582
647
  if (!requested || !state.foregroundRuns?.size || !state.currentSessionId) return undefined;
583
648
  const sessionRuns = [...state.foregroundRuns.values()].filter((run) => run.sessionId === state.currentSessionId);
@@ -622,6 +687,9 @@ type NestedResumeSourceTarget = {
622
687
  index: number;
623
688
  cwd?: string;
624
689
  sessionFile: string;
690
+ model?: string;
691
+ thinking?: AgentConfig["thinking"];
692
+ launchContractDigest?: string;
625
693
  capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
626
694
  };
627
695
  type ResumeSourceTarget = AsyncResumeSourceTarget | ForegroundResumeSourceTarget | NestedResumeSourceTarget;
@@ -663,10 +731,10 @@ function resolveResumeTarget(params: SubagentParamsLike, state: SubagentState, o
663
731
  try {
664
732
  asyncTarget = {
665
733
  source: "async",
666
- ...resolveAsyncResumeTarget(params, {}, {
734
+ ...resolveAsyncResumeTarget(params, {}, compactOptional<NonNullable<Parameters<typeof resolveAsyncResumeTarget>[2]>>({
667
735
  requireSessionFile: options.asyncRequireSessionFile,
668
736
  sessionId: state.currentSessionId ?? undefined,
669
- }),
737
+ })),
670
738
  };
671
739
  } catch (error) {
672
740
  asyncError = error;
@@ -758,7 +826,7 @@ function interruptAsyncRun(
758
826
  ): AgentToolResult<Details> | null {
759
827
  const target = getAsyncInterruptTarget(state, runId, location);
760
828
  if (!target) return null;
761
- const status = reconcileAsyncRun(target.asyncDir, { kill }).status;
829
+ const status = reconcileAsyncRun(target.asyncDir, omitUndefinedProperties({ kill })).status;
762
830
  if (!status || status.state !== "running" || typeof status.pid !== "number") {
763
831
  return {
764
832
  content: [{ type: "text", text: `No running async run with an interrupt-capable pid was found for '${runId ?? "current"}'.` }],
@@ -766,11 +834,19 @@ function interruptAsyncRun(
766
834
  details: { mode: "management", results: [] },
767
835
  };
768
836
  }
837
+ const activeSteps = status.steps?.filter((step) => step.status === "running") ?? [];
838
+ if (activeSteps.length > 0 && activeSteps.every((step) => step.runner?.type === "external-cli")) {
839
+ return {
840
+ content: [{ type: "text", text: `Interrupt is unsupported for one-shot external CLI async run ${target.asyncId}; use stop instead.` }],
841
+ isError: true,
842
+ details: { mode: "management", results: [] },
843
+ };
844
+ }
769
845
  try {
770
- deliverInterruptRequest({ asyncDir: target.asyncDir, pid: status.pid, kill, source: "interrupt-action" });
846
+ deliverInterruptRequest(omitUndefinedProperties({ asyncDir: target.asyncDir, pid: status.pid, kill, source: "interrupt-action" }));
771
847
  const tracked = state.asyncJobs.get(target.asyncId);
772
848
  if (tracked) {
773
- tracked.activityState = undefined;
849
+ delete tracked.activityState;
774
850
  tracked.updatedAt = Date.now();
775
851
  }
776
852
  return {
@@ -812,14 +888,15 @@ function appendStepToAsyncChain(input: {
812
888
  details: { mode: "management", results: [] },
813
889
  };
814
890
  }
815
- if (!input.params.chain || input.params.chain.length !== 1) {
891
+ if (!input.params.step) {
816
892
  return {
817
- content: [{ type: "text", text: "action='append-step' requires chain with exactly one step." }],
893
+ content: [{ type: "text", text: "action='append-step' requires step." }],
818
894
  isError: true,
819
895
  details: { mode: "management", results: [] },
820
896
  };
821
897
  }
822
- const acceptanceErrors = validateExecutionAcceptance(input.params);
898
+ const chain = [input.params.step];
899
+ const acceptanceErrors = validateExecutionAcceptance({ ...input.params, chain } as Parameters<typeof validateExecutionAcceptance>[0]);
823
900
  if (acceptanceErrors.length > 0) {
824
901
  return {
825
902
  content: [{ type: "text", text: `Cannot append step: ${acceptanceErrors.join(" ")}` }],
@@ -830,7 +907,7 @@ function appendStepToAsyncChain(input: {
830
907
 
831
908
  let resolved: ResolvedSubagentRunId | undefined;
832
909
  try {
833
- resolved = resolveSubagentRunId(targetRunId, { state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) });
910
+ resolved = resolveSubagentRunId(targetRunId, omitUndefinedProperties({ state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) }));
834
911
  } catch (error) {
835
912
  const message = error instanceof Error ? error.message : String(error);
836
913
  return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
@@ -888,7 +965,7 @@ function appendStepToAsyncChain(input: {
888
965
  ...pendingAppendRequests.flatMap((request) => runnerStepOutputNames(request.steps)),
889
966
  ]);
890
967
  try {
891
- validateChainOutputBindingsWithContext(input.params.chain, { maxItems: input.deps.config.chain?.dynamicFanout?.maxItems }, {
968
+ validateChainOutputBindingsWithContext(chain, omitUndefinedProperties({ maxItems: input.deps.config.chain?.dynamicFanout?.maxItems }), {
892
969
  priorOutputNames: reservedOutputNames,
893
970
  startStepIndex: status.chainStepCount ?? status.steps?.length ?? 0,
894
971
  });
@@ -908,7 +985,7 @@ function appendStepToAsyncChain(input: {
908
985
  const chainSkillInput = normalizeSkillInput(input.params.skill);
909
986
  const chainSkills = chainSkillInput === false ? [] : (chainSkillInput ?? []);
910
987
  const parentModel = input.parentModel;
911
- const asyncCtx = {
988
+ const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
912
989
  pi: input.deps.pi,
913
990
  cwd: input.ctx.cwd,
914
991
  currentSessionId: resolveCurrentSessionId(input.ctx.sessionManager),
@@ -917,9 +994,10 @@ function appendStepToAsyncChain(input: {
917
994
  currentModel: parentModel,
918
995
  modelScope: discoveredForAppend.modelScope,
919
996
  interactive: input.ctx.hasUI,
920
- };
921
- const built = buildAsyncRunnerSteps(resolved.id, {
922
- chain: wrapChainTasksForFork(input.params.chain, contextPolicy),
997
+ permissions: input.deps.config.permissions,
998
+ });
999
+ const built = buildAsyncRunnerSteps(resolved.id, compactOptional<Parameters<typeof buildAsyncRunnerSteps>[1]>({
1000
+ chain: wrapChainTasksForFork(chain, contextPolicy),
923
1001
  task: input.params.task,
924
1002
  resultMode: "chain",
925
1003
  agents,
@@ -934,7 +1012,7 @@ function appendStepToAsyncChain(input: {
934
1012
  asyncDir: resolved.location.asyncDir,
935
1013
  validateOutputBindings: false,
936
1014
  capabilityCeiling: intersectSubagentCapabilityCeilings(status.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(asyncCtx.currentSessionId)),
937
- });
1015
+ }));
938
1016
  if ("error" in built) {
939
1017
  return {
940
1018
  content: [{ type: "text", text: built.error }],
@@ -1029,7 +1107,7 @@ function resolveNestedResumeTarget(match: ResolvedSubagentRunId & { kind: "neste
1029
1107
  if (!agent) throw new Error(`Could not determine child agent for nested run '${run.id}'.`);
1030
1108
  const state = run.state === "complete" || run.state === "failed" || run.state === "paused" ? run.state : "failed";
1031
1109
  const asyncDir = resolveNestedAsyncDir(match.match.rootRunId, run);
1032
- return {
1110
+ return compactOptional<NestedResumeSourceTarget>({
1033
1111
  kind: "revive",
1034
1112
  source: "nested",
1035
1113
  runId: run.id,
@@ -1039,7 +1117,7 @@ function resolveNestedResumeTarget(match: ResolvedSubagentRunId & { kind: "neste
1039
1117
  cwd: asyncDir ? path.dirname(asyncDir) : undefined,
1040
1118
  sessionFile: validateNestedSessionFile(run, trustedSessionRoots),
1041
1119
  ...(run.capabilityCeiling ? { capabilityCeiling: run.capabilityCeiling } : {}),
1042
- };
1120
+ });
1043
1121
  }
1044
1122
 
1045
1123
  async function waitForNestedControlResult(target: ResolvedSubagentRunId & { kind: "nested" }, requestId: string, ignoredFiles: ReadonlySet<string>, timeoutMs = 1_000) {
@@ -1070,7 +1148,7 @@ function directNestedAsyncInterrupt(target: ResolvedSubagentRunId & { kind: "nes
1070
1148
  const run = target.match.run;
1071
1149
  const asyncDir = resolveNestedAsyncDir(target.match.rootRunId, run);
1072
1150
  if (!asyncDir) return undefined;
1073
- const status = reconcileAsyncRun(asyncDir, { resultsDir: path.join(RESULTS_DIR, "nested", target.match.rootRunId) }).status;
1151
+ const status = reconcileAsyncRun(asyncDir, { resultsDir: path.join(DIRS.results, "nested", target.match.rootRunId) }).status;
1074
1152
  const pid = typeof status?.pid === "number" && status.pid > 0 ? status.pid : run.pid;
1075
1153
  if (!status || status.state !== "running" || typeof pid !== "number" || pid <= 0) return undefined;
1076
1154
  try {
@@ -1086,7 +1164,7 @@ async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & {
1086
1164
  const run = input.target.match.run;
1087
1165
  const asyncDir = resolveNestedAsyncDir(input.target.match.rootRunId, run);
1088
1166
  if (!asyncDir) return undefined;
1089
- const status = reconcileAsyncRun(asyncDir, { resultsDir: path.join(RESULTS_DIR, "nested", input.target.match.rootRunId) }).status;
1167
+ const status = reconcileAsyncRun(asyncDir, { resultsDir: path.join(DIRS.results, "nested", input.target.match.rootRunId) }).status;
1090
1168
  if (!status || (status.state !== "running" && status.state !== "queued")) return undefined;
1091
1169
  const steps = status.steps ?? [];
1092
1170
  if (input.index !== undefined) {
@@ -1116,7 +1194,7 @@ async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & {
1116
1194
  const scheduled = { requestId, state: "scheduled" as const, sourceRunId: run.id, targets };
1117
1195
  return { content: [{ type: "text", text: `Steering scheduled for nested async run ${run.id} (request ${requestId}).` }], details: { mode: "management", results: [], steering: scheduled } };
1118
1196
  }
1119
- const waited = await waitForSteeringAction({ asyncDir, sourceRunId: run.id, requestId, timeoutMs: 3_000, signal: input.signal });
1197
+ const waited = await waitForSteeringAction(omitUndefinedProperties({ asyncDir, sourceRunId: run.id, requestId, timeoutMs: 3_000, signal: input.signal }));
1120
1198
  const result = waited ?? { requestId, state: "pending" as const, sourceRunId: run.id, targets };
1121
1199
  const stateText = result.state === "delivered" ? "delivered" : result.state === "failed" ? "failed" : result.state === "partial" ? "partial" : "pending";
1122
1200
  return { content: [{ type: "text", text: `Steering ${stateText} for nested async run ${run.id} (request ${requestId}).` }], ...(result.state === "failed" || result.state === "partial" ? { isError: true } : {}), details: { mode: "management", results: [], steering: result } };
@@ -1128,7 +1206,7 @@ async function interruptNestedRun(target: ResolvedSubagentRunId & { kind: "neste
1128
1206
  if (run.state === "failed") return { content: [{ type: "text", text: `Nested run ${run.id} has failed and cannot be interrupted.` }], isError: true, details: { mode: "management", results: [] } };
1129
1207
  if (run.state === "paused") return { content: [{ type: "text", text: `Nested run ${run.id} is already paused.` }], isError: true, details: { mode: "management", results: [] } };
1130
1208
  const result = await sendNestedControlRequest(target, "interrupt");
1131
- if (result) return { content: [{ type: "text", text: result.message }], isError: result.ok ? undefined : true, details: { mode: "management", results: [] } };
1209
+ if (result) return { content: [{ type: "text", text: result.message }], ...(result.ok ? {} : { isError: true }), details: { mode: "management", results: [] } };
1132
1210
  const direct = directNestedAsyncInterrupt(target);
1133
1211
  if (direct) return direct;
1134
1212
  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: [] } };
@@ -1137,7 +1215,7 @@ async function interruptNestedRun(target: ResolvedSubagentRunId & { kind: "neste
1137
1215
  async function resumeLiveNestedRun(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string }): Promise<AgentToolResult<Details>> {
1138
1216
  const run = input.target.match.run;
1139
1217
  const result = await sendNestedControlRequest(input.target, "resume", input.message);
1140
- if (result) return { content: [{ type: "text", text: result.message }], isError: result.ok ? undefined : true, details: { mode: "management", results: [] } };
1218
+ if (result) return { content: [{ type: "text", text: result.message }], ...(result.ok ? {} : { isError: true }), details: { mode: "management", results: [] } };
1141
1219
  return { content: [{ type: "text", text: `Nested run ${run.id} appears live but its owner route is not reachable. Wait for completion, then retry action='resume'.` }], isError: true, details: { mode: "management", results: [] } };
1142
1220
  }
1143
1221
 
@@ -1149,6 +1227,15 @@ async function steerNestedRun(input: { target: ResolvedSubagentRunId & { kind: "
1149
1227
  return { content: [{ type: "text", text: `Nested run ${run.id} is not a live async Pi child session with a steering inbox. action='steer' cannot target foreground nested runs.` }], isError: true, details: { mode: "management", results: [] } };
1150
1228
  }
1151
1229
 
1230
+ function externalRunnerControlError(asyncDir: string, action: "steer" | "resume"): AgentToolResult<Details> | undefined {
1231
+ const status = readStatus(asyncDir);
1232
+ if (!status?.steps?.length || !status.steps.every((step) => step.runner?.type === "external-cli")) return undefined;
1233
+ const message = action === "steer"
1234
+ ? "One-shot external CLI runners do not accept live steer messages."
1235
+ : "One-shot external CLI runners do not persist sessions and cannot be resumed.";
1236
+ return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
1237
+ }
1238
+
1152
1239
  async function resumeAsyncRun(input: {
1153
1240
  params: SubagentParamsLike;
1154
1241
  requestCwd: string;
@@ -1173,7 +1260,7 @@ async function resumeAsyncRun(input: {
1173
1260
  details: { mode: "management", results: [] },
1174
1261
  };
1175
1262
  }
1176
- const acceptanceErrors = validateExecutionAcceptance(input.params);
1263
+ const acceptanceErrors = validateExecutionAcceptance(input.params as Parameters<typeof validateExecutionAcceptance>[0]);
1177
1264
  if (acceptanceErrors.length > 0) {
1178
1265
  return {
1179
1266
  content: [{ type: "text", text: `Cannot resume: ${acceptanceErrors.join(" ")}` }],
@@ -1189,7 +1276,7 @@ async function resumeAsyncRun(input: {
1189
1276
  const requestedId = input.params.id ?? input.params.runId;
1190
1277
  let resolved: ResolvedSubagentRunId | undefined;
1191
1278
  try {
1192
- resolved = requestedId ? resolveSubagentRunId(requestedId, { state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) }) : undefined;
1279
+ resolved = requestedId ? resolveSubagentRunId(requestedId, omitUndefinedProperties({ state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) })) : undefined;
1193
1280
  } catch (error) {
1194
1281
  const message = error instanceof Error ? error.message : "";
1195
1282
  const asyncMatches = message.match(/async:/g)?.length ?? 0;
@@ -1212,6 +1299,10 @@ async function resumeAsyncRun(input: {
1212
1299
  ];
1213
1300
  target = resolveNestedResumeTarget(resolved, trustedSessionRoots);
1214
1301
  } else {
1302
+ if (resolved?.kind === "async" && resolved.location.asyncDir) {
1303
+ const unsupported = externalRunnerControlError(resolved.location.asyncDir, "resume");
1304
+ if (unsupported) return unsupported;
1305
+ }
1215
1306
  target = resolveResumeTarget(input.params, input.deps.state, { asyncRequireSessionFile: !attachChain });
1216
1307
  }
1217
1308
  } catch (error) {
@@ -1293,7 +1384,7 @@ async function resumeAsyncRun(input: {
1293
1384
  };
1294
1385
  }
1295
1386
  const runId = randomUUID().slice(0, 8);
1296
- const artifactConfig: ArtifactConfig = { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts === true, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir };
1387
+ const artifactConfig: ArtifactConfig = omitUndefinedProperties({ ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir });
1297
1388
  const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
1298
1389
  const contextPolicy = resolveExplicitContextPolicy(input.params);
1299
1390
  const workflowTask = (input.params.task ?? followUp) || undefined;
@@ -1301,20 +1392,20 @@ async function resumeAsyncRun(input: {
1301
1392
  const chain = wrapChainTasksForFork(attachChain, contextPolicy);
1302
1393
  const normalized = normalizeSkillInput(input.params.skill);
1303
1394
  const parentModel = input.parentModel;
1304
- const result = executeAsyncChain(runId, {
1395
+ const result = executeAsyncChain(runId, compactOptional<Parameters<typeof executeAsyncChain>[1]>({
1305
1396
  chain,
1306
1397
  task: workflowTask,
1307
1398
  goal,
1308
1399
  attachRoot: {
1309
1400
  runId: target.runId,
1310
- asyncDir: target.asyncDir ?? path.join(ASYNC_DIR, target.runId),
1311
- resultPath: resolveAsyncRootResultPath(RESULTS_DIR, target.runId),
1401
+ asyncDir: target.asyncDir ?? path.join(DIRS.async, target.runId),
1402
+ resultPath: resolveAsyncRootResultPath(DIRS.results, target.runId),
1312
1403
  index: target.index,
1313
1404
  agent: target.agent,
1314
1405
  label: `Attached ${target.runId}`,
1315
1406
  },
1316
1407
  agents,
1317
- ctx: {
1408
+ ctx: compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
1318
1409
  pi: input.deps.pi,
1319
1410
  cwd: input.requestCwd,
1320
1411
  currentSessionId: input.deps.state.currentSessionId,
@@ -1323,7 +1414,8 @@ async function resumeAsyncRun(input: {
1323
1414
  currentModel: parentModel,
1324
1415
  modelScope,
1325
1416
  interactive: input.ctx.hasUI,
1326
- },
1417
+ permissions: input.deps.config.permissions,
1418
+ }),
1327
1419
  availableModels,
1328
1420
  cwd: effectiveCwd,
1329
1421
  maxOutput: input.params.maxOutput,
@@ -1344,7 +1436,7 @@ async function resumeAsyncRun(input: {
1344
1436
  childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
1345
1437
  globalConcurrencyLimit: input.deps.config.globalConcurrencyLimit,
1346
1438
  capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
1347
- });
1439
+ }));
1348
1440
  if (result.isError) return result;
1349
1441
  const attachedId = result.details.asyncId ?? runId;
1350
1442
  const lines = [
@@ -1357,18 +1449,22 @@ async function resumeAsyncRun(input: {
1357
1449
  return { content: [{ type: "text", text: formatAsyncStartedMessage(lines.join("\n"), input.ctx.hasUI) }], details: result.details };
1358
1450
  }
1359
1451
 
1452
+ const revivalSessionFile = target.sessionFile;
1453
+ if (!revivalSessionFile) {
1454
+ return { content: [{ type: "text", text: `Async child '${target.runId}' has no persisted session file to resume.` }], isError: true, details: { mode: "management", results: [] } };
1455
+ }
1360
1456
  const runId = randomUUID().slice(0, 8);
1361
1457
  const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(agentConfig, recoveryDescriptor) : agentConfig;
1362
- const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts === true, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir };
1458
+ const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? omitUndefinedProperties({ ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir });
1363
1459
  const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
1364
1460
  const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
1365
1461
  const parentModel = input.parentModel;
1366
- const result = executeAsyncSingle(runId, {
1462
+ const result = executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
1367
1463
  agent: target.agent,
1368
- task: buildRevivedAsyncTask(target, followUp),
1464
+ task: buildRevivedAsyncTask(target as Parameters<typeof buildRevivedAsyncTask>[0], followUp),
1369
1465
  goal: followUp,
1370
1466
  agentConfig: recoveryAgentConfig,
1371
- ctx: {
1467
+ ctx: compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
1372
1468
  pi: input.deps.pi,
1373
1469
  cwd: input.requestCwd,
1374
1470
  currentSessionId: input.deps.state.currentSessionId,
@@ -1377,17 +1473,18 @@ async function resumeAsyncRun(input: {
1377
1473
  currentModel: parentModel,
1378
1474
  modelScope,
1379
1475
  interactive: input.ctx.hasUI,
1380
- },
1476
+ permissions: input.deps.config.permissions,
1477
+ }),
1381
1478
  cwd: effectiveCwd,
1382
1479
  maxOutput: input.params.maxOutput ?? recoveryDescriptor?.maxOutput,
1383
1480
  artifactsDir,
1384
1481
  artifactConfig,
1385
1482
  shareEnabled: recoveryDescriptor?.share ?? input.params.share === true,
1386
- sessionRoot: input.deps.getSubagentSessionRoot(parentSessionFile),
1483
+ sessionRoot: input.deps.getSubagentSessionRoot(parentSessionFile ?? revivalSessionFile),
1387
1484
  ...(recoveryDescriptor?.sessionDir ? { sessionDir: recoveryDescriptor.sessionDir } : {}),
1388
- sessionFile: target.sessionFile,
1485
+ sessionFile: revivalSessionFile,
1389
1486
  revivalLease: {
1390
- sessionFile: target.sessionFile,
1487
+ sessionFile: revivalSessionFile,
1391
1488
  runId,
1392
1489
  sourceRunId: target.runId,
1393
1490
  ...(input.deps.state.currentSessionId ? { parentSessionId: input.deps.state.currentSessionId } : {}),
@@ -1415,7 +1512,7 @@ async function resumeAsyncRun(input: {
1415
1512
  ...(input.params.turnBudget !== undefined ? { turnBudget: input.params.turnBudget } : {}),
1416
1513
  ...(input.params.toolBudget !== undefined ? { toolBudget: input.params.toolBudget } : {}),
1417
1514
  capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, recoveryDescriptor?.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
1418
- });
1515
+ }));
1419
1516
  if (result.isError) return result;
1420
1517
 
1421
1518
  const revivedId = result.details.asyncId ?? runId;
@@ -1440,82 +1537,14 @@ async function resumeAsyncRun(input: {
1440
1537
  }
1441
1538
 
1442
1539
  function resultSummaryForIntercom(result: SingleResult): string {
1443
- const reference = result.outputReference?.message;
1444
- if (reference) {
1445
- return result.exitCode !== 0 && result.error ? `${result.error}\n\n${reference}` : reference;
1540
+ const output = getSingleResultOutput(result);
1541
+ if (result.exitCode !== 0 && result.error) {
1542
+ return output ? `${result.error}\n\nOutput:\n${output}` : result.error;
1446
1543
  }
1447
- if (result.savedOutputPath) {
1448
- try {
1449
- const ref = formatSavedOutputReference(result.savedOutputPath, fs.readFileSync(result.savedOutputPath, "utf-8"));
1450
- return result.exitCode !== 0 && result.error ? `${result.error}\n\n${ref.message}` : ref.message;
1451
- } catch (error) {
1452
- const fallback = formatBoundedPersistenceFallback({
1453
- error: `Failed to read saved output: ${error instanceof Error ? error.message : String(error)}`,
1454
- outputPath: result.savedOutputPath,
1455
- fullOutput: getSingleResultOutput(result),
1456
- exitCode: result.exitCode,
1457
- processError: result.error,
1458
- });
1459
- return result.exitCode !== 0 && result.error ? `${result.error}\n\n${fallback}` : fallback;
1460
- }
1461
- }
1462
- if (result.outputSaveError) {
1463
- return formatBoundedPersistenceFallback({
1464
- error: result.outputSaveError,
1465
- outputPath: result.savedOutputPath,
1466
- fullOutput: getSingleResultOutput(result),
1467
- exitCode: result.exitCode,
1468
- processError: result.error,
1469
- });
1470
- }
1471
- // Legacy / `output: false` child without a durable saved path: bounded fallback
1472
- // only, never the raw full output.
1473
- return formatBoundedPersistenceFallback({
1474
- error: "No saved output path is available for this child.",
1475
- fullOutput: getSingleResultOutput(result),
1476
- exitCode: result.exitCode,
1477
- processError: result.error,
1478
- });
1479
- }
1480
-
1481
- /**
1482
- * Compact terminal per-child line for bridge-off foreground parallel content:
1483
- * status plus either the saved-output reference (default/file-only), the legacy
1484
- * inline full output when explicitly requested, or the bounded fallback.
1485
- */
1486
- function formatParallelChildResult(result: SingleResult, index: number): string {
1487
- const status = resolveSubagentResultStatus({
1488
- exitCode: result.exitCode,
1489
- interrupted: result.interrupted,
1490
- detached: result.detached,
1491
- processSignal: result.processSignal,
1492
- timedOut: result.timedOut,
1493
- stopped: result.stopped,
1494
- turnBudgetExceeded: result.turnBudgetExceeded,
1495
- });
1496
- const header = `=== Task ${index + 1}: ${result.agent} [${status}] ===`;
1497
- if (result.outputMode === "inline") {
1498
- const output = getSingleResultOutput(result).trim();
1499
- return `${header}\n${output || "(no output)"}`;
1500
- }
1501
- const reference = result.outputReference?.message;
1502
- if (reference) {
1503
- const body = result.exitCode !== 0 && result.error ? `${result.error}\n${reference}` : reference;
1504
- return `${header}\n${body}`;
1505
- }
1506
- return `${header}\n${formatBoundedPersistenceFallback({
1507
- error: result.outputSaveError ?? "No saved output path is available for this child.",
1508
- outputPath: result.savedOutputPath,
1509
- fullOutput: getSingleResultOutput(result),
1510
- exitCode: result.exitCode,
1511
- processError: result.error,
1512
- })}`;
1544
+ return output || result.error || "(no output)";
1513
1545
  }
1514
1546
 
1515
1547
  function formatFailedSingleRunOutput(result: SingleResult, displayOutput: string): string {
1516
- // Reference-first and persistence-failure fallbacks already carry error/status
1517
- // and must not be re-wrapped as raw "Output:" text.
1518
- if (result.savedOutputPath || result.outputSaveError) return displayOutput;
1519
1548
  const error = result.error || "Failed";
1520
1549
  const output = displayOutput.trim();
1521
1550
  const lines = [error];
@@ -1540,6 +1569,25 @@ function createForegroundControlNotifier(data: Pick<ExecutionContextData, "contr
1540
1569
  };
1541
1570
  }
1542
1571
 
1572
+ export function foregroundResultIntercomStatus(result: SingleResult): ReturnType<typeof resolveSubagentResultStatus> {
1573
+ return resolveSubagentResultStatus(omitUndefinedProperties({
1574
+ exitCode: result.exitCode,
1575
+ ...(result.acceptance?.status === "rejected" ? { success: false } : {}),
1576
+ interrupted: result.interrupted,
1577
+ detached: result.detached,
1578
+ processSignal: result.processSignal,
1579
+ timedOut: result.timedOut,
1580
+ stopped: result.stopped,
1581
+ turnBudgetExceeded: result.turnBudgetExceeded,
1582
+ }));
1583
+ }
1584
+
1585
+ export function shouldSuppressRoutineResultIntercom(input: { suppressRoutineResultIntercom?: boolean; results: SingleResult[] }): boolean {
1586
+ return input.suppressRoutineResultIntercom === true
1587
+ && input.results.length > 0
1588
+ && input.results.every((result) => foregroundResultIntercomStatus(result) === "completed");
1589
+ }
1590
+
1543
1591
  async function emitForegroundResultIntercom(input: {
1544
1592
  pi: ExtensionAPI;
1545
1593
  intercomBridge: IntercomBridgeState;
@@ -1551,24 +1599,16 @@ async function emitForegroundResultIntercom(input: {
1551
1599
  parallelHandoff?: Details["parallelHandoff"];
1552
1600
  }): Promise<ReturnType<typeof buildSubagentResultIntercomPayload> | null> {
1553
1601
  if (!input.intercomBridge.active || !input.intercomBridge.resultDelivery || !input.intercomBridge.orchestratorTarget) return null;
1554
- const children = input.results.flatMap((result, index) => result.detached ? [] : [{
1602
+ const children = input.results.flatMap((result, index) => result.detached ? [] : [omitUndefinedProperties({
1555
1603
  agent: result.agent,
1556
- status: resolveSubagentResultStatus({
1557
- exitCode: result.exitCode,
1558
- interrupted: result.interrupted,
1559
- detached: result.detached,
1560
- processSignal: result.processSignal,
1561
- timedOut: result.timedOut,
1562
- stopped: result.stopped,
1563
- turnBudgetExceeded: result.turnBudgetExceeded,
1564
- }),
1604
+ status: foregroundResultIntercomStatus(result),
1565
1605
  outputState: result.outputState ?? "unknown",
1566
1606
  summary: resultSummaryForIntercom(result),
1567
1607
  index,
1568
1608
  artifactPath: result.artifactPaths?.outputPath,
1569
1609
  sessionPath: result.sessionFile,
1570
1610
  intercomTarget: resolveSubagentIntercomTarget(input.runId, result.agent, index),
1571
- }]);
1611
+ })]);
1572
1612
  if (children.length === 0) return null;
1573
1613
  const payload = buildSubagentResultIntercomPayload({
1574
1614
  to: input.intercomBridge.orchestratorTarget,
@@ -1624,7 +1664,7 @@ function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentCo
1624
1664
  if (params.agent) {
1625
1665
  const result = resolve(params.agent);
1626
1666
  if (result.error) return { error: result.error };
1627
- params = { ...params, agent: result.name };
1667
+ params = omitUndefinedProperties({ ...params, agent: result.name });
1628
1668
  }
1629
1669
  if (params.tasks) {
1630
1670
  const tasks: TaskParam[] = [];
@@ -1641,7 +1681,7 @@ function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentCo
1641
1681
  for (let index = 0; index < params.chain.length; index++) {
1642
1682
  const step = params.chain[index]!;
1643
1683
  if (isParallelStep(step)) {
1644
- const parallel: typeof step.parallel = [];
1684
+ const parallel: ParallelTaskItem[] = [];
1645
1685
  for (let taskIndex = 0; taskIndex < step.parallel.length; taskIndex++) {
1646
1686
  const task = step.parallel[taskIndex]!;
1647
1687
  const result = resolve(task.agent, `step ${index + 1}, task ${taskIndex + 1}`);
@@ -1691,7 +1731,7 @@ function validateExecutionInput(
1691
1731
  };
1692
1732
  }
1693
1733
 
1694
- const acceptanceErrors = validateExecutionAcceptance(params);
1734
+ const acceptanceErrors = validateExecutionAcceptance(params as Parameters<typeof validateExecutionAcceptance>[0]);
1695
1735
  if (acceptanceErrors.length > 0) {
1696
1736
  return {
1697
1737
  content: [{ type: "text", text: acceptanceErrors.join(" ") }],
@@ -1780,7 +1820,7 @@ function validateExecutionInput(
1780
1820
  function validateExecutionChainBindings(params: SubagentParamsLike, dynamicFanoutMaxItems?: number): AgentToolResult<Details> | null {
1781
1821
  if ((params.chain?.length ?? 0) === 0) return null;
1782
1822
  try {
1783
- validateChainOutputBindingsWithContext(params.chain as ChainStep[], { maxItems: dynamicFanoutMaxItems });
1823
+ validateChainOutputBindingsWithContext(params.chain as ChainStep[], dynamicFanoutMaxItems === undefined ? {} : { maxItems: dynamicFanoutMaxItems });
1784
1824
  } catch (error) {
1785
1825
  if (error instanceof ChainOutputValidationError) {
1786
1826
  return {
@@ -1795,6 +1835,7 @@ function validateExecutionChainBindings(params: SubagentParamsLike, dynamicFanou
1795
1835
  }
1796
1836
 
1797
1837
  function getRequestedModeLabel(params: SubagentParamsLike): Details["mode"] {
1838
+ if (params.workflowScript !== undefined) return "workflow";
1798
1839
  if ((params.chain?.length ?? 0) > 0) return "chain";
1799
1840
  if ((params.tasks?.length ?? 0) > 0) return "parallel";
1800
1841
  if (params.agent) return "single";
@@ -1818,12 +1859,12 @@ function resolveAgentDefaultContextPolicy(params: SubagentParamsLike, agents: Ag
1818
1859
  const requestedAgentNames = collectRequestedAgentNames(params);
1819
1860
  const contextSummary = summarizeContextModes(requestedAgentNames.map((name) => contextForAgent(name)));
1820
1861
  const usesFork = contextSummary === "fork" || contextSummary === "mixed";
1821
- return {
1862
+ return omitUndefinedProperties({
1822
1863
  params,
1823
1864
  contextForAgent,
1824
1865
  contextSummary,
1825
1866
  usesFork,
1826
- };
1867
+ });
1827
1868
  }
1828
1869
 
1829
1870
  function resolveExplicitContextPolicy(params: SubagentParamsLike): AgentDefaultContextPolicy {
@@ -1899,7 +1940,8 @@ function resolveForegroundTimeout(params: SubagentParamsLike, defaultTimeoutMs?:
1899
1940
  if (rawTimeout !== undefined && rawMaxRuntime !== undefined && rawTimeout !== rawMaxRuntime) {
1900
1941
  return { error: "timeoutMs and maxRuntimeMs are aliases; provide only one value or use the same value for both." };
1901
1942
  }
1902
- return { timeoutMs: rawTimeout ?? rawMaxRuntime };
1943
+ const timeoutMs = rawTimeout ?? rawMaxRuntime;
1944
+ return timeoutMs === undefined ? {} : { timeoutMs };
1903
1945
  }
1904
1946
 
1905
1947
  function resolveToolBudget(
@@ -1908,7 +1950,7 @@ function resolveToolBudget(
1908
1950
  options?: { minimumHard?: 0 | 1 },
1909
1951
  ): { toolBudget?: ResolvedToolBudget; error?: string } {
1910
1952
  const resolved = validateToolBudgetConfig(raw, label, options);
1911
- return { toolBudget: resolved.budget, error: resolved.error };
1953
+ return { ...(resolved.budget === undefined ? {} : { toolBudget: resolved.budget }), ...(resolved.error === undefined ? {} : { error: resolved.error }) };
1912
1954
  }
1913
1955
 
1914
1956
  function resolveEffectiveToolBudget(input: { stepBudget?: ToolBudgetConfig; runBudget?: ResolvedToolBudget; agentBudget?: ToolBudgetConfig; configBudget?: ToolBudgetConfig }): { toolBudget?: ResolvedToolBudget; error?: string } {
@@ -1942,7 +1984,7 @@ function expandChainParallelCounts(chain: ChainStep[]): { chain?: ChainStep[]; e
1942
1984
  expandedChain.push(step);
1943
1985
  continue;
1944
1986
  }
1945
- const expandedParallel = [];
1987
+ const expandedParallel: ParallelTaskItem[] = [];
1946
1988
  for (let taskIndex = 0; taskIndex < step.parallel.length; taskIndex++) {
1947
1989
  const task = step.parallel[taskIndex]!;
1948
1990
  const rawCount = (task as typeof task & { count?: unknown }).count;
@@ -1965,14 +2007,14 @@ function normalizeRepeatedParallelCounts(params: SubagentParamsLike): { params?:
1965
2007
  if (expandedTasks.error) {
1966
2008
  return { error: buildRequestedModeError(params, expandedTasks.error) };
1967
2009
  }
1968
- return { params: { ...params, tasks: expandedTasks.tasks } };
2010
+ return { params: { ...params, ...(expandedTasks.tasks === undefined ? {} : { tasks: expandedTasks.tasks }) } };
1969
2011
  }
1970
2012
  if (params.chain) {
1971
2013
  const expandedChain = expandChainParallelCounts(params.chain);
1972
2014
  if (expandedChain.error) {
1973
2015
  return { error: buildRequestedModeError(params, expandedChain.error) };
1974
2016
  }
1975
- return { params: { ...params, chain: expandedChain.chain } };
2017
+ return { params: { ...params, ...(expandedChain.chain === undefined ? {} : { chain: expandedChain.chain }) } };
1976
2018
  }
1977
2019
  return { params };
1978
2020
  }
@@ -2078,6 +2120,87 @@ function collectChainThinkingOverrides(
2078
2120
  return thinkingOverrides;
2079
2121
  }
2080
2122
 
2123
+ type StaticLaunchSummary = { agent: string; model?: string; thinking?: string };
2124
+
2125
+ function resolveStaticLaunchSummary(input: {
2126
+ agent: string;
2127
+ index: number;
2128
+ explicitModel?: string;
2129
+ agents: AgentConfig[];
2130
+ parentModel?: ParentModel;
2131
+ availableModels: ModelInfo[];
2132
+ currentProvider?: string;
2133
+ modelScope?: ModelScopeConfig;
2134
+ thinkingOverrideForTask: ForkThinkingOverrideForTask;
2135
+ }): StaticLaunchSummary {
2136
+ const agentConfig = input.agents.find((agent) => agent.name === input.agent);
2137
+ const model = resolveEffectiveSubagentModel(
2138
+ input.explicitModel,
2139
+ agentConfig?.model,
2140
+ input.parentModel,
2141
+ input.availableModels,
2142
+ input.currentProvider,
2143
+ input.modelScope === undefined ? {} : { scope: input.modelScope },
2144
+ );
2145
+ const thinkingOverride = input.thinkingOverrideForTask(input.agent, input.index, model);
2146
+ const thinking = resolveEffectiveThinking(model, thinkingOverride ?? agentConfig?.thinking);
2147
+ return {
2148
+ agent: input.agent,
2149
+ ...(model ? { model } : {}),
2150
+ ...(thinking ? { thinking } : {}),
2151
+ };
2152
+ }
2153
+
2154
+ function collectStaticLaunchSummaries(input: {
2155
+ params: SubagentParamsLike;
2156
+ agents: AgentConfig[];
2157
+ parentModel?: ParentModel;
2158
+ availableModels: ModelInfo[];
2159
+ currentProvider?: string;
2160
+ modelScope?: ModelScopeConfig;
2161
+ thinkingOverrideForTask: ForkThinkingOverrideForTask;
2162
+ dynamicFanoutMaxItems?: number;
2163
+ }): StaticLaunchSummary[] {
2164
+ const summary = (agent: string, index: number, explicitModel?: string) => resolveStaticLaunchSummary({
2165
+ agent,
2166
+ index,
2167
+ explicitModel,
2168
+ agents: input.agents,
2169
+ parentModel: input.parentModel,
2170
+ availableModels: input.availableModels,
2171
+ currentProvider: input.currentProvider,
2172
+ modelScope: input.modelScope,
2173
+ thinkingOverrideForTask: input.thinkingOverrideForTask,
2174
+ });
2175
+ if (input.params.tasks) return input.params.tasks.map((task, index) => summary(task.agent, index, task.model));
2176
+ if (input.params.chain?.length) {
2177
+ const launches: StaticLaunchSummary[] = [];
2178
+ let flatIndex = 0;
2179
+ for (const step of input.params.chain) {
2180
+ if (isParallelStep(step)) {
2181
+ for (const task of step.parallel) {
2182
+ launches.push(summary(task.agent, flatIndex, task.model));
2183
+ flatIndex++;
2184
+ }
2185
+ continue;
2186
+ }
2187
+ if (isDynamicParallelStep(step)) {
2188
+ const maxItems = step.expand.maxItems ?? input.dynamicFanoutMaxItems ?? 0;
2189
+ for (let itemIndex = 0; itemIndex < maxItems; itemIndex++) {
2190
+ launches.push(summary(step.parallel.agent, flatIndex, step.parallel.model));
2191
+ flatIndex++;
2192
+ }
2193
+ continue;
2194
+ }
2195
+ const sequential = step as SequentialStep;
2196
+ launches.push(summary(sequential.agent, flatIndex, sequential.model));
2197
+ flatIndex++;
2198
+ }
2199
+ return launches;
2200
+ }
2201
+ return input.params.agent ? [summary(input.params.agent, 0, input.params.model as string | undefined)] : [];
2202
+ }
2203
+
2081
2204
  function firstChainAgent(chain: ChainStep[]): string | undefined {
2082
2205
  const first = chain[0];
2083
2206
  if (!first) return undefined;
@@ -2105,34 +2228,34 @@ function resolveAsyncEventGoal(workflowTask: string | undefined, rawChain: Chain
2105
2228
  function wrapChainTasksForFork(chain: ChainStep[], contextPolicy: AgentDefaultContextPolicy): ChainStep[] {
2106
2229
  return chain.map((step, stepIndex) => {
2107
2230
  if (isParallelStep(step)) {
2108
- return {
2231
+ return compactOptional<ParallelStep>({
2109
2232
  ...step,
2110
- parallel: step.parallel.map((task) => ({
2233
+ parallel: step.parallel.map((task) => compactOptional<ParallelTaskItem>({
2111
2234
  ...task,
2112
2235
  task: shouldForkAgent(contextPolicy, task.agent)
2113
2236
  ? wrapForkTask(task.task ?? "{previous}")
2114
2237
  : task.task,
2115
2238
  })),
2116
- };
2239
+ });
2117
2240
  }
2118
2241
  if (isDynamicParallelStep(step)) {
2119
- return {
2242
+ return compactOptional<DynamicParallelStep>({
2120
2243
  ...step,
2121
- parallel: {
2244
+ parallel: compactOptional<DynamicParallelStep["parallel"]>({
2122
2245
  ...step.parallel,
2123
2246
  task: shouldForkAgent(contextPolicy, step.parallel.agent)
2124
2247
  ? wrapForkTask(step.parallel.task ?? "{previous}")
2125
2248
  : step.parallel.task,
2126
- },
2127
- };
2249
+ }),
2250
+ });
2128
2251
  }
2129
2252
  const sequential = step as SequentialStep;
2130
- return {
2253
+ return compactOptional<SequentialStep>({
2131
2254
  ...sequential,
2132
2255
  task: shouldForkAgent(contextPolicy, sequential.agent)
2133
2256
  ? wrapForkTask(sequential.task ?? (stepIndex === 0 ? "{task}" : "{previous}"))
2134
2257
  : sequential.task,
2135
- };
2258
+ });
2136
2259
  });
2137
2260
  }
2138
2261
 
@@ -2232,16 +2355,17 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
2232
2355
  }
2233
2356
  const id = randomUUID();
2234
2357
  const parentModel = data.parentModel;
2235
- const asyncCtx = {
2358
+ const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
2236
2359
  pi: deps.pi,
2237
2360
  cwd: ctx.cwd,
2238
- currentSessionId: deps.state.currentSessionId!,
2239
- parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
2361
+ currentSessionId: data.parentSessionId!,
2362
+ parentSessionId: data.parentPiSessionId,
2240
2363
  currentModelProvider: parentModel?.provider,
2241
2364
  currentModel: parentModel,
2242
2365
  modelScope: data.modelScope,
2243
2366
  interactive: ctx.hasUI,
2244
- };
2367
+ permissions: deps.config.permissions,
2368
+ });
2245
2369
  const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
2246
2370
  const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
2247
2371
  const currentProvider = parentModel?.provider;
@@ -2250,7 +2374,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
2250
2374
 
2251
2375
  if (hasTasks && params.tasks) {
2252
2376
  const skillOverrides = params.tasks.map((task) => normalizeSkillInput(task.skill));
2253
- const parallelTasks = params.tasks.map((task, index) => ({
2377
+ const parallelTasks = params.tasks.map((task, index) => compactOptional<ParallelTaskItem>({
2254
2378
  agent: task.agent,
2255
2379
  task: shouldForkAgent(contextPolicy, task.agent) ? wrapForkTask(task.task) : task.task,
2256
2380
  cwd: task.cwd,
@@ -2265,12 +2389,12 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
2265
2389
  ...(task.agentContract !== undefined ? { agentContract: task.agentContract } : {}),
2266
2390
  ...(task.acceptance !== undefined ? { acceptance: task.acceptance } : {}),
2267
2391
  }));
2268
- return executeAsyncChain(id, {
2269
- chain: [{
2392
+ return executeAsyncChain(id, compactOptional<Parameters<typeof executeAsyncChain>[1]>({
2393
+ chain: [compactOptional<ParallelStep>({
2270
2394
  parallel: parallelTasks,
2271
2395
  concurrency: resolveTopLevelParallelConcurrency(params.concurrency, deps.config.parallel?.concurrency),
2272
2396
  worktree: params.worktree,
2273
- }],
2397
+ })],
2274
2398
  resultMode: "parallel",
2275
2399
  goal: params.tasks[0]?.task ?? "",
2276
2400
  agents,
@@ -2303,7 +2427,9 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
2303
2427
  configToolBudget: data.configToolBudget,
2304
2428
  capabilityCeiling: data.capabilityCeiling,
2305
2429
  globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
2306
- });
2430
+ parentWorkflowRunId: params.workflowParentRunId,
2431
+ workflowKey: params.workflowKey,
2432
+ }));
2307
2433
  }
2308
2434
 
2309
2435
  if (hasChain && params.chain) {
@@ -2311,7 +2437,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
2311
2437
  const chainSkills = normalized === false ? [] : (normalized ?? []);
2312
2438
  const rawChain = params.chain as ChainStep[];
2313
2439
  const chain = wrapChainTasksForFork(rawChain, contextPolicy);
2314
- return executeAsyncChain(id, {
2440
+ return executeAsyncChain(id, compactOptional<Parameters<typeof executeAsyncChain>[1]>({
2315
2441
  chain,
2316
2442
  task: params.task,
2317
2443
  goal: resolveAsyncEventGoal(params.task, rawChain),
@@ -2346,7 +2472,9 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
2346
2472
  configToolBudget: data.configToolBudget,
2347
2473
  capabilityCeiling: data.capabilityCeiling,
2348
2474
  globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
2349
- });
2475
+ parentWorkflowRunId: params.workflowParentRunId,
2476
+ workflowKey: params.workflowKey,
2477
+ }));
2350
2478
  }
2351
2479
 
2352
2480
  if (hasSingle) {
@@ -2360,11 +2488,12 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
2360
2488
  }
2361
2489
  const rawOutput = params.output !== undefined ? params.output : a.output;
2362
2490
  const effectiveOutput = normalizeSingleOutputOverride(rawOutput, a.output);
2491
+ const effectiveOutputMode = params.outputMode ?? "inline";
2363
2492
  const normalizedSkills = normalizeSkillInput(params.skill);
2364
2493
  const skills = normalizedSkills === false ? [] : normalizedSkills;
2365
2494
  const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, a.maxSubagentDepth);
2366
- const modelOverride = resolveEffectiveSubagentModel(params.model as string | undefined, a.model, parentModel, availableModels, currentProvider, { scope: data.modelScope });
2367
- return executeAsyncSingle(id, {
2495
+ const modelOverride = resolveEffectiveSubagentModel(params.model as string | undefined, a.model, parentModel, availableModels, currentProvider, data.modelScope === undefined ? {} : { scope: data.modelScope });
2496
+ return executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
2368
2497
  agent: params.agent!,
2369
2498
  task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
2370
2499
  goal: params.task ?? "",
@@ -2381,7 +2510,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
2381
2510
  context: contextPolicy.contextForAgent(params.agent!),
2382
2511
  skills,
2383
2512
  output: effectiveOutput,
2384
- outputMode: params.outputMode,
2513
+ outputMode: effectiveOutputMode,
2385
2514
  outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
2386
2515
  modelOverride,
2387
2516
  thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
@@ -2403,7 +2532,9 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
2403
2532
  usageBudget: data.usageBudget,
2404
2533
  configToolBudget: data.configToolBudget,
2405
2534
  capabilityCeiling: data.capabilityCeiling,
2406
- });
2535
+ parentWorkflowRunId: params.workflowParentRunId,
2536
+ workflowKey: params.workflowKey,
2537
+ }));
2407
2538
  }
2408
2539
 
2409
2540
  return null;
@@ -2436,8 +2567,8 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
2436
2567
  const chainSkills = normalized === false ? [] : (normalized ?? []);
2437
2568
  const chain = wrapChainTasksForFork(params.chain as ChainStep[], contextPolicy);
2438
2569
  const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
2439
- const chainCtx = normalizeParentModel(ctx.model) || !data.parentModel ? ctx : { ...ctx, model: data.parentModel };
2440
- const chainResult = await executeChain({
2570
+ const chainCtx = normalizeParentModel(ctx.model) || !data.parentModel ? ctx : ({ ...ctx, model: data.parentModel } as ExtensionContext);
2571
+ const chainResult = await executeChain(compactOptional<Parameters<typeof executeChain>[0]>({
2441
2572
  chain,
2442
2573
  task: params.task,
2443
2574
  agents,
@@ -2466,7 +2597,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
2466
2597
  foregroundControl,
2467
2598
  nestedRoute: foregroundControl?.nestedRoute,
2468
2599
  chainSkills,
2469
- chainDir: params.chainDir ?? getProjectChainRunsDir(effectiveCwd),
2600
+ chainDir: params.chainDir ?? getChainRunsDir(effectiveCwd, artifactConfig.dir),
2470
2601
  dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
2471
2602
  maxSubagentDepth: currentMaxSubagentDepth,
2472
2603
  worktreeSetupHook: deps.config.worktreeSetupHook,
@@ -2488,9 +2619,10 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
2488
2619
  toolBudget: data.toolBudget,
2489
2620
  usageBudget: data.usageBudget,
2490
2621
  configToolBudget: data.configToolBudget,
2622
+ permissions: deps.config.permissions,
2491
2623
  globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
2492
2624
  capabilityCeiling: data.capabilityCeiling,
2493
- });
2625
+ }));
2494
2626
 
2495
2627
  if (chainResult.requestedAsync) {
2496
2628
  if (!isAsyncAvailable()) {
@@ -2502,20 +2634,21 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
2502
2634
  }
2503
2635
  const id = randomUUID();
2504
2636
  const parentModel = data.parentModel;
2505
- const asyncCtx = {
2637
+ const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
2506
2638
  pi: deps.pi,
2507
2639
  cwd: ctx.cwd,
2508
- currentSessionId: deps.state.currentSessionId!,
2509
- parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
2640
+ currentSessionId: data.parentSessionId!,
2641
+ parentSessionId: data.parentPiSessionId,
2510
2642
  currentModelProvider: parentModel?.provider,
2511
2643
  currentModel: parentModel,
2512
2644
  modelScope: data.modelScope,
2513
2645
  interactive: ctx.hasUI,
2514
- };
2646
+ permissions: deps.config.permissions,
2647
+ });
2515
2648
  const rawAsyncChain = chainResult.requestedAsync.chain;
2516
2649
  const asyncChain = wrapChainTasksForFork(rawAsyncChain, contextPolicy);
2517
2650
  const firstAgent = firstChainAgent(rawAsyncChain);
2518
- return executeAsyncChain(id, {
2651
+ return executeAsyncChain(id, compactOptional<Parameters<typeof executeAsyncChain>[1]>({
2519
2652
  chain: asyncChain,
2520
2653
  task: params.task,
2521
2654
  goal: resolveAsyncEventGoal(params.task, rawAsyncChain, firstAgent ? shouldForkAgent(contextPolicy, firstAgent) : false),
@@ -2550,15 +2683,17 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
2550
2683
  configToolBudget: data.configToolBudget,
2551
2684
  capabilityCeiling: data.capabilityCeiling,
2552
2685
  globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
2553
- });
2686
+ }));
2554
2687
  }
2555
2688
 
2556
- const rawChainDetails = chainResult.details ? { ...chainResult.details, runId, timeoutMs: data.timeoutMs } : undefined;
2689
+ const rawChainDetails = chainResult.details ? compactOptional<Details>({ ...chainResult.details, runId, timeoutMs: data.timeoutMs }) : undefined;
2557
2690
  if (foregroundControl && rawChainDetails) {
2558
2691
  updateForegroundNestedProjection(foregroundControl);
2559
2692
  attachRootChildrenToSteps(runId, rawChainDetails.results, foregroundControl.nestedChildren);
2560
2693
  rawChainDetails.totalCost = sumResultsCost(rawChainDetails.results);
2561
- rawChainDetails.usageBudget = usageBudgetState(data.usageBudget, rawChainDetails.totalCost);
2694
+ const usageBudget = usageBudgetState(data.usageBudget, rawChainDetails.totalCost);
2695
+ if (usageBudget === undefined) delete rawChainDetails.usageBudget;
2696
+ else rawChainDetails.usageBudget = usageBudget;
2562
2697
  }
2563
2698
  const chainDetails = rawChainDetails ? compactForegroundDetails(rawChainDetails) : undefined;
2564
2699
  if (chainDetails) rememberForegroundRun(deps.state, { runId, mode: "chain", cwd: effectiveCwd, sessionId: data.parentSessionId, results: chainDetails.results, checkpoint: chainDetails.checkpoint });
@@ -2632,6 +2767,7 @@ interface ForegroundParallelRunInput {
2632
2767
  usageBudget?: UsageBudgetConfig;
2633
2768
  toolBudgets: (ResolvedToolBudget | undefined)[];
2634
2769
  agentContract?: AgentContract;
2770
+ permissions?: ExtensionConfig["permissions"];
2635
2771
  }
2636
2772
  function buildParallelModeError(message: string): AgentToolResult<Details> {
2637
2773
  return {
@@ -2653,13 +2789,13 @@ function createParallelWorktreeSetup(
2653
2789
  if (!enabled) return {};
2654
2790
  try {
2655
2791
  return {
2656
- setup: createWorktrees(cwd, runId, tasks.length, {
2792
+ setup: createWorktrees(cwd, runId, tasks.length, omitUndefinedProperties({
2657
2793
  agents: tasks.map((task) => task.agent),
2658
2794
  setupHook: setupHook
2659
- ? { hookPath: setupHook, timeoutMs: setupHookTimeoutMs }
2795
+ ? { hookPath: setupHook, ...(setupHookTimeoutMs === undefined ? {} : { timeoutMs: setupHookTimeoutMs }) }
2660
2796
  : undefined,
2661
2797
  baseDir,
2662
- }),
2798
+ })),
2663
2799
  };
2664
2800
  } catch (error) {
2665
2801
  const message = error instanceof Error ? error.message : String(error);
@@ -2682,19 +2818,6 @@ function resolveSingleRunOutputBaseDir(deps: ExecutorDeps, artifactsDir: string,
2682
2818
  : path.join(artifactsDir, "outputs", runId);
2683
2819
  }
2684
2820
 
2685
- /**
2686
- * Generated per-run durable output path for foreground single runs when the
2687
- * caller omits `output` (and does not set `output: false`):
2688
- * `<singleRunOutputBaseDir>/<runId>/result.md`. The base is the configured
2689
- * `singleRunOutputBaseDir` when present, otherwise `{artifactsDir}/outputs`.
2690
- */
2691
- function resolveGeneratedSingleOutputPath(deps: ExecutorDeps, artifactsDir: string, runId: string): string {
2692
- const base = deps.config.singleRunOutputBaseDir
2693
- ? path.resolve(deps.expandTilde(deps.config.singleRunOutputBaseDir))
2694
- : path.join(artifactsDir, "outputs");
2695
- return path.join(base, runId, "result.md");
2696
- }
2697
-
2698
2821
  function buildChainWorktreeTaskCwdError(chain: ChainStep[], sharedCwd: string): string | undefined {
2699
2822
  for (let stepIndex = 0; stepIndex < chain.length; stepIndex++) {
2700
2823
  const step = chain[stepIndex]!;
@@ -2728,39 +2851,41 @@ function finalizeParallelWorktreeHandoff(input: {
2728
2851
  }): { suffix: string; reference?: NonNullable<Details["parallelHandoff"]> } {
2729
2852
  const diffsDir = path.join(input.artifactsDir, "worktree-diffs", input.runId);
2730
2853
  const diffs = diffWorktrees(input.worktreeSetup, input.tasks.map((task) => task.agent), diffsDir);
2731
- const cleanup = cleanupWorktrees(input.worktreeSetup);
2732
2854
  const diffSummary = formatWorktreeDiffSummary(diffs);
2733
- try {
2734
- const reference = writeParallelHandoffGroup({
2735
- manifestPath: parallelHandoffPath(input.artifactsDir, input.runId),
2736
- runId: input.runId,
2737
- mode: "parallel",
2738
- source: "foreground",
2739
- cwd: input.cwd,
2740
- stepIndex: 0,
2741
- flatStartIndex: 0,
2742
- setup: input.worktreeSetup,
2743
- diffs,
2744
- cleanup,
2745
- results: input.results.map((result) => ({
2746
- agent: result.agent,
2747
- status: resolveSubagentResultStatus({
2748
- exitCode: result.exitCode,
2749
- interrupted: result.interrupted,
2750
- detached: result.detached,
2751
- state: result.stopped ? "stopped" : undefined,
2752
- processSignal: result.processSignal,
2753
- timedOut: result.timedOut,
2754
- stopped: result.stopped,
2755
- turnBudgetExceeded: result.turnBudgetExceeded,
2756
- }),
2757
- summary: resultSummaryForIntercom(result),
2758
- ...(result.artifactPaths?.outputPath ? { outputPath: result.artifactPaths.outputPath } : {}),
2759
- ...(result.structuredOutput !== undefined ? { structuredOutput: result.structuredOutput } : {}),
2760
- ...(result.structuredOutputPath ? { structuredOutputPath: result.structuredOutputPath } : {}),
2761
- ...(result.sessionFile ? { sessionPath: result.sessionFile } : {}),
2855
+ const manifestPath = parallelHandoffPath(input.artifactsDir, input.runId);
2856
+ const handoff = {
2857
+ manifestPath,
2858
+ runId: input.runId,
2859
+ mode: "parallel" as const,
2860
+ source: "foreground" as const,
2861
+ cwd: input.cwd,
2862
+ stepIndex: 0,
2863
+ flatStartIndex: 0,
2864
+ setup: input.worktreeSetup,
2865
+ diffs,
2866
+ results: input.results.map((result) => ({
2867
+ agent: result.agent,
2868
+ status: resolveSubagentResultStatus(omitUndefinedProperties({
2869
+ exitCode: result.exitCode,
2870
+ interrupted: result.interrupted,
2871
+ detached: result.detached,
2872
+ state: result.stopped ? "stopped" : undefined,
2873
+ processSignal: result.processSignal,
2874
+ timedOut: result.timedOut,
2875
+ stopped: result.stopped,
2876
+ turnBudgetExceeded: result.turnBudgetExceeded,
2762
2877
  })),
2763
- });
2878
+ summary: resultSummaryForIntercom(result),
2879
+ ...(result.artifactPaths?.outputPath ? { outputPath: result.artifactPaths.outputPath } : {}),
2880
+ ...(result.structuredOutput !== undefined ? { structuredOutput: result.structuredOutput } : {}),
2881
+ ...(result.structuredOutputPath ? { structuredOutputPath: result.structuredOutputPath } : {}),
2882
+ ...(result.sessionFile ? { sessionPath: result.sessionFile } : {}),
2883
+ })),
2884
+ };
2885
+ try {
2886
+ writeParallelHandoffGroup(handoff);
2887
+ const cleanup = cleanupWorktrees(input.worktreeSetup, { kind: "preserve", capturedDiffs: diffs, handoffManifestPath: manifestPath });
2888
+ const reference = writeParallelHandoffGroup({ ...handoff, cleanup });
2764
2889
  return {
2765
2890
  suffix: [diffSummary, formatParallelHandoffReference(reference)].filter(Boolean).join("\n\n"),
2766
2891
  reference,
@@ -2840,7 +2965,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
2840
2965
  beginForegroundChild(input.foregroundControl, {
2841
2966
  index,
2842
2967
  agent: task.agent,
2843
- description: input.taskDescriptions[index],
2968
+ ...(input.taskDescriptions[index] === undefined ? {} : { description: input.taskDescriptions[index] }),
2844
2969
  ...(model ? { model } : {}),
2845
2970
  ...(thinking ? { thinking } : {}),
2846
2971
  interrupt: () => {
@@ -2854,7 +2979,8 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
2854
2979
  ? createStructuredOutputRuntime(task.outputSchema, path.join(input.artifactsDir, "structured-output", input.runId))
2855
2980
  : undefined;
2856
2981
  let detachedReceipt = false;
2857
- const result = await runSync(input.ctx.cwd, input.agents, task.agent, taskText, {
2982
+ const result = await runSync(input.ctx.cwd, input.agents, task.agent, taskText, compactOptional<Parameters<typeof runSync>[4]>({
2983
+ permissions: input.permissions,
2858
2984
  parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
2859
2985
  context: input.contextPolicy.contextForAgent(task.agent),
2860
2986
  cwd: taskCwd,
@@ -2918,17 +3044,17 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
2918
3044
  const mergedProgress = input.liveProgress.filter((progress): progress is AgentProgress => progress !== undefined);
2919
3045
  input.onUpdate?.({
2920
3046
  content: progressUpdate.content,
2921
- details: {
3047
+ details: compactOptional<Details>({
2922
3048
  mode: "parallel",
2923
3049
  results: mergedResults,
2924
3050
  progress: mergedProgress,
2925
3051
  controlEvents: progressUpdate.details?.controlEvents,
2926
3052
  totalSteps: input.tasks.length,
2927
- },
3053
+ }),
2928
3054
  });
2929
3055
  }
2930
3056
  : undefined,
2931
- }).then((result) => {
3057
+ })).then((result) => {
2932
3058
  detachedReceipt = result.detached === true;
2933
3059
  return result;
2934
3060
  }).finally(() => {
@@ -3003,7 +3129,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3003
3129
  );
3004
3130
  const toolBudgets: (ResolvedToolBudget | undefined)[] = [];
3005
3131
  for (let index = 0; index < tasks.length; index++) {
3006
- const resolved = resolveEffectiveToolBudget({ stepBudget: tasks[index]?.toolBudget, runBudget: data.toolBudget, agentBudget: agentConfigs[index]?.toolBudget, configBudget: data.configToolBudget });
3132
+ const resolved = resolveEffectiveToolBudget(omitUndefinedProperties({ stepBudget: tasks[index]?.toolBudget, runBudget: data.toolBudget, agentBudget: agentConfigs[index]?.toolBudget, configBudget: data.configToolBudget }));
3007
3133
  if (resolved.error) return buildParallelModeError(resolved.error);
3008
3134
  toolBudgets.push(resolved.toolBudget);
3009
3135
  }
@@ -3029,7 +3155,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3029
3155
  ...(task.model !== undefined ? { model: task.model } : {}),
3030
3156
  }));
3031
3157
  const modelOverrides: (string | undefined)[] = tasks.map((_, i) =>
3032
- resolveEffectiveSubagentModel(behaviorOverrides[i]?.model, agentConfigs[i]?.model, parentModel, availableModels, currentProvider, { scope: data.modelScope }),
3158
+ resolveEffectiveSubagentModel(behaviorOverrides[i]?.model, agentConfigs[i]?.model, parentModel, availableModels, currentProvider, data.modelScope === undefined ? {} : { scope: data.modelScope }),
3033
3159
  );
3034
3160
 
3035
3161
  if (params.clarify === true && ctx.hasUI) {
@@ -3064,7 +3190,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3064
3190
  for (let i = 0; i < result.behaviorOverrides.length; i++) {
3065
3191
  const override = result.behaviorOverrides[i];
3066
3192
  if (override?.model !== undefined) {
3067
- modelOverrides[i] = resolveEffectiveSubagentModel(override.model, agentConfigs[i]?.model, parentModel, availableModels, currentProvider, { scope: data.modelScope });
3193
+ modelOverrides[i] = resolveEffectiveSubagentModel(override.model, agentConfigs[i]?.model, parentModel, availableModels, currentProvider, data.modelScope === undefined ? {} : { scope: data.modelScope });
3068
3194
  behaviorOverrides[i]!.model = override.model;
3069
3195
  }
3070
3196
  if (override?.output !== undefined) behaviorOverrides[i]!.output = override.output;
@@ -3085,20 +3211,21 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3085
3211
  };
3086
3212
  }
3087
3213
  const id = randomUUID();
3088
- const asyncCtx = {
3214
+ const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
3089
3215
  pi: deps.pi,
3090
3216
  cwd: ctx.cwd,
3091
- currentSessionId: deps.state.currentSessionId!,
3092
- parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
3217
+ currentSessionId: data.parentSessionId!,
3218
+ parentSessionId: data.parentPiSessionId,
3093
3219
  currentModelProvider: parentModel?.provider,
3094
3220
  currentModel: parentModel,
3095
3221
  modelScope: data.modelScope,
3096
3222
  interactive: ctx.hasUI,
3097
- };
3223
+ permissions: deps.config.permissions,
3224
+ });
3098
3225
  const parallelTasks = tasks.map((t, i) => {
3099
3226
  const taskText = shouldForkAgent(contextPolicy, t.agent) ? wrapForkTask(taskTexts[i]!) : taskTexts[i]!;
3100
3227
  const progress = taskDisallowsFileUpdates(taskText) ? false : behaviorOverrides[i]?.progress;
3101
- return {
3228
+ return compactOptional<ParallelTaskItem>({
3102
3229
  agent: t.agent,
3103
3230
  task: taskText,
3104
3231
  cwd: t.cwd,
@@ -3112,10 +3239,10 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3112
3239
  ...(t.outputSchema !== undefined ? { outputSchema: t.outputSchema } : {}),
3113
3240
  ...(t.acceptance !== undefined ? { acceptance: t.acceptance } : {}),
3114
3241
  ...(t.agentContract !== undefined ? { agentContract: t.agentContract } : {}),
3115
- };
3242
+ });
3116
3243
  });
3117
- return executeAsyncChain(id, {
3118
- chain: [{ parallel: parallelTasks, concurrency: parallelConcurrency, worktree: params.worktree }],
3244
+ return executeAsyncChain(id, compactOptional<Parameters<typeof executeAsyncChain>[1]>({
3245
+ chain: [compactOptional<ParallelStep>({ parallel: parallelTasks, concurrency: parallelConcurrency, worktree: params.worktree })],
3119
3246
  resultMode: "parallel",
3120
3247
  goal: taskTexts[0] ?? "",
3121
3248
  agents,
@@ -3147,25 +3274,14 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3147
3274
  toolBudget: data.toolBudget,
3148
3275
  configToolBudget: data.configToolBudget,
3149
3276
  globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
3150
- });
3277
+ }));
3151
3278
  }
3152
3279
  }
3153
3280
 
3154
3281
  const behaviors = tasks.map((task, index) => {
3155
3282
  let behavior = suppressProgressForReadOnlyTask(resolveStepBehavior(agentConfigs[index]!, behaviorOverrides[index]!), taskTexts[index]);
3156
- if (behaviorOverrides[index]?.output === undefined) {
3157
- if (typeof behavior.output === "string" && !path.isAbsolute(behavior.output)) {
3158
- behavior = { ...behavior, output: path.join("parallel-0", `${index}-${task.agent}`, behavior.output) };
3159
- } else if (behavior.output === false && behaviorOverrides[index]?.output !== false && behaviorOverrides[index]?.output !== "false") {
3160
- // Omitted output: generate a collision-safe per-task durable path in the
3161
- // existing per-task namespace (parallel-0/<index>-<agent>/result.md).
3162
- behavior = { ...behavior, output: path.join("parallel-0", `${index}-${task.agent}`, "result.md") };
3163
- }
3164
- }
3165
- // Omitted outputMode resolves to file-only whenever an output path is active;
3166
- // explicit `outputMode: "inline"` is preserved exactly.
3167
- if (behaviorOverrides[index]?.outputMode === undefined && typeof behavior.output === "string") {
3168
- behavior = { ...behavior, outputMode: "file-only" };
3283
+ if (behaviorOverrides[index]?.output === undefined && typeof behavior.output === "string" && !path.isAbsolute(behavior.output)) {
3284
+ behavior = { ...behavior, output: path.join("parallel-0", `${index}-${task.agent}`, behavior.output) };
3169
3285
  }
3170
3286
  return behavior;
3171
3287
  });
@@ -3186,15 +3302,27 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3186
3302
 
3187
3303
  let worktreeFinalized = false;
3188
3304
  try {
3305
+ if (worktreeSetup) {
3306
+ writePendingParallelHandoff({
3307
+ manifestPath: parallelHandoffPath(artifactsDir, runId),
3308
+ runId,
3309
+ mode: "parallel",
3310
+ source: "foreground",
3311
+ cwd: effectiveCwd,
3312
+ stepIndex: 0,
3313
+ flatStartIndex: 0,
3314
+ setup: worktreeSetup,
3315
+ });
3316
+ }
3189
3317
  const outputBaseDir = path.join(artifactsDir, "outputs", runId);
3190
- const duplicateOutputError = findDuplicateParallelOutputPath({
3318
+ const duplicateOutputError = findDuplicateParallelOutputPath(omitUndefinedProperties({
3191
3319
  tasks,
3192
3320
  behaviors,
3193
3321
  paramsCwd: effectiveCwd,
3194
3322
  ctxCwd: ctx.cwd,
3195
3323
  outputBaseDir,
3196
3324
  worktreeSetup,
3197
- });
3325
+ }));
3198
3326
  if (duplicateOutputError) return buildParallelModeError(duplicateOutputError);
3199
3327
  for (let index = 0; index < tasks.length; index++) {
3200
3328
  const taskCwd = resolveParallelTaskCwd(tasks[index]!, effectiveCwd, worktreeSetup, index);
@@ -3213,8 +3341,9 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3213
3341
  }
3214
3342
 
3215
3343
  const deadlineAt = data.deadlineAt ?? (data.timeoutMs !== undefined ? Date.now() + data.timeoutMs : undefined);
3216
- const results = await runForegroundParallelTasks({
3344
+ const results = await runForegroundParallelTasks(compactOptional<ForegroundParallelRunInput>({
3217
3345
  tasks,
3346
+ permissions: deps.config.permissions,
3218
3347
  taskTexts,
3219
3348
  taskDescriptions,
3220
3349
  agents,
@@ -3262,7 +3391,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3262
3391
  usageBudget: data.usageBudget,
3263
3392
  toolBudgets,
3264
3393
  agentContract: params.agentContract,
3265
- });
3394
+ }));
3266
3395
  for (let i = 0; i < results.length; i++) {
3267
3396
  const run = results[i]!;
3268
3397
  recordRun(run.agent, taskTexts[i]!, run.exitCode, run.progressSummary?.durationMs ?? 0);
@@ -3284,7 +3413,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3284
3413
  }
3285
3414
  const interrupted = results.find((result) => result.interrupted);
3286
3415
  const totalCost = sumResultsCost(results);
3287
- const details = compactForegroundDetails({
3416
+ const details = compactForegroundDetails(compactOptional<Details>({
3288
3417
  mode: "parallel",
3289
3418
  runId,
3290
3419
  timeoutMs: data.timeoutMs,
@@ -3295,7 +3424,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3295
3424
  totalCost,
3296
3425
  usageBudget: usageBudgetState(data.usageBudget, totalCost),
3297
3426
  ...(handoff?.reference ? { parallelHandoff: handoff.reference } : {}),
3298
- });
3427
+ }));
3299
3428
  rememberForegroundRun(deps.state, { runId, mode: "parallel", cwd: effectiveCwd, sessionId: data.parentSessionId, results: details.results });
3300
3429
  if (interrupted) {
3301
3430
  return {
@@ -3312,31 +3441,43 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3312
3441
  };
3313
3442
  }
3314
3443
 
3444
+ const suppressRoutineResultIntercom = shouldSuppressRoutineResultIntercom({ suppressRoutineResultIntercom: params.suppressRoutineResultIntercom, results });
3315
3445
  if (foregroundControl) updateForegroundNestedProjection(foregroundControl);
3316
- const intercomReceipt = await maybeBuildForegroundIntercomReceipt({
3317
- pi: deps.pi,
3318
- intercomBridge: data.intercomBridge,
3319
- runId,
3320
- mode: "parallel",
3321
- details,
3322
- ...(foregroundControl?.nestedChildren?.length ? { nestedChildren: foregroundControl.nestedChildren } : {}),
3323
- });
3324
- if (intercomReceipt) {
3325
- return {
3326
- content: [{ type: "text", text: intercomReceipt.text }],
3327
- details: intercomReceipt.details,
3328
- };
3446
+ if (!suppressRoutineResultIntercom) {
3447
+ const intercomReceipt = await maybeBuildForegroundIntercomReceipt({
3448
+ pi: deps.pi,
3449
+ intercomBridge: data.intercomBridge,
3450
+ runId,
3451
+ mode: "parallel",
3452
+ details,
3453
+ ...(foregroundControl?.nestedChildren?.length ? { nestedChildren: foregroundControl.nestedChildren } : {}),
3454
+ });
3455
+ if (intercomReceipt) {
3456
+ return {
3457
+ content: [{ type: "text", text: intercomReceipt.text }],
3458
+ details: intercomReceipt.details,
3459
+ };
3460
+ }
3329
3461
  }
3330
3462
 
3331
3463
  const worktreeSuffix = handoff?.suffix ?? "";
3332
3464
  const ok = results.filter((result) => result.exitCode === 0).length;
3333
3465
  const downgradeNote = backgroundRequestedWhileClarifying ? " (background requested, but clarify kept this run foreground)" : "";
3334
- const perChildResults = results.map((result, i) => formatParallelChildResult(result, i)).join("\n\n");
3466
+ const aggregatedOutput = aggregateParallelOutputs(
3467
+ results.map((result) => ({
3468
+ agent: result.agent,
3469
+ output: result.truncation?.text || getSingleResultOutput(result),
3470
+ exitCode: result.exitCode,
3471
+ ...(result.error === undefined ? {} : { error: result.error }),
3472
+ ...(result.timedOut === undefined ? {} : { timedOut: result.timedOut }),
3473
+ })),
3474
+ (i, agent) => `=== Task ${i + 1}: ${agent} ===`,
3475
+ );
3335
3476
 
3336
3477
  const summary = `${ok}/${results.length} succeeded${downgradeNote}`;
3337
3478
  const fullContent = worktreeSuffix
3338
- ? `${summary}\n\n${perChildResults}\n\n${worktreeSuffix}`
3339
- : `${summary}\n\n${perChildResults}`;
3479
+ ? `${summary}\n\n${aggregatedOutput}\n\n${worktreeSuffix}`
3480
+ : `${summary}\n\n${aggregatedOutput}`;
3340
3481
 
3341
3482
  return {
3342
3483
  content: [{ type: "text", text: fullContent }],
@@ -3378,7 +3519,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3378
3519
  details: { mode: "single", results: [] },
3379
3520
  };
3380
3521
  }
3381
- const effectiveToolBudget = resolveEffectiveToolBudget({ runBudget: data.toolBudget, agentBudget: agentConfig.toolBudget, configBudget: data.configToolBudget });
3522
+ const effectiveToolBudget = resolveEffectiveToolBudget(omitUndefinedProperties({ runBudget: data.toolBudget, agentBudget: agentConfig.toolBudget, configBudget: data.configToolBudget }));
3382
3523
  if (effectiveToolBudget.error) return toExecutionErrorResult(params, new Error(effectiveToolBudget.error), data.contextPolicy.contextSummary);
3383
3524
 
3384
3525
  const parentModel = data.parentModel;
@@ -3391,16 +3532,17 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3391
3532
  parentModel,
3392
3533
  availableModels,
3393
3534
  currentProvider,
3394
- { scope: data.modelScope },
3535
+ data.modelScope === undefined ? {} : { scope: data.modelScope },
3395
3536
  );
3396
3537
  let skillOverride: string[] | false | undefined = normalizeSkillInput(params.skill);
3397
3538
  const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
3398
3539
  let effectiveOutput = normalizeSingleOutputOverride(rawOutput, agentConfig.output);
3540
+ const effectiveOutputMode = params.outputMode ?? "inline";
3399
3541
  const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
3400
3542
  const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
3401
3543
 
3402
3544
  if (params.clarify === true && ctx.hasUI) {
3403
- const behavior = resolveStepBehavior(agentConfig, { output: effectiveOutput, skills: skillOverride });
3545
+ const behavior = resolveStepBehavior(agentConfig, omitUndefinedProperties({ output: effectiveOutput, skills: skillOverride }));
3404
3546
  const availableSkills = discoverAvailableSkills(effectiveCwd);
3405
3547
 
3406
3548
  const result = await ctx.ui.custom<ChainClarifyResult>(
@@ -3427,7 +3569,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3427
3569
 
3428
3570
  task = result.templates[0]!;
3429
3571
  const override = result.behaviorOverrides[0];
3430
- if (override?.model !== undefined) modelOverride = resolveEffectiveSubagentModel(override.model, agentConfig.model, parentModel, availableModels, currentProvider, { scope: data.modelScope });
3572
+ if (override?.model !== undefined) modelOverride = resolveEffectiveSubagentModel(override.model, agentConfig.model, parentModel, availableModels, currentProvider, data.modelScope === undefined ? {} : { scope: data.modelScope });
3431
3573
  if (override?.output !== undefined) effectiveOutput = normalizeSingleOutputOverride(override.output, agentConfig.output);
3432
3574
  if (override?.skills !== undefined) skillOverride = override.skills;
3433
3575
 
@@ -3440,17 +3582,18 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3440
3582
  };
3441
3583
  }
3442
3584
  const id = randomUUID();
3443
- const asyncCtx = {
3585
+ const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
3444
3586
  pi: deps.pi,
3445
3587
  cwd: ctx.cwd,
3446
- currentSessionId: deps.state.currentSessionId!,
3447
- parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
3588
+ currentSessionId: data.parentSessionId!,
3589
+ parentSessionId: data.parentPiSessionId,
3448
3590
  currentModelProvider: parentModel?.provider,
3449
3591
  currentModel: parentModel,
3450
3592
  modelScope: data.modelScope,
3451
3593
  interactive: ctx.hasUI,
3452
- };
3453
- return executeAsyncSingle(id, {
3594
+ permissions: deps.config.permissions,
3595
+ });
3596
+ return executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
3454
3597
  agent: params.agent!,
3455
3598
  task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(task) : task,
3456
3599
  goal: task,
@@ -3467,7 +3610,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3467
3610
  context: contextPolicy.contextForAgent(params.agent!),
3468
3611
  skills: skillOverride === false ? [] : skillOverride,
3469
3612
  output: effectiveOutput,
3470
- outputMode: params.outputMode,
3613
+ outputMode: effectiveOutputMode,
3471
3614
  outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
3472
3615
  modelOverride,
3473
3616
  thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
@@ -3488,7 +3631,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3488
3631
  toolBudget: effectiveToolBudget.toolBudget,
3489
3632
  usageBudget: data.usageBudget,
3490
3633
  allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
3491
- });
3634
+ }));
3492
3635
  }
3493
3636
  }
3494
3637
 
@@ -3496,16 +3639,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3496
3639
  task = wrapForkTask(task);
3497
3640
  }
3498
3641
  const cleanTask = task;
3499
- // Omitted output resolves to a generated per-run durable path
3500
- // (`<singleRunOutputBaseDir>/<runId>/result.md`); omitted outputMode resolves to
3501
- // file-only whenever an output path is active. Explicit paths, `output: false`,
3502
- // and explicit `outputMode: "inline"` are preserved exactly.
3503
- const outputPath = typeof effectiveOutput === "string"
3504
- ? resolveSingleOutputPath(effectiveOutput, ctx.cwd, effectiveCwd, resolveSingleRunOutputBaseDir(deps, artifactsDir, runId))
3505
- : effectiveOutput === false
3506
- ? undefined
3507
- : resolveGeneratedSingleOutputPath(deps, artifactsDir, runId);
3508
- const effectiveOutputMode = params.outputMode ?? (outputPath ? "file-only" : "inline");
3642
+ const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, effectiveCwd, resolveSingleRunOutputBaseDir(deps, artifactsDir, runId));
3509
3643
  const validationError = validateFileOnlyOutputMode(effectiveOutputMode, outputPath, `Single run (${params.agent})`);
3510
3644
  if (validationError) {
3511
3645
  return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [] } };
@@ -3526,7 +3660,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3526
3660
  const foregroundControl = deps.state.foregroundControls.get(runId);
3527
3661
  if (foregroundControl) {
3528
3662
  const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride));
3529
- beginForegroundChild(foregroundControl, {
3663
+ beginForegroundChild(foregroundControl, omitUndefinedProperties({
3530
3664
  index: 0,
3531
3665
  agent: params.agent!,
3532
3666
  description: foregroundControl.description,
@@ -3538,7 +3672,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3538
3672
  return true;
3539
3673
  },
3540
3674
  detach: () => detachForeground?.("user request") === true,
3541
- });
3675
+ }));
3542
3676
  }
3543
3677
 
3544
3678
  const forwardSingleUpdate = onUpdate
@@ -3549,9 +3683,10 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3549
3683
  : undefined;
3550
3684
 
3551
3685
  const deadlineAt = data.deadlineAt ?? (data.timeoutMs !== undefined ? Date.now() + data.timeoutMs : undefined);
3552
- let r: Awaited<ReturnType<typeof runSync>>;
3686
+ let r: Awaited<ReturnType<typeof runSync>> | undefined;
3553
3687
  try {
3554
- r = await runSync(ctx.cwd, agents, params.agent!, task, {
3688
+ r = await runSync(ctx.cwd, agents, params.agent!, task, compactOptional<Parameters<typeof runSync>[4]>({
3689
+ permissions: deps.config.permissions,
3555
3690
  parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
3556
3691
  context: data.contextPolicy.contextForAgent(params.agent!),
3557
3692
  cwd: effectiveCwd,
@@ -3593,7 +3728,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3593
3728
  onDetachedExit: (result) => {
3594
3729
  try {
3595
3730
  try {
3596
- updateRememberedForegroundChild(deps.state, { runId, mode: "single", cwd: effectiveCwd, sessionId: data.parentSessionId, index: 0, result, events: deps.pi.events });
3731
+ updateRememberedForegroundChild(deps.state, { runId, mode: "single", cwd: effectiveCwd, sessionId: data.parentSessionId, index: 0, result, events: deps.pi.events, notify: params.workflowParentRunId === undefined });
3597
3732
  } catch {
3598
3733
  // Remembered foreground state is best-effort; run history and cleanup must still complete.
3599
3734
  }
@@ -3617,7 +3752,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3617
3752
  toolBudget: effectiveToolBudget.toolBudget,
3618
3753
  capabilityCeiling: data.capabilityCeiling,
3619
3754
  allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
3620
- });
3755
+ }));
3621
3756
  } finally {
3622
3757
  // An attached runSync rejection still owns its child and structured runtime.
3623
3758
  // A successful detached receipt transfers both to onDetachedExit while the
@@ -3635,7 +3770,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3635
3770
  if (r.artifactPaths) allArtifactPaths.push(r.artifactPaths);
3636
3771
 
3637
3772
  const fullOutput = getSingleResultOutput(r);
3638
- const finalizedOutput = finalizeSingleOutput({
3773
+ const finalizedOutput = finalizeSingleOutput(omitUndefinedProperties({
3639
3774
  fullOutput,
3640
3775
  truncatedOutput: r.truncation?.text,
3641
3776
  outputPath,
@@ -3644,14 +3779,13 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3644
3779
  savedPath: r.savedOutputPath,
3645
3780
  outputReference: r.outputReference,
3646
3781
  saveError: r.outputSaveError,
3647
- error: r.error,
3648
- });
3782
+ }));
3649
3783
  if (foregroundControl) {
3650
3784
  updateForegroundNestedProjection(foregroundControl);
3651
3785
  attachRootChildrenToSteps(runId, [r], foregroundControl.nestedChildren);
3652
3786
  }
3653
3787
  const totalCost = sumResultsCost([r]);
3654
- const details = compactForegroundDetails({
3788
+ const details = compactForegroundDetails(compactOptional<Details>({
3655
3789
  mode: "single",
3656
3790
  runId,
3657
3791
  timeoutMs: data.timeoutMs,
@@ -3664,10 +3798,11 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3664
3798
  totalChildUsage: sumResultsUsage([r]),
3665
3799
  totalCost,
3666
3800
  usageBudget: usageBudgetState(data.usageBudget, totalCost),
3667
- });
3801
+ }));
3668
3802
  rememberForegroundRun(deps.state, { runId, mode: "single", cwd: effectiveCwd, sessionId: data.parentSessionId, results: details.results });
3669
3803
 
3670
- if (!r.detached && !r.interrupted) {
3804
+ const suppressRoutineResultIntercom = shouldSuppressRoutineResultIntercom({ suppressRoutineResultIntercom: params.suppressRoutineResultIntercom, results: [r] });
3805
+ if (!r.detached && !r.interrupted && !suppressRoutineResultIntercom) {
3671
3806
  if (foregroundControl) updateForegroundNestedProjection(foregroundControl);
3672
3807
  const intercomReceipt = await maybeBuildForegroundIntercomReceipt({
3673
3808
  pi: deps.pi,
@@ -3712,7 +3847,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3712
3847
  };
3713
3848
  }
3714
3849
 
3715
- function inferExecutionMode(params: SubagentParamsLike): SubagentRunMode {
3850
+ function inferExecutionMode(params: SubagentParamsLike): Details["mode"] {
3851
+ if (params.workflowScript !== undefined) return "workflow";
3716
3852
  if ((params.chain?.length ?? 0) > 0) return "chain";
3717
3853
  if ((params.tasks?.length ?? 0) > 0) return "parallel";
3718
3854
  return "single";
@@ -3729,6 +3865,84 @@ function duplicateSubagentCallResult(params: SubagentParamsLike): AgentToolResul
3729
3865
  };
3730
3866
  }
3731
3867
 
3868
+ function workflowChildResult(key: string, result: AgentToolResult<Details>): WorkflowScriptChildResult {
3869
+ const output = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n");
3870
+ const artifactPaths = new Set<string>();
3871
+ if (result.details.asyncDir) artifactPaths.add(result.details.asyncDir);
3872
+ if (result.details.parallelHandoff?.path) artifactPaths.add(result.details.parallelHandoff.path);
3873
+ for (const child of result.details.results) {
3874
+ if (child.savedOutputPath) artifactPaths.add(child.savedOutputPath);
3875
+ if (child.outputReference?.path) artifactPaths.add(child.outputReference.path);
3876
+ if (child.sessionFile) artifactPaths.add(child.sessionFile);
3877
+ }
3878
+ const structured = result.details.results.map((child) => child.structuredOutput).filter((value) => value !== undefined);
3879
+ return {
3880
+ key,
3881
+ ok: result.isError !== true,
3882
+ ...(result.details.runId || result.details.asyncId ? { runId: result.details.runId ?? result.details.asyncId } : {}),
3883
+ output,
3884
+ ...(result.isError === true ? { error: output || "Child run failed." } : {}),
3885
+ ...(structured.length === 1 ? { structuredOutput: structured[0] } : structured.length > 1 ? { structuredOutput: structured } : {}),
3886
+ artifactPaths: [...artifactPaths],
3887
+ results: result.details.results,
3888
+ };
3889
+ }
3890
+
3891
+ function prepareWorkflowChildParams(params: SubagentParamsLike): SubagentParamsLike {
3892
+ if (params.worktree !== true || !params.agent) return params;
3893
+ const {
3894
+ agent,
3895
+ task = "",
3896
+ model,
3897
+ skill,
3898
+ output,
3899
+ outputMode,
3900
+ outputSchema,
3901
+ acceptance,
3902
+ agentContract,
3903
+ toolBudget,
3904
+ ...runParams
3905
+ } = params;
3906
+ return {
3907
+ ...runParams,
3908
+ worktree: true,
3909
+ tasks: [{
3910
+ agent,
3911
+ task,
3912
+ ...(model !== undefined ? { model } : {}),
3913
+ ...(skill !== undefined ? { skill } : {}),
3914
+ ...(output !== undefined ? { output } : {}),
3915
+ ...(outputMode !== undefined ? { outputMode } : {}),
3916
+ ...(outputSchema !== undefined ? { outputSchema } : {}),
3917
+ ...(acceptance !== undefined ? { acceptance } : {}),
3918
+ ...(agentContract !== undefined ? { agentContract } : {}),
3919
+ ...(toolBudget !== undefined ? { toolBudget } : {}),
3920
+ }],
3921
+ };
3922
+ }
3923
+
3924
+ function formatWorkflowValue(value: unknown): string {
3925
+ if (value === undefined) return "(undefined)";
3926
+ if (typeof value === "string") return value;
3927
+ try {
3928
+ return JSON.stringify(value, null, 2);
3929
+ } catch {
3930
+ return String(value);
3931
+ }
3932
+ }
3933
+
3934
+ function workflowChatProgressUpdate(
3935
+ runId: string,
3936
+ chatProgress: WorkflowChatProgressProjection,
3937
+ workflow: NonNullable<Details["workflow"]>,
3938
+ ): AgentToolResult<Details> | undefined {
3939
+ if (chatProgress.mode !== "live-card") return undefined;
3940
+ return {
3941
+ content: [{ type: "text", text: "Workflow running." }],
3942
+ details: { mode: "workflow", runId, results: [], workflow, chatProgress },
3943
+ };
3944
+ }
3945
+
3732
3946
  function omitExecutionModeActionAlias(params: SubagentParamsLike): SubagentParamsLike {
3733
3947
  const action = params.action?.toLowerCase();
3734
3948
  if (action === "single" && (params.agent !== undefined || params.task !== undefined)) {
@@ -3744,6 +3958,36 @@ function omitExecutionModeActionAlias(params: SubagentParamsLike): SubagentParam
3744
3958
  return params;
3745
3959
  }
3746
3960
 
3961
+ function createScheduledOwnerState(source: SubagentState, ownerSessionId: string, ctx: ExtensionContext): SubagentState {
3962
+ const ownerSpawns = source.subagentSpawns?.sessionId === ownerSessionId
3963
+ ? {
3964
+ ...source.subagentSpawns,
3965
+ grantHistory: [...(source.subagentSpawns.grantHistory ?? [])],
3966
+ }
3967
+ : undefined;
3968
+ return {
3969
+ ...source,
3970
+ baseCwd: ctx.cwd,
3971
+ currentSessionId: ownerSessionId,
3972
+ parentSessionFile: ctx.sessionManager.getSessionFile() ?? null,
3973
+ subagentInProgress: false,
3974
+ ...(ownerSpawns ? { subagentSpawns: ownerSpawns } : { subagentSpawns: undefined }),
3975
+ asyncJobs: new Map(),
3976
+ fleetJobs: new Map(),
3977
+ foregroundRuns: new Map(),
3978
+ foregroundControls: new Map(),
3979
+ lastForegroundControlId: null,
3980
+ cleanupTimers: new Map(),
3981
+ lastUiContext: null,
3982
+ poller: null,
3983
+ completionSeen: new Map(),
3984
+ watcher: null,
3985
+ watcherRestartTimer: null,
3986
+ waitSubscriptions: new Map(),
3987
+ workflowControllers: new Map(),
3988
+ };
3989
+ }
3990
+
3747
3991
  export function createSubagentExecutor(deps: ExecutorDeps): {
3748
3992
  execute: (
3749
3993
  id: string,
@@ -3764,34 +4008,319 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
3764
4008
  onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
3765
4009
  ctx: ExtensionContext,
3766
4010
  ) => Promise<AgentToolResult<Details>>;
4011
+ /** Scheduled launches retain their owning context without replacing the live active session. */
4012
+ executeScheduled: (
4013
+ id: string,
4014
+ params: SubagentParamsLike,
4015
+ signal: AbortSignal,
4016
+ ctx: ExtensionContext,
4017
+ ) => Promise<AgentToolResult<Details>>;
3767
4018
  } {
3768
4019
  const delegatedThinkingOverrides = new WeakMap<object, AgentConfig["thinking"]>();
3769
4020
  const delegatedZeroToolBudgets = new WeakSet<object>();
4021
+ const scheduledOwnerExecutors = new Map<string, ReturnType<typeof createSubagentExecutor>>();
3770
4022
  const execute = async (
3771
4023
  _id: string,
3772
4024
  params: SubagentParamsLike,
3773
4025
  signal: AbortSignal,
3774
4026
  onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
3775
4027
  ctx: ExtensionContext,
4028
+ preserveActiveSession = false,
3776
4029
  ): Promise<AgentToolResult<Details>> => {
3777
4030
  const delegatedThinkingOverride = delegatedThinkingOverrides.get(params);
3778
4031
  const allowZeroToolBudget = delegatedZeroToolBudgets.has(params);
3779
- deps.state.baseCwd = ctx.cwd;
4032
+ if (!preserveActiveSession) deps.state.baseCwd = ctx.cwd;
3780
4033
  deps.state.foregroundRuns ??= new Map();
3781
4034
  deps.state.foregroundControls ??= new Map();
3782
4035
  deps.state.lastForegroundControlId ??= null;
3783
4036
  const requestParams = omitExecutionModeActionAlias(params);
3784
- const requestCwd = resolveRequestedCwd(ctx.cwd, requestParams.cwd);
3785
- const paramsWithResolvedCwd = requestParams.cwd === undefined ? requestParams : { ...requestParams, cwd: requestCwd };
4037
+ if (requestParams.workflowScript !== undefined) {
4038
+ const invalidMode = requestParams.action !== undefined || requestParams.agent !== undefined || requestParams.step !== undefined || requestParams.tasks !== undefined || requestParams.chain !== undefined;
4039
+ if (invalidMode) {
4040
+ return { content: [{ type: "text", text: "workflowScript is its own execution mode; do not combine it with action, agent, step, tasks, or chain." }], isError: true, details: { mode: "workflow", results: [] } };
4041
+ }
4042
+ if (requestParams.clarify === true) {
4043
+ return { content: [{ type: "text", text: "workflowScript does not support clarify UI." }], isError: true, details: { mode: "workflow", results: [] } };
4044
+ }
4045
+ const timeout = requestParams.timeoutMs ?? requestParams.maxRuntimeMs ?? DEFAULT_FOREGROUND_TIMEOUT_MS;
4046
+ const workflowUsageBudget = validateUsageBudgetConfig(requestParams.usageBudget ?? deps.config.usageBudget, requestParams.usageBudget ? "usageBudget" : "config.usageBudget");
4047
+ if (workflowUsageBudget.error) return buildRequestedModeError(requestParams, workflowUsageBudget.error);
4048
+ const workflowCwd = resolveRequestedCwd(ctx.cwd, requestParams.cwd);
4049
+ const chatProgressResult = resolveWorkflowChatProgress({ requested: requestParams.chatProgress, parentCwd: ctx.cwd, workflowCwd, background: requestParams.async !== false });
4050
+ if (chatProgressResult.error) return { content: [{ type: "text", text: chatProgressResult.error }], isError: true, details: { mode: "workflow", results: [] } };
4051
+ const chatProgress = chatProgressResult.projection!;
4052
+ if (requestParams.async !== false) {
4053
+ const workflowRunId = _id;
4054
+ const asyncDir = path.join(DIRS.async, workflowRunId);
4055
+ const resultPath = path.join(DIRS.results, `${workflowRunId}.json`);
4056
+ const statusPath = path.join(asyncDir, "status.json");
4057
+ const eventsPath = path.join(asyncDir, "events.jsonl");
4058
+ const startedAt = Date.now();
4059
+ const currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
4060
+ fs.mkdirSync(asyncDir, { recursive: true });
4061
+ fs.mkdirSync(DIRS.results, { recursive: true });
4062
+ const controller = new AbortController();
4063
+ deps.state.workflowControllers ??= new Map();
4064
+ deps.state.workflowControllers.set(workflowRunId, controller);
4065
+ let status: AsyncStatus = {
4066
+ runId: workflowRunId,
4067
+ sessionId: currentSessionId ?? undefined,
4068
+ mode: "workflow",
4069
+ state: "running",
4070
+ startedAt,
4071
+ lastUpdate: startedAt,
4072
+ deadlineAt: startedAt + timeout,
4073
+ timeoutMs: timeout,
4074
+ cwd: ctx.cwd,
4075
+ pid: process.pid,
4076
+ steps: [],
4077
+ workflow: { trace: [], emits: [], console: [] },
4078
+ };
4079
+ const appendWorkflowEvent = (event: Record<string, unknown>) => fs.appendFileSync(eventsPath, `${JSON.stringify({ ts: Date.now(), runId: workflowRunId, ...event })}\n`, "utf-8");
4080
+ const persist = () => {
4081
+ status.lastUpdate = Date.now();
4082
+ writeAtomicJson(statusPath, status);
4083
+ const job = deps.state.asyncJobs.get(workflowRunId);
4084
+ if (job) {
4085
+ job.status = status.state;
4086
+ job.updatedAt = status.lastUpdate;
4087
+ if (status.steps) {
4088
+ job.steps = status.steps.map((step, index) => ({ ...step, index }));
4089
+ job.agents = status.steps.map((step) => step.agent);
4090
+ } else {
4091
+ delete job.steps;
4092
+ delete job.agents;
4093
+ }
4094
+ job.workflow = status.workflow;
4095
+ }
4096
+ };
4097
+ const workflowJob: AsyncJobState = { asyncId: workflowRunId, asyncDir, cwd: ctx.cwd, status: "running", sessionId: currentSessionId ?? undefined, mode: "workflow", agents: [], steps: [], startedAt, updatedAt: startedAt, timeoutMs: timeout, deadlineAt: startedAt + timeout, workflow: status.workflow };
4098
+ deps.state.asyncJobs.set(workflowRunId, workflowJob);
4099
+ deps.state.fleetJobs ??= new Map();
4100
+ deps.state.fleetJobs.set(workflowRunId, workflowJob);
4101
+ persist();
4102
+ appendWorkflowEvent({ type: "subagent.workflow.started" });
4103
+ const { workflowScript, async: _workflowAsync, chatProgress: _chatProgress, ...workflowRequest } = requestParams;
4104
+ void Promise.resolve().then(async () => {
4105
+ const workflowResults: SingleResult[] = [];
4106
+ const { action: _action, agent: _agent, task: _task, tasks: _tasks, chain: _chain, concurrency: _concurrency, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, ...workflowChildDefaults } = workflowRequest;
4107
+ const updateTrace = (trace: NonNullable<Details["workflow"]>["trace"]) => {
4108
+ status.workflow = { ...(status.workflow ?? { emits: [], console: [] }), trace };
4109
+ for (const entry of trace.filter((candidate) => candidate.operation === "run")) {
4110
+ const existing = status.steps?.find((step) => step.workflowKey === entry.key);
4111
+ if (entry.state === "reused" && existing) continue;
4112
+ const mapped = entry.state === "started" || entry.state === "reused" ? "running" : entry.state === "completed" ? "completed" : "failed";
4113
+ if (existing) {
4114
+ existing.status = mapped;
4115
+ if (entry.error === undefined) delete existing.error;
4116
+ else existing.error = entry.error;
4117
+ if (entry.durationMs === undefined) delete existing.durationMs;
4118
+ else existing.durationMs = entry.durationMs;
4119
+ } else {
4120
+ status.steps?.push({ agent: entry.key, label: entry.key, workflowKey: entry.key, parentWorkflowRunId: workflowRunId, status: mapped });
4121
+ }
4122
+ }
4123
+ persist();
4124
+ appendWorkflowEvent({ type: "subagent.workflow.trace", trace });
4125
+ };
4126
+ try {
4127
+ const workflow = await runWorkflowScript({
4128
+ script: workflowScript,
4129
+ timeoutMs: timeout,
4130
+ signal: controller.signal,
4131
+ onTrace: updateTrace,
4132
+ onEmit: (emits) => {
4133
+ // Each emit is validated at the host boundary in runWorkflowScript before onEmit fires.
4134
+ status.workflow = { ...(status.workflow ?? { trace: [], console: [] }), emits };
4135
+ persist();
4136
+ appendWorkflowEvent({ type: "subagent.workflow.emit", value: emits.at(-1) });
4137
+ },
4138
+ launch: async (key, childParams, workflowSignal) => {
4139
+ if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
4140
+ const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
4141
+ if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
4142
+ const childRequest = prepareWorkflowChildParams({ ...workflowChildDefaults, ...childParams, workflowParentRunId: workflowRunId, workflowKey: key } as SubagentParamsLike);
4143
+ const result = await execute(randomUUID(), childRequest, workflowSignal, undefined, ctx, preserveActiveSession);
4144
+ workflowResults.push(...result.details.results);
4145
+ const child = workflowChildResult(key, result);
4146
+ if (result.details.asyncId) {
4147
+ const childJob = deps.state.asyncJobs.get(result.details.asyncId);
4148
+ if (childJob) { childJob.parentWorkflowRunId = workflowRunId; childJob.workflowKey = key; }
4149
+ }
4150
+ return child;
4151
+ },
4152
+ status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession)),
4153
+ });
4154
+ const returnPreview = formatWorkflowValue(workflow.value).slice(0, 1_000);
4155
+ const emitPreview = workflow.emits.length > 0 ? ` Emitted: ${workflow.emits.map(formatWorkflowValue).join(", ").slice(0, 1_000)}` : "";
4156
+ const summary = `Workflow completed with ${workflow.children.length} child run(s). Return: ${returnPreview}${emitPreview} Trace: ${workflow.trace.length} event(s).`;
4157
+ const workflowUsage = sumResultsUsage(workflowResults);
4158
+ status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console }, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
4159
+ persist();
4160
+ appendWorkflowEvent({ type: "subagent.workflow.completed", state: "complete" });
4161
+ writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary, output: summary, results: workflow.children.map((child) => ({ agent: child.key, output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: ctx.cwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
4162
+ } catch (error) {
4163
+ const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
4164
+ const stopped = controller.signal.aborted;
4165
+ status = compactOptional<AsyncStatus>({ ...status, state: stopped ? "stopped" : "failed", stopped: stopped || undefined, error: error instanceof Error ? error.message : String(error), endedAt: Date.now(), workflow: { trace: partial.trace, emits: partial.emits, console: partial.console } });
4166
+ persist();
4167
+ appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, error: status.error });
4168
+ writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, agent: "workflow", mode: "workflow", success: false, state: status.state, summary: status.error, error: status.error, stopped: status.stopped, results: partial.children.map((child) => ({ agent: child.key, output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: ctx.cwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
4169
+ } finally {
4170
+ deps.state.workflowControllers?.delete(workflowRunId);
4171
+ }
4172
+ });
4173
+ return {
4174
+ content: [{ type: "text", text: formatAsyncStartedMessage(`Async workflow [${workflowRunId}]`, ctx.hasUI === true) }],
4175
+ details: { mode: "workflow", runId: workflowRunId, asyncId: workflowRunId, asyncDir, results: [], chatProgress },
4176
+ };
4177
+ }
4178
+ const { workflowScript: _workflowScript, action: _action, agent: _agent, task: _task, tasks: _tasks, chain: _chain, concurrency: _concurrency, async: _async, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, chatProgress: _chatProgress, ...workflowChildDefaults } = requestParams;
4179
+ const workflowResults: SingleResult[] = [];
4180
+ let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [] };
4181
+ const sendWorkflowProgress = () => {
4182
+ const update = workflowChatProgressUpdate(_id, chatProgress, liveWorkflow);
4183
+ if (update) onUpdate?.(update);
4184
+ };
4185
+ try {
4186
+ const workflow = await runWorkflowScript({
4187
+ script: requestParams.workflowScript,
4188
+ timeoutMs: timeout,
4189
+ signal,
4190
+ onTrace: (trace) => {
4191
+ liveWorkflow = { ...liveWorkflow, trace };
4192
+ sendWorkflowProgress();
4193
+ },
4194
+ onEmit: (emits) => {
4195
+ liveWorkflow = { ...liveWorkflow, emits };
4196
+ sendWorkflowProgress();
4197
+ },
4198
+ launch: async (key, childParams, workflowSignal) => {
4199
+ if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
4200
+ const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
4201
+ if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
4202
+ const childRequest = prepareWorkflowChildParams({ ...workflowChildDefaults, ...childParams, workflowParentRunId: _id, workflowKey: key, suppressRoutineResultIntercom: chatProgress.mode === "live-card" } as SubagentParamsLike);
4203
+ const result = await execute(randomUUID(), childRequest, workflowSignal, undefined, ctx, preserveActiveSession);
4204
+ workflowResults.push(...result.details.results);
4205
+ return workflowChildResult(key, result);
4206
+ },
4207
+ status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession)),
4208
+ });
4209
+ const traceLines = workflow.trace.map((entry) => `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.durationMs !== undefined ? ` in ${entry.durationMs}ms` : ""}${entry.error ? ` — ${entry.error}` : ""}`);
4210
+ const sections = ["Workflow completed.", `Return:\n${formatWorkflowValue(workflow.value)}`];
4211
+ if (workflow.emits.length > 0) sections.push(`Emitted:\n${workflow.emits.map(formatWorkflowValue).join("\n")}`);
4212
+ if (workflow.console.length > 0) sections.push(`Console:\n${workflow.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
4213
+ if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
4214
+ return {
4215
+ content: [{ type: "text", text: sections.join("\n\n") }],
4216
+ details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflow.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console }, chatProgress }),
4217
+ };
4218
+ } catch (error) {
4219
+ const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
4220
+ const text = error instanceof Error ? error.message : String(error);
4221
+ const traceLines = partial.trace.map((entry) => `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.error ? ` — ${entry.error}` : ""}`);
4222
+ const sections = [`Workflow failed: ${text}`];
4223
+ if (partial.emits.length > 0) sections.push(`Emitted:\n${partial.emits.map(formatWorkflowValue).join("\n")}`);
4224
+ if (partial.console.length > 0) sections.push(`Console:\n${partial.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
4225
+ if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
4226
+ return {
4227
+ content: [{ type: "text", text: sections.join("\n\n") }],
4228
+ isError: true,
4229
+ details: compactOptional<Details>({ mode: "workflow", runId: _id, results: partial.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: partial.trace, emits: partial.emits, console: partial.console }, chatProgress }),
4230
+ };
4231
+ }
4232
+ }
4233
+ const directParams = prepareWorkflowChildParams(requestParams);
4234
+ const requestCwd = resolveRequestedCwd(ctx.cwd, directParams.cwd);
4235
+ const paramsWithResolvedCwd = directParams.cwd === undefined ? directParams : { ...directParams, cwd: requestCwd };
3786
4236
  const action = paramsWithResolvedCwd.action;
4237
+ let requestSessionId = "";
4238
+ let requestPiSessionId: string | undefined;
3787
4239
  let requestParentModel: ParentModel | undefined;
3788
4240
  try {
3789
- requestParentModel = rememberParentModel(deps.state, resolveCurrentSessionId(ctx.sessionManager), ctx.model);
4241
+ requestSessionId = resolveCurrentSessionId(ctx.sessionManager);
4242
+ requestPiSessionId = ctx.sessionManager.getSessionId() ?? undefined;
4243
+ requestParentModel = preserveActiveSession
4244
+ ? normalizeParentModel(ctx.model)
4245
+ : rememberParentModel(deps.state, requestSessionId, ctx.model);
3790
4246
  } catch (error) {
3791
4247
  if (action?.toLowerCase() !== "doctor") throw error;
3792
4248
  requestParentModel = normalizeParentModel(ctx.model);
3793
4249
  }
3794
4250
  if (action) {
4251
+ if (action === "worktree.discard") {
4252
+ if (deps.allowMutatingManagementActions === false) {
4253
+ return { content: [{ type: "text", text: "Action 'worktree.discard' is not available from child-safe subagent fanout mode." }], isError: true, details: { mode: "management", results: [] } };
4254
+ }
4255
+ if (!paramsWithResolvedCwd.handoffPath?.trim()) {
4256
+ return { content: [{ type: "text", text: "worktree.discard requires handoffPath from parallelHandoff.path or async status." }], isError: true, details: { mode: "management", results: [] } };
4257
+ }
4258
+ const decision = resolveAuthorityDecision({ action: "discardWorktree", ...(deps.config.authorityPolicy === undefined ? {} : { policy: deps.config.authorityPolicy }) });
4259
+ if (decision === "forbid") {
4260
+ return { content: [{ type: "text", text: "Authority policy forbids worktree discard." }], isError: true, details: { mode: "management", results: [] } };
4261
+ }
4262
+ let confirmed = decision === "auto";
4263
+ if (decision === "confirm") {
4264
+ if (!ctx.hasUI) return { content: [{ type: "text", text: "Authority policy requires user confirmation for worktree discard, but this session has no interactive UI. Preserved worktrees were not changed." }], isError: true, details: { mode: "management", results: [] } };
4265
+ confirmed = await ctx.ui.confirm("Discard preserved subagent worktrees?", `This permanently removes preserved worktrees and temporary branches recorded in:\n${paramsWithResolvedCwd.handoffPath}`);
4266
+ }
4267
+ if (!confirmed) return { content: [{ type: "text", text: "Worktree discard canceled; preserved worktrees were not changed." }], details: { mode: "management", results: [] } };
4268
+ try {
4269
+ const discarded = discardPreservedWorktrees(
4270
+ path.isAbsolute(paramsWithResolvedCwd.handoffPath) ? paramsWithResolvedCwd.handoffPath : path.resolve(requestCwd, paramsWithResolvedCwd.handoffPath),
4271
+ { kind: decision === "confirm" ? "confirmed" : "policy", ...(deps.config.authorityPolicy ? { policy: deps.config.authorityPolicy } : {}) },
4272
+ );
4273
+ return { content: [{ type: "text", text: discarded.text }], details: { mode: "management", results: [] } };
4274
+ } catch (error) {
4275
+ return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
4276
+ }
4277
+ }
4278
+ if ((HERDR_PROJECT_PANE_ACTIONS as readonly string[]).includes(action)) {
4279
+ if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
4280
+ return { content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }], isError: true, details: { mode: "management", results: [] } };
4281
+ }
4282
+ return handleHerdrProjectPaneAction(action as (typeof HERDR_PROJECT_PANE_ACTIONS)[number], paramsWithResolvedCwd, { cwd: requestCwd, signal });
4283
+ }
4284
+ if ((HERDR_INSPECTOR_ACTIONS as readonly string[]).includes(action)) {
4285
+ if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
4286
+ return { content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }], isError: true, details: { mode: "management", results: [] } };
4287
+ }
4288
+ deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
4289
+ return handleHerdrInspectorAction(action as (typeof HERDR_INSPECTOR_ACTIONS)[number], paramsWithResolvedCwd, {
4290
+ state: deps.state,
4291
+ cwd: requestCwd,
4292
+ ...(deps.config.missions ? { missions: deps.config.missions } : {}),
4293
+ ...(deps.config.authorityPolicy ? { authorityPolicy: deps.config.authorityPolicy } : {}),
4294
+ signal,
4295
+ });
4296
+ }
4297
+ if ((MISSION_ACTIONS as readonly string[]).includes(action)) {
4298
+ if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
4299
+ return {
4300
+ content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
4301
+ isError: true,
4302
+ details: { mode: "management", results: [] },
4303
+ };
4304
+ }
4305
+ const currentSessionId = deps.state.currentSessionId ?? ctx.sessionManager.getSessionId() ?? undefined;
4306
+ return handleMissionAction(action as (typeof MISSION_ACTIONS)[number], paramsWithResolvedCwd, {
4307
+ cwd: requestCwd,
4308
+ ...(deps.config.missions ? { config: deps.config.missions } : {}),
4309
+ ...(currentSessionId ? { currentSessionId } : {}),
4310
+ });
4311
+ }
4312
+ const policyAction = action === "stop" ? "stopRun" : action === "steer" ? "steerRun" : action === "schedule.create" ? "scheduleCreate" : undefined;
4313
+ if (policyAction) {
4314
+ const decision = resolveAuthorityDecision({ action: policyAction, ...(deps.config.authorityPolicy === undefined ? {} : { policy: deps.config.authorityPolicy }) });
4315
+ if (decision === "forbid") {
4316
+ return { content: [{ type: "text", text: `Authority policy forbids action '${action}'.` }], isError: true, details: { mode: "management", results: [] } };
4317
+ }
4318
+ if (decision === "confirm") {
4319
+ if (!ctx.hasUI) return { content: [{ type: "text", text: `Authority policy requires user confirmation for action '${action}', but this session has no interactive UI.` }], isError: true, details: { mode: "management", results: [] } };
4320
+ const confirmed = await ctx.ui.confirm(`Authorize subagent ${action}?`, `Authority policy requires confirmation before '${action}'.`);
4321
+ if (!confirmed) return { content: [{ type: "text", text: `Action '${action}' canceled; authority was not granted.` }], details: { mode: "management", results: [] } };
4322
+ }
4323
+ }
3795
4324
  if ((WATCHDOG_TOOL_ACTIONS as readonly string[]).includes(action)) {
3796
4325
  if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
3797
4326
  return {
@@ -3836,7 +4365,15 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
3836
4365
  details: { mode: "management", results: [], spawnBudget: preview.snapshot },
3837
4366
  };
3838
4367
  }
3839
- const confirmed = await ctx.ui.confirm(
4368
+ const authority = resolveAuthorityDecision({ action: "spawnBudgetGrant", ...(deps.config.authorityPolicy === undefined ? {} : { policy: deps.config.authorityPolicy }) });
4369
+ if (authority === "forbid") {
4370
+ return {
4371
+ content: [{ type: "text", text: "Authority policy forbids spawn budget grants." }],
4372
+ isError: true,
4373
+ details: { mode: "management", results: [], spawnBudget: preview.snapshot },
4374
+ };
4375
+ }
4376
+ const confirmed = authority === "auto" || await ctx.ui.confirm(
3840
4377
  "Grant subagent spawn budget?",
3841
4378
  `Add ${additional} launches to this logical session?\n\n${formatSpawnBudget(preview.snapshot)}\n\nUsage is not reset. Compaction keeps the same budget; a new parent session starts a fresh one.`,
3842
4379
  );
@@ -3886,7 +4423,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
3886
4423
  return {
3887
4424
  content: [{
3888
4425
  type: "text",
3889
- text: buildDoctorReport({
4426
+ text: buildDoctorReport(omitUndefinedProperties({
3890
4427
  cwd: requestCwd,
3891
4428
  config: deps.config,
3892
4429
  state: deps.state,
@@ -3897,13 +4434,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
3897
4434
  orchestratorTarget,
3898
4435
  sessionError,
3899
4436
  expandTilde: deps.expandTilde,
3900
- }),
4437
+ })),
3901
4438
  }],
3902
4439
  details: { mode: "management", results: [], spawnBudget },
3903
4440
  };
3904
4441
  }
3905
4442
  if (action === "status") {
3906
- deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
4443
+ if (!preserveActiveSession) deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
3907
4444
  const withBudget = (result: AgentToolResult<Details>) => withSpawnBudgetStatus(
3908
4445
  result,
3909
4446
  deps.state,
@@ -3914,11 +4451,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
3914
4451
  const nestedScope = nestedResolutionScopeForExecutor(deps);
3915
4452
  const sessionRoots = trustedSessionRootsForStatus(ctx, deps);
3916
4453
  if (paramsWithResolvedCwd.view === "fleet") {
3917
- return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, { state: deps.state, nested: nestedScope, sessionRoots }));
4454
+ return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots })));
3918
4455
  }
3919
4456
  if (targetRunId) {
3920
4457
  try {
3921
- const resolved = resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedScope });
4458
+ const resolved = resolveSubagentRunId(targetRunId, omitUndefinedProperties({ state: deps.state, nested: nestedScope }));
3922
4459
  if (resolved?.kind === "foreground") {
3923
4460
  const foreground = getForegroundControl(deps.state, resolved.id);
3924
4461
  if (foreground) {
@@ -3945,7 +4482,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
3945
4482
  });
3946
4483
  }
3947
4484
  }
3948
- return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, { state: deps.state, nested: nestedScope, sessionRoots }));
4485
+ return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots })));
3949
4486
  }
3950
4487
 
3951
4488
  if (action === "approve-checkpoint" || action === "reject-checkpoint") {
@@ -3955,7 +4492,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
3955
4492
  return { content: [{ type: "text", text: `action='${action}' requires id or dir.` }], isError: true, details: { mode: "management", results: [] } };
3956
4493
  }
3957
4494
  try {
3958
- const resolved = targetRunId ? resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedResolutionScopeForExecutor(deps) }) : undefined;
4495
+ const resolved = targetRunId ? resolveSubagentRunId(targetRunId, omitUndefinedProperties({ state: deps.state, nested: nestedResolutionScopeForExecutor(deps) })) : undefined;
3959
4496
  const decision = action === "approve-checkpoint" ? "approved" : "rejected";
3960
4497
  if (resolved?.kind === "foreground") {
3961
4498
  const run = deps.state.foregroundRuns?.get(resolved.id);
@@ -3966,7 +4503,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
3966
4503
  return { content: [{ type: "text", text: `Run '${resolved.id}' was not found in the active session.` }], isError: true, details: { mode: "management", results: [] } };
3967
4504
  }
3968
4505
  const decidedAt = Date.now();
3969
- const checkpoint = { ...run.checkpoint, status: decision, ...(decision === "approved" ? { approvedAt: decidedAt } : { rejectedAt: decidedAt }) };
4506
+ const checkpoint: NonNullable<Details["checkpoint"]> = decision === "approved"
4507
+ ? { ...run.checkpoint, status: "approved", approvedAt: decidedAt }
4508
+ : { ...run.checkpoint, status: "rejected", rejectedAt: decidedAt };
3970
4509
  run.checkpoint = checkpoint;
3971
4510
  run.updatedAt = decidedAt;
3972
4511
  return {
@@ -3975,7 +4514,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
3975
4514
  };
3976
4515
  }
3977
4516
  const location = paramsWithResolvedCwd.dir
3978
- ? resolveAsyncRunLocation(paramsWithResolvedCwd, ASYNC_DIR, RESULTS_DIR)
4517
+ ? resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results)
3979
4518
  : resolved?.kind === "async"
3980
4519
  ? resolved.location
3981
4520
  : undefined;
@@ -4001,7 +4540,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4001
4540
  }
4002
4541
  }
4003
4542
  if (action === "resume") {
4004
- return resumeAsyncRun({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel });
4543
+ return resumeAsyncRun(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel }));
4005
4544
  }
4006
4545
  if (action === "steer") {
4007
4546
  deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
@@ -4010,9 +4549,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4010
4549
  const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
4011
4550
  if (paramsWithResolvedCwd.dir) {
4012
4551
  try {
4013
- const location = resolveAsyncRunLocation(paramsWithResolvedCwd, ASYNC_DIR, RESULTS_DIR);
4552
+ const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
4014
4553
  const runId = location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir);
4015
- return steerAsyncRun({
4554
+ if (location.asyncDir) {
4555
+ const unsupported = externalRunnerControlError(location.asyncDir, "steer");
4556
+ if (unsupported) return unsupported;
4557
+ }
4558
+ return steerAsyncRun(compactOptional<Parameters<typeof steerAsyncRun>[0]>({
4016
4559
  state: deps.state,
4017
4560
  runId,
4018
4561
  message,
@@ -4024,10 +4567,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4024
4567
  ? {}
4025
4568
  : {
4026
4569
  recover: ({ absoluteDeadlineAt, ...limits }) =>
4027
- resumeAsyncRun({ params: { ...limits, action: "resume", id: runId, message }, requestCwd, ctx, deps, parentModel: requestParentModel, absoluteDeadlineAt }),
4570
+ resumeAsyncRun(omitUndefinedProperties({ params: { ...limits, action: "resume", id: runId, message }, requestCwd, ctx, deps, parentModel: requestParentModel, absoluteDeadlineAt })),
4028
4571
  }
4029
4572
  ),
4030
- });
4573
+ }));
4031
4574
  } catch (error) {
4032
4575
  const text = error instanceof Error ? error.message : String(error);
4033
4576
  return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
@@ -4036,15 +4579,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4036
4579
  if (!targetRunId) return { content: [{ type: "text", text: "action='steer' requires id or dir." }], isError: true, details: { mode: "management", results: [] } };
4037
4580
  let resolved: ResolvedSubagentRunId | undefined;
4038
4581
  try {
4039
- resolved = resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedResolutionScopeForExecutor(deps) });
4582
+ resolved = resolveSubagentRunId(targetRunId, omitUndefinedProperties({ state: deps.state, nested: nestedResolutionScopeForExecutor(deps) }));
4040
4583
  } catch (error) {
4041
4584
  const text = error instanceof Error ? error.message : String(error);
4042
4585
  return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
4043
4586
  }
4044
- if (resolved?.kind === "nested") return steerNestedRun({ target: resolved, message, index: paramsWithResolvedCwd.index, signal });
4587
+ if (resolved?.kind === "nested") return steerNestedRun(omitUndefinedProperties({ target: resolved, message, index: paramsWithResolvedCwd.index, signal }));
4045
4588
  if (resolved?.kind === "foreground") return { content: [{ type: "text", text: "action='steer' currently supports live async Pi child sessions only; use action='interrupt' or action='resume' for foreground runs." }], isError: true, details: { mode: "management", results: [] } };
4046
4589
  if (resolved?.kind !== "async") return { content: [{ type: "text", text: `No async run found for '${targetRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
4047
- return steerAsyncRun({
4590
+ if (resolved.location.asyncDir) {
4591
+ const unsupported = externalRunnerControlError(resolved.location.asyncDir, "steer");
4592
+ if (unsupported) return unsupported;
4593
+ }
4594
+ return steerAsyncRun(compactOptional<Parameters<typeof steerAsyncRun>[0]>({
4048
4595
  state: deps.state,
4049
4596
  runId: resolved.id,
4050
4597
  message,
@@ -4056,22 +4603,29 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4056
4603
  ? {}
4057
4604
  : {
4058
4605
  recover: ({ absoluteDeadlineAt, ...limits }) =>
4059
- resumeAsyncRun({
4606
+ resumeAsyncRun(omitUndefinedProperties({
4060
4607
  params: { ...limits, action: "resume", id: resolved!.id, message },
4061
4608
  requestCwd,
4062
4609
  ctx,
4063
4610
  deps,
4064
4611
  parentModel: requestParentModel,
4065
4612
  absoluteDeadlineAt,
4066
- }),
4613
+ })),
4067
4614
  }
4068
4615
  ),
4069
- });
4616
+ }));
4070
4617
  }
4071
4618
  if (action === "append-step") {
4072
- return appendStepToAsyncChain({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel });
4619
+ return appendStepToAsyncChain(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel }));
4073
4620
  }
4074
- if (action === "schedule" || action === "schedule-list" || action === "schedule-status" || action === "schedule-cancel") {
4621
+ if (action.startsWith("schedule.")) {
4622
+ if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
4623
+ return {
4624
+ content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
4625
+ isError: true,
4626
+ details: { mode: "management", results: [] },
4627
+ };
4628
+ }
4075
4629
  if (!deps.handleScheduledRunAction) {
4076
4630
  return {
4077
4631
  content: [{ type: "text", text: `Action '${action}' is not available in this subagent context.` }],
@@ -4083,11 +4637,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4083
4637
  }
4084
4638
  if (action === "stop") {
4085
4639
  const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
4640
+ const workflowController = targetRunId ? deps.state.workflowControllers?.get(targetRunId) : undefined;
4641
+ if (workflowController) {
4642
+ workflowController.abort(new Error("Workflow stopped by user."));
4643
+ return { content: [{ type: "text", text: `Stop requested for async workflow ${targetRunId}.` }], details: { mode: "management", results: [] } };
4644
+ }
4086
4645
  let resolved: ResolvedSubagentRunId | undefined;
4087
4646
  if (paramsWithResolvedCwd.dir) {
4088
4647
  try {
4089
- const location = resolveAsyncRunLocation(paramsWithResolvedCwd, ASYNC_DIR, RESULTS_DIR);
4090
- return stopAsyncRun(deps.state, location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir), deps.kill, location);
4648
+ const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
4649
+ const stopResult = stopAsyncRun(deps.state, location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir), deps.kill, location);
4650
+ return stopResult ?? { content: [{ type: "text", text: `No running or queued async run was found for '${targetRunId ?? paramsWithResolvedCwd.dir}'.` }], isError: true, details: { mode: "management", results: [] } };
4091
4651
  } catch (error) {
4092
4652
  const text = error instanceof Error ? error.message : String(error);
4093
4653
  return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
@@ -4095,13 +4655,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4095
4655
  }
4096
4656
  if (!targetRunId) return { content: [{ type: "text", text: "action='stop' requires id or dir." }], isError: true, details: { mode: "management", results: [] } };
4097
4657
  try {
4098
- resolved = resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedResolutionScopeForExecutor(deps) });
4658
+ resolved = resolveSubagentRunId(targetRunId, omitUndefinedProperties({ state: deps.state, nested: nestedResolutionScopeForExecutor(deps) }));
4099
4659
  } catch (error) {
4100
4660
  const message = error instanceof Error ? error.message : String(error);
4101
4661
  return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
4102
4662
  }
4103
4663
  if (resolved?.kind === "nested") return { content: [{ type: "text", text: "action='stop' supports current-session top-level async runs only." }], isError: true, details: { mode: "management", results: [] } };
4104
4664
  if (resolved?.kind === "foreground") return { content: [{ type: "text", text: "action='stop' supports async runs only. Use action='interrupt' for foreground runs." }], isError: true, details: { mode: "management", results: [] } };
4665
+ if (resolved?.kind === "async") {
4666
+ const existingStatus = readStatus(resolved.location.asyncDir ?? "");
4667
+ if (existingStatus?.mode === "workflow" && existingStatus.state === "running") {
4668
+ return { content: [{ type: "text", text: `Workflow ${resolved.id} is not controlled by this extension runtime; reload recovery cannot stop it safely.` }], isError: true, details: { mode: "management", results: [] } };
4669
+ }
4670
+ }
4105
4671
  const stopResult = stopAsyncRun(
4106
4672
  deps.state,
4107
4673
  resolved?.kind === "async" ? resolved.id : targetRunId,
@@ -4120,7 +4686,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4120
4686
  let resolved: ResolvedSubagentRunId | undefined;
4121
4687
  if (targetRunId) {
4122
4688
  try {
4123
- resolved = resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedResolutionScopeForExecutor(deps) });
4689
+ resolved = resolveSubagentRunId(targetRunId, omitUndefinedProperties({ state: deps.state, nested: nestedResolutionScopeForExecutor(deps) }));
4124
4690
  } catch (error) {
4125
4691
  const message = error instanceof Error ? error.message : String(error);
4126
4692
  return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
@@ -4132,7 +4698,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4132
4698
  const interrupted = foreground.interrupt();
4133
4699
  if (interrupted) {
4134
4700
  foreground.updatedAt = Date.now();
4135
- foreground.currentActivityState = undefined;
4701
+ delete foreground.currentActivityState;
4136
4702
  return {
4137
4703
  content: [{ type: "text", text: `Interrupt requested for foreground run ${foreground.runId}.` }],
4138
4704
  details: { mode: "management", results: [] },
@@ -4267,7 +4833,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4267
4833
  const spawnPreflight = preflightSpawnBudget(
4268
4834
  deps.state,
4269
4835
  deps.config,
4270
- deps.state.currentSessionId,
4836
+ requestSessionId,
4271
4837
  requestedSpawns,
4272
4838
  );
4273
4839
  if (spawnPreflight.error) return spawnBudgetErrorResult(spawnPreflight.error, foregroundMode);
@@ -4312,6 +4878,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4312
4878
  const requestedAsync = effectiveParams.async ?? deps.asyncByDefault;
4313
4879
  const backgroundRequestedWhileClarifying = (hasChain || hasTasks) && requestedAsync && effectiveParams.clarify === true;
4314
4880
  const effectiveAsync = requestedAsync && effectiveParams.clarify !== true;
4881
+ const selectedAgentNames = hasSingle
4882
+ ? [effectiveParams.agent!]
4883
+ : hasTasks
4884
+ ? (effectiveParams.tasks ?? []).map((task) => task.agent)
4885
+ : (effectiveParams.chain ?? []).flatMap((step) => getStepAgents(step as ChainStep));
4886
+ const externalAgent = selectedAgentNames
4887
+ .map((name) => agents.find((agent) => agent.name === name))
4888
+ .find((agent) => agent?.runner?.type === "external-cli");
4889
+ if (externalAgent && (!effectiveAsync || effectiveParams.foregroundOnly === true)) {
4890
+ return buildRequestedModeError(effectiveParams, `Agent '${externalAgent.name}' uses runner.type='external-cli', which currently supports async/background execution only. Omit async or pass async:true; clarify and foregroundOnly are unsupported.`);
4891
+ }
4315
4892
  const foregroundTimeout = resolveForegroundTimeout(
4316
4893
  effectiveParams,
4317
4894
  effectiveAsync ? undefined : DEFAULT_FOREGROUND_TIMEOUT_MS,
@@ -4319,11 +4896,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4319
4896
  if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
4320
4897
  const controlConfig = resolveControlConfig(deps.config.control, effectiveParams.control);
4321
4898
 
4322
- const artifactConfig: ArtifactConfig = {
4899
+ const artifactConfig: ArtifactConfig = omitUndefinedProperties({
4323
4900
  ...DEFAULT_ARTIFACT_CONFIG,
4324
- enabled: effectiveParams.artifacts === true,
4901
+ enabled: effectiveParams.artifacts !== false,
4325
4902
  dir: deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir,
4326
- };
4903
+ });
4327
4904
  const artifactsDir = getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
4328
4905
 
4329
4906
  let sessionRoot: string;
@@ -4380,15 +4957,48 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4380
4957
  }, contextPolicy.contextSummary))
4381
4958
  : undefined;
4382
4959
 
4960
+ let missionBinding: MissionLaunchBinding | undefined;
4961
+ let missionWarning: string | undefined;
4962
+ const explicitMission = effectiveParams.missionId !== undefined || effectiveParams.mission !== undefined;
4963
+ try {
4964
+ missionBinding = prepareMissionLaunch({
4965
+ params: effectiveParams,
4966
+ projectRoot: effectiveCwd,
4967
+ ...(deps.config.missions ? { config: deps.config.missions } : {}),
4968
+ ownerSessionId: requestSessionId,
4969
+ });
4970
+ } catch (error) {
4971
+ if (explicitMission) return toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary);
4972
+ missionWarning = `Mission tracking unavailable: ${error instanceof Error ? error.message : String(error)}`;
4973
+ }
4974
+
4975
+ const attachMission = (result: AgentToolResult<Details>): AgentToolResult<Details> => {
4976
+ if (!missionBinding) return missionWarning ? { ...result, details: { ...result.details, missionWarning } } : result;
4977
+ try {
4978
+ return attachMissionToLaunchResult({ binding: missionBinding, result });
4979
+ } catch (error) {
4980
+ const warning = `Mission tracking unavailable after launch: ${error instanceof Error ? error.message : String(error)}`;
4981
+ if (explicitMission) {
4982
+ return {
4983
+ ...result,
4984
+ isError: true,
4985
+ content: [...result.content, { type: "text", text: warning }],
4986
+ details: { ...result.details, missionWarning: warning },
4987
+ };
4988
+ }
4989
+ return { ...result, details: { ...result.details, missionWarning: warning } };
4990
+ }
4991
+ };
4992
+
4383
4993
  const reservation = reserveSpawnBudget(
4384
4994
  deps.state,
4385
4995
  deps.config,
4386
- deps.state.currentSessionId,
4996
+ requestSessionId,
4387
4997
  requestedSpawns,
4388
4998
  );
4389
- if (reservation.error) return spawnBudgetErrorResult(reservation.error, foregroundMode);
4999
+ if (reservation.error) return attachMission(spawnBudgetErrorResult(reservation.error, foregroundMode));
4390
5000
 
4391
- const execData: ExecutionContextData = {
5001
+ const execData: ExecutionContextData = omitUndefinedProperties({
4392
5002
  params: effectiveParams,
4393
5003
  effectiveCwd,
4394
5004
  ctx,
@@ -4418,18 +5028,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4418
5028
  contextPolicy,
4419
5029
  modelScope,
4420
5030
  parentModel: requestParentModel,
4421
- parentSessionId: deps.state.currentSessionId,
4422
- capabilityCeiling: resolveCurrentSubagentCapabilityCeiling(deps.state.currentSessionId ?? undefined),
4423
- };
5031
+ parentSessionId: requestSessionId,
5032
+ parentPiSessionId: requestPiSessionId,
5033
+ capabilityCeiling: resolveCurrentSubagentCapabilityCeiling(requestSessionId),
5034
+ });
4424
5035
 
4425
5036
  const foregroundDescription = effectiveParams.task?.trim()
4426
5037
  || effectiveParams.tasks?.[0]?.task?.trim()
4427
5038
  || (effectiveParams.chain ? firstRawChainTask(effectiveParams.chain)?.trim() : undefined);
4428
5039
  const foregroundControl: ForegroundRunControl | undefined = effectiveAsync
4429
5040
  ? undefined
4430
- : {
5041
+ : compactOptional<ForegroundRunControl>({
4431
5042
  runId,
4432
- ...(deps.state.currentSessionId ? { sessionId: deps.state.currentSessionId } : {}),
5043
+ sessionId: requestSessionId,
4433
5044
  mode: foregroundMode,
4434
5045
  startedAt: Date.now(),
4435
5046
  updatedAt: Date.now(),
@@ -4443,7 +5054,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4443
5054
  schedulingOwners: 1,
4444
5055
  nestedRoute,
4445
5056
  interrupt: undefined,
4446
- };
5057
+ });
4447
5058
  if (foregroundControl) {
4448
5059
  deps.state.foregroundControls.set(runId, foregroundControl);
4449
5060
  deps.state.lastForegroundControlId = runId;
@@ -4463,21 +5074,33 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4463
5074
  const errorText = result?.isError
4464
5075
  ? result.content.find((item) => item.type === "text")?.text
4465
5076
  : undefined;
4466
- const agentsForSummary = hasTasks && effectiveParams.tasks
4467
- ? effectiveParams.tasks.map((task) => task.agent)
4468
- : hasChain && effectiveParams.chain
4469
- ? effectiveParams.chain.flatMap((step) => isParallelStep(step) ? step.parallel.map((task) => task.agent) : [(step as SequentialStep).agent])
4470
- : effectiveParams.agent ? [effectiveParams.agent] : [];
5077
+ let startedLaunches: StaticLaunchSummary[];
5078
+ try {
5079
+ startedLaunches = collectStaticLaunchSummaries({
5080
+ params: effectiveParams,
5081
+ agents,
5082
+ parentModel: requestParentModel,
5083
+ availableModels: ctx.modelRegistry.getAvailable().map(toModelInfo),
5084
+ currentProvider: requestParentModel?.provider,
5085
+ modelScope,
5086
+ thinkingOverrideForTask: forkThinkingOverrideForTask,
5087
+ dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
5088
+ });
5089
+ } catch (error) {
5090
+ console.error("Failed to resolve nested foreground launch metadata:", error);
5091
+ startedLaunches = selectedAgentNames.map((agent) => ({ agent }));
5092
+ }
5093
+ const agentsForSummary = startedLaunches.map((launch) => launch.agent);
4471
5094
  const leafIntercomTarget = intercomBridge.active && agentsForSummary[0]
4472
5095
  ? resolveSubagentIntercomTarget(runId, agentsForSummary[0], 0)
4473
5096
  : undefined;
4474
5097
  try {
4475
- writeNestedEvent(inheritedNestedRoute, {
5098
+ writeNestedEvent(inheritedNestedRoute, compactOptional<Parameters<typeof writeNestedEvent>[1]>({
4476
5099
  type,
4477
5100
  ts: now,
4478
5101
  parentRunId: nestedParentAddress.parentRunId,
4479
5102
  parentStepIndex: nestedParentAddress.parentStepIndex,
4480
- child: {
5103
+ child: compactOptional<NestedRunSummary>({
4481
5104
  id: runId,
4482
5105
  parentRunId: nestedParentAddress.parentRunId,
4483
5106
  parentStepIndex: nestedParentAddress.parentStepIndex,
@@ -4491,19 +5114,32 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4491
5114
  state,
4492
5115
  agent: agentsForSummary[0],
4493
5116
  agents: agentsForSummary,
5117
+ ...(agentsForSummary.length === 1 && (type === "subagent.nested.started" ? startedLaunches[0]?.model : details?.results[0]?.model) ? { model: type === "subagent.nested.started" ? startedLaunches[0]?.model : details?.results[0]?.model } : {}),
5118
+ ...(agentsForSummary.length === 1 && (type === "subagent.nested.started" ? startedLaunches[0]?.thinking : details?.results[0]?.thinking) ? { thinking: type === "subagent.nested.started" ? startedLaunches[0]?.thinking : details?.results[0]?.thinking } : {}),
4494
5119
  startedAt: foregroundControl?.startedAt ?? now,
4495
5120
  ...(state !== "running" ? { endedAt: now } : {}),
4496
5121
  lastUpdate: now,
4497
5122
  ...(details?.totalCost ? { totalCost: details.totalCost } : {}),
4498
5123
  ...(errorText ? { error: errorText } : {}),
4499
- ...(details?.results.length ? { steps: details.results.map((child) => ({
4500
- agent: child.agent,
4501
- status: child.interrupted || child.detached ? "paused" : child.exitCode === 0 ? "complete" : "failed",
4502
- ...(child.sessionFile ? { sessionFile: child.sessionFile } : {}),
4503
- ...(child.error ? { error: child.error } : {}),
4504
- })) } : {}),
4505
- },
4506
- });
5124
+ ...(type === "subagent.nested.started"
5125
+ ? { steps: startedLaunches.map((launch) => ({
5126
+ agent: launch.agent,
5127
+ status: "running" as const,
5128
+ ...(launch.model ? { model: launch.model } : {}),
5129
+ ...(launch.thinking ? { thinking: launch.thinking } : {}),
5130
+ })) }
5131
+ : details?.results.length
5132
+ ? { steps: details.results.map((child) => ({
5133
+ agent: child.agent,
5134
+ status: child.interrupted || child.detached ? "paused" as const : child.exitCode === 0 ? "complete" as const : "failed" as const,
5135
+ ...(child.model ? { model: child.model } : {}),
5136
+ ...(child.thinking ? { thinking: child.thinking } : {}),
5137
+ ...(child.sessionFile ? { sessionFile: child.sessionFile } : {}),
5138
+ ...(child.error ? { error: child.error } : {}),
5139
+ })) }
5140
+ : {}),
5141
+ }),
5142
+ }));
4507
5143
  } catch (error) {
4508
5144
  console.error("Failed to emit nested foreground status event:", error);
4509
5145
  }
@@ -4512,7 +5148,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4512
5148
  let nestedForegroundStarted = false;
4513
5149
  try {
4514
5150
  const asyncResult = runAsyncPath(execData, deps);
4515
- if (asyncResult) return withResolvedContext(withForkThinkingNotes(asyncResult, forkThinkingDowngrades), contextPolicy.contextSummary);
5151
+ if (asyncResult) return attachMission(withResolvedContext(withForkThinkingNotes(asyncResult, forkThinkingDowngrades), contextPolicy.contextSummary));
4516
5152
  if (foregroundControl) {
4517
5153
  writeNestedForegroundEvent("subagent.nested.started");
4518
5154
  nestedForegroundStarted = true;
@@ -4520,22 +5156,22 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4520
5156
  if (hasChain && effectiveParams.chain) {
4521
5157
  const result = await runChainPath(execData, deps);
4522
5158
  writeNestedForegroundEvent("subagent.nested.completed", result);
4523
- return withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary);
5159
+ return attachMission(withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary));
4524
5160
  }
4525
5161
  if (hasTasks && effectiveParams.tasks) {
4526
5162
  const result = await runParallelPath(execData, deps);
4527
5163
  writeNestedForegroundEvent("subagent.nested.completed", result);
4528
- return withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary);
5164
+ return attachMission(withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary));
4529
5165
  }
4530
5166
  if (hasSingle) {
4531
5167
  const result = await runSinglePath(execData, deps);
4532
5168
  writeNestedForegroundEvent("subagent.nested.completed", result);
4533
- return withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary);
5169
+ return attachMission(withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary));
4534
5170
  }
4535
5171
  } catch (error) {
4536
5172
  const errorResult = withForkThinkingNotes(toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary), forkThinkingDowngrades);
4537
5173
  if (nestedForegroundStarted) writeNestedForegroundEvent("subagent.nested.completed", errorResult);
4538
- return errorResult;
5174
+ return attachMission(errorResult);
4539
5175
  } finally {
4540
5176
  if (foregroundControl) {
4541
5177
  settleForegroundSchedulingOwner(foregroundControl);
@@ -4593,5 +5229,23 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4593
5229
  return execute(id, delegatedParams, signal, onUpdate, ctx);
4594
5230
  };
4595
5231
 
4596
- return { execute: executeWithSingleDispatchGuard, executeDelegated };
5232
+ const executeScheduled = (
5233
+ id: string,
5234
+ params: SubagentParamsLike,
5235
+ signal: AbortSignal,
5236
+ ctx: ExtensionContext,
5237
+ ) => {
5238
+ const ownerSessionId = resolveCurrentSessionId(ctx.sessionManager);
5239
+ let ownerExecutor = scheduledOwnerExecutors.get(ownerSessionId);
5240
+ if (!ownerExecutor) {
5241
+ ownerExecutor = createSubagentExecutor({
5242
+ ...deps,
5243
+ state: createScheduledOwnerState(deps.state, ownerSessionId, ctx),
5244
+ });
5245
+ scheduledOwnerExecutors.set(ownerSessionId, ownerExecutor);
5246
+ }
5247
+ return ownerExecutor.execute(id, params, signal, undefined, ctx);
5248
+ };
5249
+
5250
+ return { execute: executeWithSingleDispatchGuard, executeDelegated, executeScheduled };
4597
5251
  }