@selesai/code 0.13.15 → 0.13.18

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 (473) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +2 -14
  3. package/dist/bun/cli.d.ts +3 -1
  4. package/dist/bun/cli.js +3 -9
  5. package/dist/bun/{register-bedrock.js → runtime-setup.js} +5 -0
  6. package/dist/bun/sandbox-env-setup.d.ts +1 -0
  7. package/dist/bun/sandbox-env-setup.js +3 -0
  8. package/dist/cli/auth-command.d.ts +23 -0
  9. package/dist/cli/auth-command.js +102 -0
  10. package/dist/cli/config-selector.js +2 -1
  11. package/dist/cli/setup.d.ts +1 -0
  12. package/dist/cli/setup.js +11 -0
  13. package/dist/cli.js +2 -14
  14. package/dist/config.d.ts +3 -2
  15. package/dist/config.js +9 -4
  16. package/dist/core/agent-session-runtime.js +15 -7
  17. package/dist/core/agent-session.d.ts +2 -6
  18. package/dist/core/agent-session.js +15 -4
  19. package/dist/core/compaction/branch-summarization.d.ts +1 -1
  20. package/dist/core/compaction/branch-summarization.js +2 -1
  21. package/dist/core/extensions/loader.js +3 -0
  22. package/dist/core/extensions/types.d.ts +16 -2
  23. package/dist/core/http-dispatcher.js +2 -0
  24. package/dist/core/keybindings.d.ts +5 -0
  25. package/dist/core/keybindings.js +4 -0
  26. package/dist/core/messages.d.ts +1 -1
  27. package/dist/core/model-config.d.ts +15 -0
  28. package/dist/core/model-config.js +3 -0
  29. package/dist/core/model-runtime.d.ts +1 -0
  30. package/dist/core/model-runtime.js +5 -2
  31. package/dist/core/provider-composer.js +8 -2
  32. package/dist/core/session-export.d.ts +3 -0
  33. package/dist/core/session-export.js +31 -0
  34. package/dist/core/session-manager.d.ts +3 -2
  35. package/dist/core/session-manager.js +40 -13
  36. package/dist/core/skills.d.ts +1 -1
  37. package/dist/core/skills.js +4 -2
  38. package/dist/core/system-prompt.js +10 -10
  39. package/dist/core/tools/bash.js +3 -117
  40. package/dist/core/tools/edit.d.ts +1 -12
  41. package/dist/core/tools/edit.js +5 -163
  42. package/dist/core/tools/find.js +4 -55
  43. package/dist/core/tools/grep.js +4 -60
  44. package/dist/core/tools/ls.js +4 -49
  45. package/dist/core/tools/read.d.ts +4 -0
  46. package/dist/core/tools/read.js +10 -122
  47. package/dist/core/tools/renderers/bash.d.ts +11 -0
  48. package/dist/core/tools/renderers/bash.js +129 -0
  49. package/dist/core/tools/renderers/edit.d.ts +22 -0
  50. package/dist/core/tools/renderers/edit.js +166 -0
  51. package/dist/core/tools/renderers/find.d.ts +9 -0
  52. package/dist/core/tools/renderers/find.js +63 -0
  53. package/dist/core/tools/renderers/grep.d.ts +9 -0
  54. package/dist/core/tools/renderers/grep.js +68 -0
  55. package/dist/core/tools/renderers/index.d.ts +27 -0
  56. package/dist/core/tools/renderers/index.js +46 -0
  57. package/dist/core/tools/renderers/ls.d.ts +9 -0
  58. package/dist/core/tools/renderers/ls.js +57 -0
  59. package/dist/core/tools/renderers/read.d.ts +10 -0
  60. package/dist/core/tools/renderers/read.js +128 -0
  61. package/dist/core/tools/renderers/write.d.ts +9 -0
  62. package/dist/core/tools/renderers/write.js +152 -0
  63. package/dist/core/tools/write.js +5 -146
  64. package/dist/extensions/capability-gateway/index.ts +12 -1
  65. package/dist/extensions/capability-gateway/integration.test.ts +23 -0
  66. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +18 -0
  67. package/dist/extensions/pi-hermes-memory/README.md +16 -12
  68. package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +1 -1
  69. package/dist/extensions/pi-hermes-memory/package-lock.json +587 -62
  70. package/dist/extensions/pi-hermes-memory/package.json +4 -4
  71. package/dist/extensions/pi-hermes-memory/src/config.ts +32 -0
  72. package/dist/extensions/pi-hermes-memory/src/constants.ts +12 -2
  73. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +109 -51
  74. package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +9 -3
  75. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +257 -98
  76. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +9 -2
  77. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +5 -0
  78. package/dist/extensions/pi-hermes-memory/src/index.ts +22 -2
  79. package/dist/extensions/pi-hermes-memory/src/paths.ts +1 -1
  80. package/dist/extensions/pi-hermes-memory/src/store/db.ts +6 -0
  81. package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +58 -1
  82. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +60 -8
  83. package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +169 -7
  84. package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +7 -21
  85. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +134 -15
  86. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +4 -2
  87. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +24 -9
  88. package/dist/extensions/pi-hermes-memory/src/types.ts +11 -0
  89. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +42 -0
  90. package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +159 -3
  91. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +452 -0
  92. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +39 -0
  93. package/dist/extensions/pi-hermes-memory/tests/integration/corruption-recovery-integration.test.ts +91 -0
  94. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +29 -0
  95. package/dist/extensions/pi-hermes-memory/tests/store/fts-query.test.ts +116 -0
  96. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +163 -0
  97. package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +233 -0
  98. package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +12 -0
  99. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +175 -0
  100. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +2 -2
  101. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +22 -0
  102. package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +37 -1
  103. package/dist/extensions/pi-intercom/CHANGELOG.md +13 -0
  104. package/dist/extensions/pi-intercom/README.md +16 -2
  105. package/dist/extensions/pi-intercom/broker/spawn.test.ts +19 -2
  106. package/dist/extensions/pi-intercom/broker/spawn.ts +4 -4
  107. package/dist/extensions/pi-intercom/index.ts +1 -1
  108. package/dist/extensions/pi-intercom/intercom.integration.test.ts +121 -2
  109. package/dist/extensions/pi-intercom/package.json +1 -1
  110. package/dist/extensions/pi-intercom/project-agent.test.ts +1 -1
  111. package/dist/extensions/pi-intercom/project-agent.ts +1 -6
  112. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +7 -6
  113. package/dist/extensions/pi-rewind-hook/index.test.ts +7 -0
  114. package/dist/extensions/pi-subagents/.github/workflows/test.yml +36 -4
  115. package/dist/extensions/pi-subagents/CHANGELOG.md +120 -0
  116. package/dist/extensions/pi-subagents/README.md +3 -3
  117. package/dist/extensions/pi-subagents/agents/researcher.md +23 -13
  118. package/dist/extensions/pi-subagents/agents/reviewer.md +1 -1
  119. package/dist/extensions/pi-subagents/agents/scout.md +1 -1
  120. package/dist/extensions/pi-subagents/docs/agents.md +32 -16
  121. package/dist/extensions/pi-subagents/docs/configuration.md +41 -15
  122. package/dist/extensions/pi-subagents/docs/extension-api.md +109 -6
  123. package/dist/extensions/pi-subagents/docs/missions.md +2 -0
  124. package/dist/extensions/pi-subagents/docs/models.md +58 -1
  125. package/dist/extensions/pi-subagents/docs/observability.md +50 -12
  126. package/dist/extensions/pi-subagents/docs/tool-reference.md +17 -10
  127. package/dist/extensions/pi-subagents/docs/watchdog.md +1 -1
  128. package/dist/extensions/pi-subagents/docs/workflows.md +26 -9
  129. package/dist/extensions/pi-subagents/package-lock.json +747 -177
  130. package/dist/extensions/pi-subagents/package.json +6 -4
  131. package/dist/extensions/pi-subagents/runner-server-preload.mjs +13 -0
  132. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +2 -1
  133. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  134. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +21 -4
  135. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -1
  136. package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
  137. package/dist/extensions/pi-subagents/src/agents/advertised-agent-prompt.ts +63 -0
  138. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +61 -16
  139. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
  140. package/dist/extensions/pi-subagents/src/agents/agents.ts +8 -0
  141. package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +0 -1
  142. package/dist/extensions/pi-subagents/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
  143. package/dist/extensions/pi-subagents/src/api/preflight.ts +7 -4
  144. package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -2
  145. package/dist/extensions/pi-subagents/src/api/workflow-resources.ts +6 -0
  146. package/dist/extensions/pi-subagents/src/extension/config.ts +4 -2
  147. package/dist/extensions/pi-subagents/src/extension/doctor.ts +2 -10
  148. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +9 -11
  149. package/dist/extensions/pi-subagents/src/extension/index.ts +99 -10
  150. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +13 -0
  151. package/dist/extensions/pi-subagents/src/extension/rpc.ts +7 -23
  152. package/dist/extensions/pi-subagents/src/extension/schemas.ts +11 -8
  153. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +25 -7
  154. package/dist/extensions/pi-subagents/src/integrations/pi-web-session-liveness.ts +73 -0
  155. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +228 -136
  156. package/dist/extensions/pi-subagents/src/intercom/supervisor-ui.ts +244 -0
  157. package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +37 -16
  158. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +18 -18
  159. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +51 -25
  160. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +46 -3
  161. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -3
  162. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +9 -0
  163. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +10 -12
  164. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -15
  165. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +40 -29
  166. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +8 -0
  167. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +79 -247
  168. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +88 -12
  169. package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +6 -6
  170. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +24 -24
  171. package/dist/extensions/pi-subagents/src/runs/background/retained-nested-route-tracker.ts +96 -0
  172. package/dist/extensions/pi-subagents/src/runs/background/run-child-session.ts +642 -0
  173. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +75 -6
  174. package/dist/extensions/pi-subagents/src/runs/background/runner-aliases.ts +163 -0
  175. package/dist/extensions/pi-subagents/src/runs/background/runner-child-launch.ts +86 -0
  176. package/dist/extensions/pi-subagents/src/runs/background/runner-child-sessions.ts +31 -0
  177. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +18 -4
  178. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -1
  179. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +597 -1092
  180. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +3 -0
  181. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +4 -0
  182. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +20 -17
  183. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +296 -394
  184. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +4 -0
  185. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +3 -1
  186. package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +9 -5
  187. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +630 -283
  188. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +8 -5
  189. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +78 -98
  190. package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +3 -3
  191. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +16 -3
  192. package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +1 -1
  193. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +47 -47
  194. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -2
  195. package/dist/extensions/pi-subagents/src/runs/shared/child-hooks.ts +174 -0
  196. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +14 -3
  197. package/dist/extensions/pi-subagents/src/runs/shared/child-launch.ts +319 -0
  198. package/dist/extensions/pi-subagents/src/runs/shared/child-lifecycle.ts +25 -0
  199. package/dist/extensions/pi-subagents/src/runs/shared/child-runtime-config.ts +126 -0
  200. package/dist/extensions/pi-subagents/src/runs/shared/child-session.ts +373 -0
  201. package/dist/extensions/pi-subagents/src/runs/shared/child-tool-plan.ts +530 -0
  202. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +2 -2
  203. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +2 -1
  204. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +16 -0
  205. package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +11 -11
  206. package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +30 -20
  207. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
  208. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +84 -15
  209. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +88 -12
  210. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +37 -53
  211. package/dist/extensions/pi-subagents/src/runs/shared/nested-path.ts +1 -1
  212. package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +18 -7
  213. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +57 -12
  214. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +3 -4
  215. package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +0 -11
  216. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +4 -1
  217. package/dist/extensions/pi-subagents/src/runs/shared/readonly-drain-observation.ts +42 -0
  218. package/dist/extensions/pi-subagents/src/runs/shared/readonly-model-continuation.ts +69 -0
  219. package/dist/extensions/pi-subagents/src/runs/shared/readonly-session-evidence.ts +307 -0
  220. package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +8 -21
  221. package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +3 -30
  222. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +17 -4
  223. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +2 -0
  224. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +99 -386
  225. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +18 -62
  226. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +0 -14
  227. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +31 -9
  228. package/dist/extensions/pi-subagents/src/runs/shared/worktree-setup-command.ts +190 -0
  229. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +506 -226
  230. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +1 -1
  231. package/dist/extensions/pi-subagents/src/shared/jsonl-writer.ts +11 -0
  232. package/dist/extensions/pi-subagents/src/shared/model-response-aliases.ts +13 -0
  233. package/dist/extensions/pi-subagents/src/shared/thinking-ceiling.ts +0 -6
  234. package/dist/extensions/pi-subagents/src/shared/types.ts +145 -89
  235. package/dist/extensions/pi-subagents/src/shared/utils.ts +13 -6
  236. package/dist/extensions/pi-subagents/src/shared/watch-strategy.ts +2 -0
  237. package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +18 -13
  238. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +0 -6
  239. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +1 -1
  240. package/dist/extensions/pi-subagents/src/tui/fleet.ts +11 -6
  241. package/dist/extensions/pi-subagents/src/tui/render.ts +277 -46
  242. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +0 -1
  243. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +12 -12
  244. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -3
  245. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +116 -15
  246. package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +15 -18
  247. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +57 -8
  248. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +19 -19
  249. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +3 -3
  250. package/dist/extensions/pi-subagents/src/workflows/workflow-resources.ts +97 -22
  251. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +3 -0
  252. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +336 -21
  253. package/dist/extensions/pi-subagents/test/integration/async-execution.part-1.test.ts +1181 -0
  254. package/dist/extensions/pi-subagents/test/integration/async-execution.part-2.test.ts +2240 -0
  255. package/dist/extensions/pi-subagents/test/integration/async-execution.part-3.test.ts +2268 -0
  256. package/dist/extensions/pi-subagents/test/integration/async-execution.part-4.test.ts +1512 -0
  257. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +231 -16
  258. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +4 -18
  259. package/dist/extensions/pi-subagents/test/integration/foreign-workflow-steering.test.ts +95 -0
  260. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +9 -15
  261. package/dist/extensions/pi-subagents/test/integration/in-process-child.test.ts +374 -0
  262. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +20 -10
  263. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +18 -6
  264. package/dist/extensions/pi-subagents/test/integration/recovered-assistant-error.test.ts +151 -0
  265. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +42 -1
  266. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +150 -25
  267. package/dist/extensions/pi-subagents/test/integration/result-publication.test.ts +354 -0
  268. package/dist/extensions/pi-subagents/test/integration/shared-cwd-terminal-evidence.test.ts +117 -0
  269. package/dist/extensions/pi-subagents/test/integration/single-execution.part-1.test.ts +3678 -0
  270. package/dist/extensions/pi-subagents/test/integration/{single-execution.test.ts → single-execution.part-2.test.ts} +500 -4207
  271. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -2
  272. package/dist/extensions/pi-subagents/test/integration/workflow-result-publication.test.ts +127 -0
  273. package/dist/extensions/pi-subagents/test/integration/workflow-steer-inbox.test.ts +365 -0
  274. package/dist/extensions/pi-subagents/test/smoke/pi085-child.ts +31 -0
  275. package/dist/extensions/pi-subagents/test/smoke/pi085-clean-install.mjs +74 -0
  276. package/dist/extensions/pi-subagents/test/smoke/pi085-extension.ts +16 -0
  277. package/dist/extensions/pi-subagents/test/support/async-execution-fixture.ts +699 -0
  278. package/dist/extensions/pi-subagents/test/support/fake-child-session.ts +418 -0
  279. package/dist/extensions/pi-subagents/test/support/foreign-workflow-steer-host.mjs +13 -0
  280. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +30 -76
  281. package/dist/extensions/pi-subagents/test/support/notify-diagnostics-fixture.ts +84 -0
  282. package/dist/extensions/pi-subagents/test/support/result-publication-capacity-preload.mjs +70 -0
  283. package/dist/extensions/pi-subagents/test/support/runner-child-session-factory.ts +17 -0
  284. package/dist/extensions/pi-subagents/test/support/single-execution-fixture.ts +390 -0
  285. package/dist/extensions/pi-subagents/test/unit/acceptance-compaction.test.ts +120 -0
  286. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +24 -3
  287. package/dist/extensions/pi-subagents/test/unit/advertised-agent-prompt.test.ts +87 -0
  288. package/dist/extensions/pi-subagents/test/unit/advertised-agent-refresh.test.ts +163 -0
  289. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +70 -31
  290. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +78 -3
  291. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +2 -8
  292. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +1 -1
  293. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +73 -66
  294. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +51 -1
  295. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +49 -1
  296. package/dist/extensions/pi-subagents/test/unit/async-spawn-preload.test.ts +103 -0
  297. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +3 -3
  298. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +9 -8
  299. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +10 -7
  300. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +48 -6
  301. package/dist/extensions/pi-subagents/test/unit/child-runtime-config.test.ts +90 -0
  302. package/dist/extensions/pi-subagents/test/unit/{pi-args-permission-system.test.ts → child-tool-plan-permission-system.test.ts} +48 -86
  303. package/dist/extensions/pi-subagents/test/unit/child-tool-plan.test.ts +33 -0
  304. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +18 -18
  305. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +9 -9
  306. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +8 -7
  307. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +225 -65
  308. package/dist/extensions/pi-subagents/test/unit/extension-bindings.test.ts +17 -22
  309. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +11 -1
  310. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +47 -3
  311. package/dist/extensions/pi-subagents/test/unit/foreign-workflow-steering.test.ts +74 -0
  312. package/dist/extensions/pi-subagents/test/unit/fork-cache-key.test.ts +23 -29
  313. package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +96 -4
  314. package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +4 -4
  315. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +116 -55
  316. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +58 -1
  317. package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +104 -3
  318. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +10 -1
  319. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +31 -1
  320. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +102 -51
  321. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +196 -6
  322. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +181 -38
  323. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +62 -70
  324. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +58 -53
  325. package/dist/extensions/pi-subagents/test/unit/notify-diagnostics.test.ts +42 -0
  326. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +84 -0
  327. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +13 -5
  328. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +21 -5
  329. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +5 -4
  330. package/dist/extensions/pi-subagents/test/unit/permissions.test.ts +2 -6
  331. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +28 -2
  332. package/dist/extensions/pi-subagents/test/unit/pi-web-session-liveness.test.ts +209 -0
  333. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +38 -1
  334. package/dist/extensions/pi-subagents/test/unit/prompt-audit-optional-peers.test.ts +43 -0
  335. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +24 -2
  336. package/dist/extensions/pi-subagents/test/unit/readonly-drain-observation.test.ts +174 -0
  337. package/dist/extensions/pi-subagents/test/unit/readonly-model-continuation.test.ts +94 -0
  338. package/dist/extensions/pi-subagents/test/unit/readonly-session-evidence.test.ts +1329 -0
  339. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +29 -80
  340. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +11 -8
  341. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +2 -2
  342. package/dist/extensions/pi-subagents/test/unit/retained-nested-route-tracker.test.ts +175 -0
  343. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +38 -12
  344. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +2 -5
  345. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +89 -1
  346. package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +2 -16
  347. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +27 -3
  348. package/dist/extensions/pi-subagents/test/unit/scheduled-supervisor-demand.test.ts +215 -0
  349. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +15 -11
  350. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +237 -1
  351. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -5
  352. package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +2 -2
  353. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +14 -1
  354. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +130 -739
  355. package/dist/extensions/pi-subagents/test/unit/supervisor-ask-registration.test.ts +682 -0
  356. package/dist/extensions/pi-subagents/test/unit/supervisor-ui.test.ts +137 -0
  357. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +1 -11
  358. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +23 -3
  359. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +8 -1
  360. package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +19 -1
  361. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +72 -4
  362. package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +17 -3
  363. package/dist/extensions/pi-subagents/test/unit/workflow-child-summary.test.ts +34 -0
  364. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +12 -2
  365. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +46 -6
  366. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +3 -3
  367. package/dist/extensions/pi-subagents/test/unit/workflow-resources.test.ts +133 -1
  368. package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +53 -2
  369. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +222 -14
  370. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +526 -85
  371. package/dist/extensions/pi-web-agent/src/backends/config.ts +1 -0
  372. package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +5 -3
  373. package/dist/extensions/pi-web-agent/src/search/tokenin.ts +7 -3
  374. package/dist/extensions/pi-web-agent/src/types.ts +1 -1
  375. package/dist/extensions/pi-zentui/README.md +17 -0
  376. package/dist/extensions/pi-zentui/assets/screenshots/footer-status-hyperlink.png +0 -0
  377. package/dist/extensions/pi-zentui/docs/configuration.md +71 -2
  378. package/dist/extensions/pi-zentui/extensions/zentui/config.ts +11 -0
  379. package/dist/extensions/pi-zentui/extensions/zentui/editor-transfer.ts +14 -0
  380. package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +29 -13
  381. package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +5 -4
  382. package/dist/extensions/pi-zentui/extensions/zentui/footer-text.ts +11 -0
  383. package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +11 -10
  384. package/dist/extensions/pi-zentui/extensions/zentui/index.ts +77 -3
  385. package/dist/extensions/pi-zentui/extensions/zentui/presets.ts +77 -0
  386. package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +103 -2
  387. package/dist/extensions/pi-zentui/extensions/zentui/working-line-extension-segments.ts +115 -0
  388. package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +87 -5
  389. package/dist/extensions/pi-zentui/package-lock.json +277 -517
  390. package/dist/extensions/pi-zentui/package.json +7 -4
  391. package/dist/extensions/pi-zentui/test/config-presets.test.ts +238 -0
  392. package/dist/extensions/pi-zentui/test/editor-transfer.test.ts +24 -1
  393. package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +570 -2
  394. package/dist/extensions/pi-zentui/test/extension-status.test.ts +90 -0
  395. package/dist/extensions/pi-zentui/test/footer-hyperlink.test.ts +243 -0
  396. package/dist/extensions/pi-zentui/test/package-contents.mjs +1 -0
  397. package/dist/extensions/pi-zentui/test/presets.test.ts +121 -0
  398. package/dist/extensions/pi-zentui/test/settings-command.test.ts +205 -1
  399. package/dist/extensions/pi-zentui/test/working-line-extension-segments.test.ts +139 -0
  400. package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +237 -3
  401. package/dist/extensions/pi-zentui/test/working-line.test.ts +94 -2
  402. package/dist/index.d.ts +1 -1
  403. package/dist/main.js +4 -1
  404. package/dist/modes/interactive/chat-viewport.d.ts +19 -0
  405. package/dist/modes/interactive/chat-viewport.js +27 -0
  406. package/dist/modes/interactive/components/assistant-message.d.ts +6 -2
  407. package/dist/modes/interactive/components/assistant-message.js +30 -13
  408. package/dist/modes/interactive/components/custom-editor.d.ts +10 -1
  409. package/dist/modes/interactive/components/custom-editor.js +39 -1
  410. package/dist/modes/interactive/components/first-time-setup.js +1 -1
  411. package/dist/modes/interactive/components/index.d.ts +1 -1
  412. package/dist/modes/interactive/components/markdown-transform.d.ts +2 -0
  413. package/dist/modes/interactive/components/markdown-transform.js +18 -0
  414. package/dist/modes/interactive/components/model-selector.js +19 -17
  415. package/dist/modes/interactive/components/scoped-models-selector.js +21 -23
  416. package/dist/modes/interactive/components/settings-selector.d.ts +7 -1
  417. package/dist/modes/interactive/components/settings-selector.js +128 -20
  418. package/dist/modes/interactive/components/settings-submenu.d.ts +71 -0
  419. package/dist/modes/interactive/components/settings-submenu.js +164 -0
  420. package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
  421. package/dist/modes/interactive/components/status-indicator.js +10 -3
  422. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  423. package/dist/modes/interactive/components/thinking-selector.js +75 -18
  424. package/dist/modes/interactive/components/tool-execution.d.ts +21 -4
  425. package/dist/modes/interactive/components/tool-execution.js +35 -33
  426. package/dist/modes/interactive/components/trust-selector.js +2 -2
  427. package/dist/modes/interactive/interactive-mode.d.ts +13 -15
  428. package/dist/modes/interactive/interactive-mode.js +144 -80
  429. package/dist/modes/interactive/session-share.d.ts +15 -0
  430. package/dist/modes/interactive/session-share.js +183 -0
  431. package/dist/modes/interactive/theme/dark.json +2 -1
  432. package/dist/modes/interactive/theme/light.json +2 -1
  433. package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
  434. package/dist/modes/interactive/theme/theme-controller.js +5 -0
  435. package/dist/modes/interactive/theme/theme-json.d.ts +83 -0
  436. package/dist/modes/interactive/theme/theme-json.js +129 -0
  437. package/dist/modes/interactive/theme/theme-schema.json +9 -1
  438. package/dist/modes/interactive/theme/theme.d.ts +14 -5
  439. package/dist/modes/interactive/theme/theme.js +18 -119
  440. package/dist/modes/interactive/tui-renderer.d.ts +20 -0
  441. package/dist/modes/interactive/tui-renderer.js +65 -0
  442. package/dist/utils/exif-orientation.js +2 -3
  443. package/dist/utils/management-http.d.ts +24 -0
  444. package/dist/utils/management-http.js +53 -0
  445. package/dist/utils/text.d.ts +7 -0
  446. package/dist/utils/text.js +8 -0
  447. package/dist/utils/tools-manager.d.ts +1 -0
  448. package/dist/utils/tools-manager.js +43 -21
  449. package/docs/docs.json +0 -4
  450. package/package.json +4 -4
  451. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +0 -415
  452. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +0 -1059
  453. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +0 -116
  454. package/dist/extensions/pi-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
  455. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +0 -224
  456. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +0 -6986
  457. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +0 -447
  458. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +0 -185
  459. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +0 -311
  460. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +0 -84
  461. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +0 -202
  462. package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +0 -101
  463. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +0 -2347
  464. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +0 -104
  465. package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +0 -49
  466. package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -1465
  467. package/dist/skills/imagegen-frontend-web/SKILL.md +0 -987
  468. package/dist/skills/implanger/SKILL.md +0 -70
  469. package/dist/skills/output/SKILL.md +0 -49
  470. package/dist/skills/planger/SKILL.md +0 -167
  471. package/dist/skills/workflow/SKILL.md +0 -95
  472. package/docs/workflows.md +0 -18
  473. /package/dist/bun/{register-bedrock.d.ts → runtime-setup.d.ts} +0 -0
@@ -50,9 +50,10 @@ import { updateActiveRunIndex } from "../background/active-run-index.ts";
50
50
  import { steeringReceipt } from "../background/steering.ts";
51
51
  import { acquireActiveAsyncCapacity, ActiveAsyncCapacityError, getActiveAsyncCapacitySnapshot, resolveAbandonedSlotReleaseAfterMs, resolveMaxActiveAsyncRunsPerSession, transferActiveAsyncCapacity, type ActiveAsyncCapacityHandle } from "../background/active-async-capacity.ts";
52
52
  import { isScheduledRunAction } from "../background/scheduled-runs.ts";
53
+ import { encodeIndexSegment } from "../background/index-segment.ts";
53
54
  import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
54
55
  import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
55
- import { normalizeGateAcceptance, resolveAcceptanceReportMode, validateExecutionAcceptance } from "../shared/acceptance.ts";
56
+ import { normalizeGateAcceptance, resolveAcceptanceReportMode, validateAcceptanceInput, validateExecutionAcceptance } from "../shared/acceptance.ts";
56
57
  import { canPreferFork, createForkContextResolver, forkedChildRequiresThinkingOff, resolveSubagentLaunchContext } from "../../shared/fork-context.ts";
57
58
  import { createPrunedForkSessionWriter } from "../../shared/pruned-fork.ts";
58
59
  import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
@@ -60,17 +61,18 @@ import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
60
61
  import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBridge, resolveIntercomSessionTarget, resolveSubagentIntercomTarget, type IntercomBridgeState } from "../../intercom/intercom-bridge.ts";
61
62
  import { formatControlIntercomMessage, formatControlNoticeMessage, resolveControlConfig, shouldNotifyControlEvent } from "../shared/subagent-control.ts";
62
63
  import { formatSpawnBudget, getSpawnBudgetSnapshot, grantSpawnBudget, preflightSpawnBudget, preflightSpawnBudgetGrant, reserveSpawnBudget } from "../shared/spawn-budget.ts";
63
- import { claimRunFanoutBatch, claimRunFanoutBatchWithCommit, createRunFanoutBudget, decodeRunFanoutBudgetDescriptor, formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetDescriptor, RunFanoutLimitError, RUN_FANOUT_BUDGET_ENV, writeRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.ts";
64
+ import { claimRunFanoutBatch, claimRunFanoutBatchWithCommit, createRunFanoutBudget, formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetDescriptor, RunFanoutLimitError, writeRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.ts";
65
+ import { retainLiveForegroundNestedRoute } from "../../integrations/pi-web-session-liveness.ts";
64
66
  import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
65
67
  import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig } from "../shared/usage-budget.ts";
66
68
  import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
67
- import { isAgentContractV1 } from "../shared/agent-contract.ts";
69
+ import { isAgentContract } from "../shared/agent-contract.ts";
68
70
  import { normalizeExtensionBindings, type ExtensionBindings } from "../shared/extension-bindings.ts";
69
71
  import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, outputPathMappingFromTask, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
70
72
  import { assertJsonSchemaObject, cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
71
73
  import { compactForegroundDetails, getSingleResultOutput, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage, toAgentToolUsage } from "../../shared/utils.ts";
72
74
  import { createTaskMutationArbiter } from "../shared/llm-intent-arbiter.ts";
73
- import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, formatStoredParallelHandoffCleanup, parallelHandoffPath, readParallelHandoffManifest, recordParallelHandoffMerge, recordParallelHandoffSupersession, writeParallelHandoffGroup, writePendingParallelHandoff } from "../shared/parallel-handoff.ts";
75
+ import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, formatStoredParallelHandoffCleanup, parallelHandoffPath, readParallelHandoffManifest, recordParallelHandoffMerge, recordParallelHandoffSupersession, writeParallelHandoffGroup, writeWorktreeSetupHandoff } from "../shared/parallel-handoff.ts";
74
76
  import { summarizeContextModes, type ContextMode, type ContextSummary } from "../shared/context-mode.ts";
75
77
  import {
76
78
  attachNestedChildrenToResultChildren,
@@ -81,15 +83,13 @@ import {
81
83
  stripDetailsOutputsForIntercomReceipt,
82
84
  } from "../../intercom/result-intercom.ts";
83
85
  import { applySteeringRecoveryAgentConfig, asyncReviveRequiresRecoveryDescriptor, buildRevivedAsyncTask, readAsyncRecoveryDescriptor, resolveAsyncResumeTarget, resolveAsyncRunLocation } from "../background/async-resume.ts";
84
- import { deliverInterruptRequest, readRevivalBriefs, requestAsyncSteer, type SteerDeliveryMode } from "../background/control-channel.ts";
85
- import { updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
86
+ import { closeSteerInbox, consumeSteerRequests, deliverInterruptRequest, readRevivalBriefs, requestAsyncSteer, watchAsyncControlInbox, type SteerDeliveryMode, type SteerRequest } from "../background/control-channel.ts";
87
+ import { createSteeringStatus, recordSteeringRequest, steeringStatus, updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
86
88
  import { canQueueRetainedAsyncFollowUp, steerAsyncRun } from "./async-steering-action.ts";
87
89
  import {
88
- removeWorkflowForegroundSteeringRoute,
89
90
  resolveWorkflowForegroundSteeringTarget,
90
91
  steerWorkflowForegroundTarget,
91
- workflowForegroundSteeringDir,
92
- workflowForegroundSteeringLaunchOptions,
92
+ steerWorkflowRun,
93
93
  } from "./workflow-foreground-steering.ts";
94
94
  import { stopAsyncRun } from "./async-stop-action.ts";
95
95
  import { dismissRecoveredWorkflow } from "./async-dismiss-action.ts";
@@ -97,10 +97,11 @@ import { promotePausedWorkflowIfSettled, reconcileDetachedWorkflowChildCompletio
97
97
  import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
98
98
  import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.ts";
99
99
  import { resultFilePath, writeAsyncResultFile } from "../background/result-files.ts";
100
- import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedRunResolutionScope } from "../shared/nested-events.ts";
100
+ import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, inheritedNestedParentAddressOf, inheritedNestedRouteOf, resolveNestedAsyncDir, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedParentAddress, type NestedRoute, type NestedRunResolutionScope } from "../shared/nested-events.ts";
101
+ import type { ChildRuntimeConfig } from "../shared/child-runtime-config.ts";
101
102
  import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
102
103
  import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
103
- import { isStoppableAsyncStatusStep, resolveAsyncStatusChild } from "../shared/child-identity.ts";
104
+ import { isStoppableAsyncStatusStep, resolveAsyncStatusChild, stopStoppableAsyncStatusChildren } from "../shared/child-identity.ts";
104
105
  import { inspectSubagentStatus } from "../background/run-status.ts";
105
106
  import { getExternalJobProvider } from "../../api/external-job-provider.ts";
106
107
  import { externalJobFollowUpRequestDigest, externalJobFollowUpRequestId, externalJobFollowUpRunId, externalJobPromptDigest, externalJobStableJson } from "../shared/external-job-runner.ts";
@@ -116,10 +117,10 @@ import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspe
116
117
  import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
117
118
  import { previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError, type WorkflowLanePlan, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowScriptTraceEntry, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
118
119
  import { executeWorkflowHostCommand, resolveWorkflowHostOutputClaimPath, type WorkflowHostCommandParams, type WorkflowHostCommandResult } from "../../workflows/host-command.ts";
119
- import { buildWorkflowReceipt, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt, type WorkflowReceipt, type WorkflowReceiptState } from "../../workflows/workflow-receipt.ts";
120
+ import { buildWorkflowReceipt, readWorkflowReceipt, workflowReceiptPath, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt, type WorkflowReceipt, type WorkflowReceiptState } from "../../workflows/workflow-receipt.ts";
120
121
  import { upsertHostStep, validHostStepNodes } from "../shared/host-step-status.ts";
121
122
  import { assertWorkflowLaneKey, normalizeWorkflowLaneMetadata } from "../shared/lane-metadata.ts";
122
- import { parseWorkflowChildSummary, workflowChildSummary } from "../../workflows/workflow-child-summary.ts";
123
+ import { parseWorkflowChildSummary, workflowChildProgress, workflowChildSummary } from "../../workflows/workflow-child-summary.ts";
123
124
  import { resolveWorkflowChatProgress, type WorkflowChatProgressProjection } from "../../workflows/chat-progress.ts";
124
125
  import { annotateWorkflowPreflightTrace, formatWorkflowPreflight, formatWorkflowPreflightWarnings, normalizeWorkflowPreflight, workflowPreflightWarnings } from "../../workflows/workflow-preflight.ts";
125
126
  import {
@@ -136,6 +137,8 @@ import { resolveWorkflowResource } from "../../workflows/workflow-resources.ts";
136
137
  import {
137
138
  cleanupWorktrees,
138
139
  createWorktrees,
140
+ withWorktreeTransaction,
141
+ type WorktreeSetupProgress,
139
142
  diffWorktrees,
140
143
  formatWorktreeDiffSummary,
141
144
  type WorktreeSetup,
@@ -154,8 +157,11 @@ import {
154
157
  type Details,
155
158
  type ExtensionConfig,
156
159
  type ForegroundResumeChild,
160
+ type ForegroundChildSessionControls,
161
+ type ForegroundSteerInput,
162
+ type ForegroundSteerOutcome,
157
163
  type ForegroundRunControl,
158
- type HostStepNodeV1,
164
+ type HostStepNode,
159
165
  type IntercomBridgeConfig,
160
166
  type IntercomEventBus,
161
167
  type JsonSchemaObject,
@@ -172,7 +178,7 @@ import {
172
178
  type ToolBudgetConfig,
173
179
  type Usage,
174
180
  type UsageBudgetConfig,
175
- type WorkflowResourceProvenanceV1,
181
+ type WorkflowResourceProvenance,
176
182
  type WorkflowGraphNode,
177
183
  type WorkflowGraphSnapshot,
178
184
  type WorkflowNodeStatus,
@@ -192,6 +198,7 @@ import {
192
198
  resolveMaxSubagentSpawnsPerRun,
193
199
  wrapForkTask,
194
200
  type ScheduleOrigin,
201
+ type SteeringTargetState,
195
202
  } from "../../shared/types.ts";
196
203
  import { deriveChildSessionName } from "../../shared/child-session-name.ts";
197
204
 
@@ -324,7 +331,7 @@ export interface SubagentParamsLike {
324
331
  workflowScriptPath?: string;
325
332
  globalConcurrencyLimit?: number;
326
333
  maxSubagentSpawnsPerRun?: number;
327
- preflight?: import("../../shared/types.ts").WorkflowPreflightV1;
334
+ preflight?: import("../../shared/types.ts").WorkflowPreflight;
328
335
  chatProgress?: "auto" | "off" | "live-card";
329
336
  isolation?: "none" | "worktree";
330
337
  step?: ChainStep;
@@ -352,6 +359,8 @@ export interface SubagentParamsLike {
352
359
  tasks?: TaskParam[];
353
360
  concurrency?: number;
354
361
  worktree?: boolean;
362
+ /** Git ref used as the managed worktree base. */
363
+ baseRef?: string;
355
364
  context?: "fresh" | "fork" | "profile";
356
365
  /** Per-run intercom bridge config. It replaces the global config for this launch only. */
357
366
  intercomBridge?: IntercomBridgeConfig;
@@ -433,10 +442,27 @@ interface ExecutorDeps {
433
442
  getSubagentSessionRoot: (parentSessionFile: string | null) => string;
434
443
  expandTilde: (p: string) => string;
435
444
  discoverAgents: (cwd: string, scope: AgentScope, preferredModelProvider?: string) => { agents: AgentConfig[]; agentDiagnostics?: AgentDiscoveryDiagnostic[]; modelScope?: ModelScopeConfig; maxThinking?: AgentConfig["maxThinking"]; cwd?: string; scope?: AgentScope; directories?: UnknownAgentDiagnosticContext["directories"] };
445
+ onAgentsChanged?: () => void;
436
446
  allowMutatingManagementActions?: boolean;
437
447
  activateSupervisorTransport?: () => void;
448
+ findPendingAsks?: Parameters<typeof steerAsyncRun>[0]["findPendingAsks"];
438
449
  refreshResultDelivery?: () => void;
450
+ trackRetainedNestedRoute?: (rootRunId: string) => void;
439
451
  kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
452
+ /** Set when this executor runs inside a child session; carries the runtime settings the host passes instead of environment variables. */
453
+ childRuntime?: ChildRuntimeConfig;
454
+ }
455
+
456
+ function inheritedNestedRoute(deps: Pick<ExecutorDeps, "childRuntime">): NestedRoute | undefined {
457
+ return inheritedNestedRouteOf(deps.childRuntime);
458
+ }
459
+
460
+ function inheritedNestedParentAddress(deps: Pick<ExecutorDeps, "childRuntime">): NestedParentAddress | undefined {
461
+ return inheritedNestedParentAddressOf(deps.childRuntime);
462
+ }
463
+
464
+ function inheritedRunFanoutBudget(deps: Pick<ExecutorDeps, "childRuntime">): RunFanoutBudgetDescriptor | undefined {
465
+ return deps.childRuntime?.runFanoutBudget;
440
466
  }
441
467
 
442
468
  type ForkSessionFileForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => string | undefined;
@@ -478,6 +504,8 @@ interface ExecutionContextData {
478
504
  configToolTimeoutMs?: number;
479
505
  toolBudget?: ResolvedToolBudget;
480
506
  usageBudget?: UsageBudgetConfig;
507
+ /** Parent workflow owns this allowance; presence vetoes unverifiable child continuation. */
508
+ inheritedUsageBudget?: UsageBudgetConfig;
481
509
  allowZeroToolBudget?: boolean;
482
510
  configToolBudget?: ResolvedToolBudget;
483
511
  contextPolicy: AgentDefaultContextPolicy;
@@ -510,10 +538,16 @@ function loadWorkflowScriptPath(params: SubagentParamsLike, runtimeCwd: string):
510
538
  return { params: { ...rest, workflowScript } };
511
539
  }
512
540
 
513
- function removeForegroundControlIfIdle(state: SubagentState, runId: string): boolean {
541
+ export function removeForegroundControlIfIdle(state: SubagentState, runId: string, trackRetainedNestedRoute?: (rootRunId: string) => void): boolean {
514
542
  const control = state.foregroundControls.get(runId);
515
543
  if (control && (!foregroundSchedulingSettled(control) || (control.activeChildren?.size ?? 0) > 0)) return false;
516
- if (control) removeWorkflowForegroundSteeringRoute(control);
544
+ if (control?.nestedRoute && trackRetainedNestedRoute) {
545
+ try {
546
+ if (retainLiveForegroundNestedRoute(state, control.nestedRoute)) trackRetainedNestedRoute(runId);
547
+ } catch (error) {
548
+ console.error(`Failed to retain live nested descendants for foreground run '${runId}':`, error);
549
+ }
550
+ }
517
551
  state.foregroundControls.delete(runId);
518
552
  if (state.lastForegroundControlId === runId) state.lastForegroundControlId = null;
519
553
  return true;
@@ -566,8 +600,8 @@ function formatForegroundActivity(control: SubagentState["foregroundControls"] e
566
600
 
567
601
  function nestedResolutionScopeForExecutor(deps: ExecutorDeps): NestedRunResolutionScope | undefined {
568
602
  if (deps.allowMutatingManagementActions !== false) return undefined;
569
- const route = resolveInheritedNestedRouteFromEnv();
570
- const address = route ? resolveNestedParentAddressFromEnv() : undefined;
603
+ const route = inheritedNestedRoute(deps);
604
+ const address = route ? inheritedNestedParentAddress(deps) : undefined;
571
605
  return {
572
606
  routes: route ? [route] : [],
573
607
  ...(address ? { descendantOf: { parentRunId: address.parentRunId, ...(address.parentStepIndex !== undefined ? { parentStepIndex: address.parentStepIndex } : {}) } } : {}),
@@ -708,7 +742,7 @@ function foregroundChildActivityFromProgress(progress: SingleResult["progress"]
708
742
  };
709
743
  }
710
744
 
711
- function rememberForegroundRun(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[]; params: SubagentParamsLike; effectiveOutput?: string | boolean; effectiveOutputMode: OutputMode; extensionBindings?: ExtensionBindings }): void {
745
+ function rememberForegroundRun(state: SubagentState, input: { modelResponseAliases?: Record<string, string[]>; runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[]; params: SubagentParamsLike; effectiveOutput?: string | boolean; effectiveOutputMode: OutputMode; extensionBindings?: ExtensionBindings }): void {
712
746
  state.foregroundRuns ??= new Map();
713
747
  const previous = state.foregroundRuns.get(input.runId);
714
748
  const updatedAt = Date.now();
@@ -720,6 +754,7 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
720
754
  updatedAt,
721
755
  children: input.results.map((result, index) => {
722
756
  const resumeContract = omitUndefinedProperties({
757
+ modelResponseAliases: input.modelResponseAliases,
723
758
  outputSchema: input.params.outputSchema,
724
759
  agentContract: input.params.agentContract,
725
760
  acceptance: input.params.acceptance,
@@ -1298,8 +1333,10 @@ function appendStepToAsyncChain(input: {
1298
1333
  currentModelProvider: parentModel?.provider,
1299
1334
  currentModel: parentModel,
1300
1335
  modelScope: discoveredForAppend.modelScope,
1336
+ modelResponseAliases: input.deps.config.modelResponseAliases,
1301
1337
  interactive: input.ctx.hasUI,
1302
1338
  permissions: input.deps.config.permissions,
1339
+ childRuntime: input.deps.childRuntime,
1303
1340
  });
1304
1341
  const built = buildAsyncRunnerSteps(resolved.id, compactOptional<Parameters<typeof buildAsyncRunnerSteps>[1]>({
1305
1342
  chain: wrapChainTasksForFork(chain, contextPolicy),
@@ -1312,7 +1349,7 @@ function appendStepToAsyncChain(input: {
1312
1349
  cwd: status.cwd ?? input.requestCwd,
1313
1350
  chainSkills,
1314
1351
  dynamicFanoutMaxItems: input.deps.config.chain?.dynamicFanout?.maxItems,
1315
- maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
1352
+ maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
1316
1353
  waitToolEnabled: input.deps.waitToolEnabled,
1317
1354
  waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
1318
1355
  contextForAgent: contextPolicy.contextForAgent,
@@ -1638,11 +1675,11 @@ async function resumeExternalJobFollowUp(input: {
1638
1675
  return externalJobFollowUpStarted({ sourceRunId: input.target.runId, runId, asyncDir, duplicate: true, interactive: input.ctx.hasUI });
1639
1676
  }
1640
1677
 
1641
- const depthState = checkSubagentDepth(input.deps.config.maxSubagentDepth);
1678
+ const depthState = checkSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime);
1642
1679
  if (depthState.blocked) {
1643
1680
  return { content: [{ type: "text", text: `Nested subagent resume blocked (depth=${depthState.depth}, max=${depthState.maxDepth}). Complete the follow-up directly instead.` }], isError: true, details: { mode: "management", results: [] } };
1644
1681
  }
1645
- const topLevelResume = depthState.depth === 0 && !resolveInheritedNestedRouteFromEnv() && !input.deps.state.workflowControllers?.has(input.target.runId);
1682
+ const topLevelResume = depthState.depth === 0 && !inheritedNestedRoute(input.deps) && !input.deps.state.workflowControllers?.has(input.target.runId);
1646
1683
  let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
1647
1684
  try {
1648
1685
  activeAsyncCapacity = topLevelResume ? acquireActiveAsyncCapacity({
@@ -1678,15 +1715,17 @@ async function resumeExternalJobFollowUp(input: {
1678
1715
  currentModelProvider: parentModel?.provider,
1679
1716
  currentModel: parentModel,
1680
1717
  modelScope: input.modelScope,
1718
+ modelResponseAliases: input.deps.config.modelResponseAliases,
1681
1719
  interactive: input.ctx.hasUI,
1682
1720
  permissions: input.deps.config.permissions,
1721
+ childRuntime: input.deps.childRuntime,
1683
1722
  }),
1684
1723
  cwd: input.effectiveCwd,
1685
1724
  artifactsDir,
1686
1725
  artifactConfig,
1687
1726
  shareEnabled: false,
1688
1727
  ...(input.parentSessionFile ? { sessionRoot: input.deps.getSubagentSessionRoot(input.parentSessionFile) } : {}),
1689
- maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
1728
+ maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
1690
1729
  waitToolEnabled: input.deps.waitToolEnabled,
1691
1730
  waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
1692
1731
  worktreeSetupHook: input.deps.config.worktreeSetupHook,
@@ -1712,6 +1751,28 @@ async function resumeExternalJobFollowUp(input: {
1712
1751
  return externalJobFollowUpStarted({ sourceRunId: input.target.runId, runId: result.details.asyncId ?? runId, asyncDir: result.details.asyncDir ?? asyncDir, interactive: input.ctx.hasUI });
1713
1752
  }
1714
1753
 
1754
+ function resolveRequestedResumeTarget(params: SubagentParamsLike, deps: ExecutorDeps, parentSessionFile: string | null): ResumeSourceTarget | { kind: "live-nested"; target: ResolvedSubagentRunId & { kind: "nested" } } {
1755
+ const requestedId = params.id ?? params.runId;
1756
+ let resolved: ResolvedSubagentRunId | undefined;
1757
+ try {
1758
+ resolved = requestedId ? resolveSubagentRunId(requestedId, omitUndefinedProperties({ state: deps.state, nested: nestedResolutionScopeForExecutor(deps) })) : undefined;
1759
+ } catch (error) {
1760
+ const message = error instanceof Error ? error.message : "";
1761
+ const asyncMatches = message.match(/async:/g)?.length ?? 0;
1762
+ if (!isResumeAmbiguity(error) || !message.includes("foreground:") || asyncMatches !== 1) throw error;
1763
+ }
1764
+ if (resolved?.kind === "nested") {
1765
+ if (params.chain?.length) throw new Error("Attaching a running subagent as a chain root is currently available for top-level async runs only.");
1766
+ if (resolved.match.run.state === "running" || resolved.match.run.state === "queued") return { kind: "live-nested", target: resolved };
1767
+ const trustedSessionRoots = [
1768
+ ...(deps.config.defaultSessionDir ? [path.resolve(deps.expandTilde(deps.config.defaultSessionDir))] : []),
1769
+ ...(parentSessionFile ? [deps.getSubagentSessionRoot(parentSessionFile)] : []),
1770
+ ];
1771
+ return resolveNestedResumeTarget(resolved, trustedSessionRoots);
1772
+ }
1773
+ return resolveResumeTarget(params, deps.state, { asyncRequireSessionFile: false });
1774
+ }
1775
+
1715
1776
  async function resumeAsyncRun(input: {
1716
1777
  params: SubagentParamsLike;
1717
1778
  requestCwd: string;
@@ -1750,34 +1811,9 @@ async function resumeAsyncRun(input: {
1750
1811
  let target: ResumeSourceTarget;
1751
1812
  const parentSessionFile = input.ctx.sessionManager.getSessionFile() ?? null;
1752
1813
  try {
1753
- const requestedId = input.params.id ?? input.params.runId;
1754
- let resolved: ResolvedSubagentRunId | undefined;
1755
- try {
1756
- resolved = requestedId ? resolveSubagentRunId(requestedId, omitUndefinedProperties({ state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) })) : undefined;
1757
- } catch (error) {
1758
- const message = error instanceof Error ? error.message : "";
1759
- const asyncMatches = message.match(/async:/g)?.length ?? 0;
1760
- if (!isResumeAmbiguity(error) || !message.includes("foreground:") || asyncMatches !== 1) throw error;
1761
- }
1762
- if (resolved?.kind === "nested") {
1763
- if (attachChain) {
1764
- return {
1765
- content: [{ type: "text", text: "Attaching a running subagent as a chain root is currently available for top-level async runs only." }],
1766
- isError: true,
1767
- details: { mode: "management", results: [] },
1768
- };
1769
- }
1770
- if (resolved.match.run.state === "running" || resolved.match.run.state === "queued") {
1771
- return resumeLiveNestedRun({ target: resolved, message: followUp });
1772
- }
1773
- const trustedSessionRoots = [
1774
- ...(input.deps.config.defaultSessionDir ? [path.resolve(input.deps.expandTilde(input.deps.config.defaultSessionDir))] : []),
1775
- ...(parentSessionFile ? [input.deps.getSubagentSessionRoot(parentSessionFile)] : []),
1776
- ];
1777
- target = resolveNestedResumeTarget(resolved, trustedSessionRoots);
1778
- } else {
1779
- target = resolveResumeTarget(input.params, input.deps.state, { asyncRequireSessionFile: false });
1780
- }
1814
+ const resolved = resolveRequestedResumeTarget(input.params, input.deps, parentSessionFile);
1815
+ if (resolved.kind === "live-nested") return resumeLiveNestedRun({ target: resolved.target, message: followUp });
1816
+ target = resolved;
1781
1817
  } catch (error) {
1782
1818
  const message = error instanceof Error ? error.message : String(error);
1783
1819
  return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
@@ -1797,7 +1833,7 @@ async function resumeAsyncRun(input: {
1797
1833
  };
1798
1834
  }
1799
1835
 
1800
- const { blocked, depth, maxDepth } = checkSubagentDepth(input.deps.config.maxSubagentDepth);
1836
+ const { blocked, depth, maxDepth } = checkSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime);
1801
1837
  if (blocked) {
1802
1838
  return {
1803
1839
  content: [{ type: "text", text: `Nested subagent resume blocked (depth=${depth}, max=${maxDepth}). Complete the follow-up directly instead.` }],
@@ -1886,7 +1922,7 @@ async function resumeAsyncRun(input: {
1886
1922
  };
1887
1923
  }
1888
1924
  const runId = randomUUID();
1889
- const topLevelResume = depth === 0 && !resolveInheritedNestedRouteFromEnv() && !input.params.workflowParentRunId;
1925
+ const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
1890
1926
  let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
1891
1927
  try {
1892
1928
  activeAsyncCapacity = topLevelResume ? acquireActiveAsyncCapacity({
@@ -1930,8 +1966,10 @@ async function resumeAsyncRun(input: {
1930
1966
  currentModelProvider: parentModel?.provider,
1931
1967
  currentModel: parentModel,
1932
1968
  modelScope,
1969
+ modelResponseAliases: input.deps.config.modelResponseAliases,
1933
1970
  interactive: input.ctx.hasUI,
1934
1971
  permissions: input.deps.config.permissions,
1972
+ childRuntime: input.deps.childRuntime,
1935
1973
  }),
1936
1974
  availableModels,
1937
1975
  cwd: effectiveCwd,
@@ -1944,12 +1982,13 @@ async function resumeAsyncRun(input: {
1944
1982
  agentContract: input.params.agentContract,
1945
1983
  fast: input.params.fast,
1946
1984
  dynamicFanoutMaxItems: input.deps.config.chain?.dynamicFanout?.maxItems,
1947
- maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
1985
+ maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
1948
1986
  waitToolEnabled: input.deps.waitToolEnabled,
1949
1987
  waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
1950
1988
  worktreeSetupHook: input.deps.config.worktreeSetupHook,
1951
1989
  worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
1952
1990
  worktreeBaseDir: input.deps.config.worktreeBaseDir,
1991
+ baseRef: input.params.baseRef ?? target.recoveryDescriptor?.baseRef,
1953
1992
  worktreeProvider: input.deps.config.worktreeProvider,
1954
1993
  worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
1955
1994
  controlConfig: resolveControlConfig(input.deps.config.control, input.params.control),
@@ -1986,8 +2025,11 @@ async function resumeAsyncRun(input: {
1986
2025
  if (target.source === "async" && asyncReviveRequiresRecoveryDescriptor(target)) {
1987
2026
  return { content: [{ type: "text", text: `Async child '${target.runId}' is missing its required run fan-out recovery identity. Start a new run instead.` }], isError: true, details: { mode: "management", results: [] } };
1988
2027
  }
2028
+ if (input.params.baseRef !== undefined && "managedWorktree" in target && target.managedWorktree === true) {
2029
+ return { content: [{ type: "text", text: "Cannot resume with baseRef: retained managed-worktree children continue in their existing worktree. Start a new worktree run from that base ref instead." }], isError: true, details: { mode: "management", results: [] } };
2030
+ }
1989
2031
  const runId = randomUUID();
1990
- const topLevelResume = depth === 0 && !resolveInheritedNestedRouteFromEnv() && !input.params.workflowParentRunId;
2032
+ const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
1991
2033
  let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
1992
2034
  try {
1993
2035
  activeAsyncCapacity = !topLevelResume ? undefined : target.source === "async"
@@ -2033,8 +2075,11 @@ async function resumeAsyncRun(input: {
2033
2075
  currentModelProvider: parentModel?.provider,
2034
2076
  currentModel: parentModel,
2035
2077
  modelScope,
2078
+ // Absence in the retained contract is meaningful; never acquire current aliases.
2079
+ modelResponseAliases: recoveryDescriptor ? recoveryDescriptor.modelResponseAliases : foregroundContract?.modelResponseAliases,
2036
2080
  interactive: input.ctx.hasUI,
2037
2081
  permissions: input.deps.config.permissions,
2082
+ childRuntime: input.deps.childRuntime,
2038
2083
  }),
2039
2084
  cwd: effectiveCwd,
2040
2085
  maxOutput: input.params.maxOutput ?? recoveryDescriptor?.maxOutput,
@@ -2059,12 +2104,13 @@ async function resumeAsyncRun(input: {
2059
2104
  thinkingCeiling: recoveryDescriptor?.thinkingCeiling ?? ("thinkingCeiling" in target ? target.thinkingCeiling : undefined),
2060
2105
  extensionBindings: recoveryDescriptor?.extensionBindings ?? ("extensionBindings" in target ? target.extensionBindings : undefined),
2061
2106
  outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
2062
- maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
2107
+ maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
2063
2108
  waitToolEnabled: input.deps.waitToolEnabled,
2064
2109
  waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
2065
2110
  worktreeSetupHook: input.deps.config.worktreeSetupHook,
2066
2111
  worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
2067
2112
  worktreeBaseDir: input.deps.config.worktreeBaseDir,
2113
+ baseRef: input.params.baseRef ?? recoveryDescriptor?.baseRef,
2068
2114
  worktreeProvider: input.deps.config.worktreeProvider,
2069
2115
  worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
2070
2116
  // A retained async child already owns the recorded worktree. Resume it in
@@ -2078,6 +2124,7 @@ async function resumeAsyncRun(input: {
2078
2124
  availableModels,
2079
2125
  output: input.params.output !== undefined ? input.params.output : foregroundContract?.output ?? recoveryDescriptor?.outputPath,
2080
2126
  outputMode: input.params.outputMode ?? foregroundContract?.outputMode ?? recoveryDescriptor?.outputMode,
2127
+ outputClaimPath: input.params.workflowOutputClaimPath,
2081
2128
  ...(agentContract ? { agentContract } : {}),
2082
2129
  ...(outputSchema ? { structuredOutputSchema: outputSchema } : {}),
2083
2130
  ...(recoveryDescriptor?.skills ? { skills: [...recoveryDescriptor.skills] } : {}),
@@ -2154,6 +2201,7 @@ async function resumeAsyncRun(input: {
2154
2201
  ...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
2155
2202
  ...(completed.acceptance ? { acceptance: completed.acceptance } : {}),
2156
2203
  ...(completed.artifactPaths ? { artifactPaths: completed.artifactPaths } : {}),
2204
+ ...(completed.savedOutputPath ? { savedOutputPath: completed.savedOutputPath } : {}),
2157
2205
  ...(completed.outputSaveError ? { outputSaveError: completed.outputSaveError } : {}),
2158
2206
  ...(completed.transcriptPath ? { transcriptPath: completed.transcriptPath } : {}),
2159
2207
  ...(completed.transcriptError ? { transcriptError: completed.transcriptError } : {}),
@@ -3129,6 +3177,7 @@ async function waitForWorkflowAsyncSingleResult(
3129
3177
  ...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
3130
3178
  ...(completed.acceptance ? { acceptance: completed.acceptance } : {}),
3131
3179
  ...(completed.artifactPaths ? { artifactPaths: completed.artifactPaths } : {}),
3180
+ ...(completed.savedOutputPath ? { savedOutputPath: completed.savedOutputPath } : {}),
3132
3181
  ...(completed.outputSaveError ? { outputSaveError: completed.outputSaveError } : {}),
3133
3182
  ...(completed.transcriptPath ? { transcriptPath: completed.transcriptPath } : {}),
3134
3183
  ...(completed.transcriptError ? { transcriptError: completed.transcriptError } : {}),
@@ -3186,11 +3235,13 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3186
3235
  currentModelProvider: parentModel?.provider,
3187
3236
  currentModel: parentModel,
3188
3237
  modelScope: data.modelScope,
3238
+ modelResponseAliases: deps.config.modelResponseAliases,
3189
3239
  interactive: ctx.hasUI,
3190
3240
  permissions: deps.config.permissions,
3241
+ childRuntime: deps.childRuntime,
3191
3242
  });
3192
3243
  const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
3193
- const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
3244
+ const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
3194
3245
  const currentProvider = parentModel?.provider;
3195
3246
  const controlIntercomTarget = resolveRunLevelIntercomTarget(intercomBridge, contextPolicy);
3196
3247
  const childIntercomTarget = resolveChildIntercomTargetFactory(intercomBridge, contextPolicy, id);
@@ -3270,6 +3321,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3270
3321
  worktreeSetupHook: deps.config.worktreeSetupHook,
3271
3322
  worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
3272
3323
  worktreeBaseDir: deps.config.worktreeBaseDir,
3324
+ baseRef: params.baseRef,
3273
3325
  worktreeProvider: deps.config.worktreeProvider,
3274
3326
  worktreeBranchPrefix: deps.config.worktreeBranchPrefix,
3275
3327
  controlConfig,
@@ -3300,34 +3352,38 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3300
3352
  return null;
3301
3353
  }
3302
3354
 
3303
- function createSingleWorktreeSetup(
3304
- enabled: boolean | undefined,
3355
+ async function createSingleWorktreeSetup(
3305
3356
  cwd: string,
3306
3357
  runId: string,
3307
3358
  agent: string,
3308
3359
  setupHook: ExtensionConfig["worktreeSetupHook"],
3309
3360
  setupHookTimeoutMs: ExtensionConfig["worktreeSetupHookTimeoutMs"],
3310
3361
  baseDir: ExtensionConfig["worktreeBaseDir"],
3362
+ baseRef: string | undefined,
3311
3363
  provider: ExtensionConfig["worktreeProvider"],
3312
3364
  branchPrefix: ExtensionConfig["worktreeBranchPrefix"],
3313
3365
  label?: string,
3314
3366
  task?: string,
3315
- beforeCreate?: (setup: WorktreeSetup) => void,
3316
- ): { setup?: WorktreeSetup; errorResult?: AgentToolResult<Details> } {
3317
- if (!enabled) return {};
3367
+ onProgress?: (snapshot: WorktreeSetupProgress) => void,
3368
+ signal?: AbortSignal,
3369
+ deadlineAt?: number,
3370
+ ): Promise<{ setup?: WorktreeSetup; errorResult?: AgentToolResult<Details> }> {
3318
3371
  try {
3319
3372
  return {
3320
- setup: createWorktrees(cwd, runId, 1, omitUndefinedProperties({
3373
+ setup: await createWorktrees(cwd, runId, 1, omitUndefinedProperties({
3321
3374
  agents: [agent],
3322
3375
  setupHook: setupHook
3323
3376
  ? { hookPath: setupHook, ...(setupHookTimeoutMs === undefined ? {} : { timeoutMs: setupHookTimeoutMs }) }
3324
3377
  : undefined,
3325
3378
  baseDir,
3379
+ baseRef,
3326
3380
  provider,
3327
3381
  branchPrefix,
3328
3382
  labels: [label],
3329
3383
  tasks: [task],
3330
- beforeCreate,
3384
+ onProgress,
3385
+ signal,
3386
+ deadlineAt,
3331
3387
  })),
3332
3388
  };
3333
3389
  } catch (error) {
@@ -3353,15 +3409,6 @@ function resolveSingleRunOutputBaseDir(deps: ExecutorDeps, artifactsDir: string,
3353
3409
  return resolveConfiguredSingleRunOutputBaseDir(deps) ?? path.join(artifactsDir, "outputs", sanitizeRunPathSegment(runId));
3354
3410
  }
3355
3411
 
3356
- function resolveWorkflowAggregateOutputPath(
3357
- output: string | boolean | undefined,
3358
- ctxCwd: string,
3359
- workflowCwd: string,
3360
- outputBaseDir: string,
3361
- ): string | undefined {
3362
- return resolveSingleOutputPath(output, ctxCwd, workflowCwd, outputBaseDir);
3363
- }
3364
-
3365
3412
  function workflowChildDefaultOutput(aggregateOutputPath: string | undefined, artifactsDir: string, workflowRunId: string, workflowKey: string): string {
3366
3413
  if (aggregateOutputPath) {
3367
3414
  const parsed = path.parse(aggregateOutputPath);
@@ -3423,7 +3470,9 @@ function resolveWorkflowChildOutputPath(input: {
3423
3470
  key: string;
3424
3471
  params: Record<string, unknown>;
3425
3472
  }): { path?: string; inherited: boolean } {
3426
- if (typeof input.params.resume === "string") {
3473
+ const rawOutput = input.params.output;
3474
+ const hasExplicitOutput = typeof rawOutput === "string" || typeof rawOutput === "boolean";
3475
+ if (typeof input.params.resume === "string" && (!hasExplicitOutput || rawOutput === true || rawOutput === "true")) {
3427
3476
  if (!input.state) return { path: undefined, inherited: false };
3428
3477
  const index = input.params.index;
3429
3478
  const target = resolveResumeTarget({
@@ -3432,15 +3481,16 @@ function resolveWorkflowChildOutputPath(input: {
3432
3481
  }, input.state);
3433
3482
  return { path: "recoveryDescriptor" in target ? target.recoveryDescriptor?.outputPath : undefined, inherited: false };
3434
3483
  }
3435
- const rawOutput = input.params.output;
3436
- const hasExplicitOutput = typeof rawOutput === "string" || typeof rawOutput === "boolean";
3437
3484
  const childCwd = typeof input.params.cwd === "string" ? resolveChildCwd(input.workflowCwd, input.params.cwd) : input.workflowCwd;
3438
- const agentScope = resolveExecutionAgentScope(input.params.agentScope ?? input.workflowAgentScope);
3439
- const discoveredAgents = input.discoverAgents(childCwd, agentScope).agents;
3440
- const agent = typeof input.params.agent === "string"
3441
- ? resolveAgentName(input.params.agent, discoveredAgents).agent ?? resolveAgentName(input.params.agent, input.agents).agent
3442
- : undefined;
3443
- const agentOutput = typeof agent?.output === "string" ? agent.output : undefined;
3485
+ let agentOutput: string | undefined;
3486
+ if (rawOutput === true || rawOutput === "true" || (!hasExplicitOutput && !input.aggregateOutputPath)) {
3487
+ const agentScope = resolveExecutionAgentScope(input.params.agentScope ?? input.workflowAgentScope);
3488
+ const discoveredAgents = input.discoverAgents(childCwd, agentScope).agents;
3489
+ const agent = typeof input.params.agent === "string"
3490
+ ? resolveAgentName(input.params.agent, discoveredAgents).agent ?? resolveAgentName(input.params.agent, input.agents).agent
3491
+ : undefined;
3492
+ agentOutput = typeof agent?.output === "string" ? agent.output : undefined;
3493
+ }
3444
3494
  const output = rawOutput === true || rawOutput === "true"
3445
3495
  ? agentOutput
3446
3496
  : hasExplicitOutput
@@ -3529,7 +3579,7 @@ function prepareWorkflowChildLaunchParams(input: {
3529
3579
  childParams = { ...input.childParams, output: input.outputOverride };
3530
3580
  } else if (usesDefaultOutput && input.aggregateOutputPath !== undefined) {
3531
3581
  childParams = { ...input.childParams, output: workflowChildDefaultOutput(input.aggregateOutputPath, input.artifactsDir, input.parentWorkflowRunId, input.workflowKey) };
3532
- } else if (input.childParams.resume === undefined) {
3582
+ } else if (input.childParams.resume === undefined || input.childParams.output !== undefined) {
3533
3583
  const resolvedOutput = resolveWorkflowChildOutputPath({ ctxCwd: input.ctxCwd, workflowCwd: input.workflowCwd, artifactsDir: input.artifactsDir, workflowRunId: input.parentWorkflowRunId, aggregateOutputPath: input.aggregateOutputPath, configuredOutputBaseDir: input.configuredOutputBaseDir, discoverAgents: input.discoverAgents, agents: input.agents, workflowAgentScope: input.workflowAgentScope, key: input.workflowKey, params: input.childParams });
3534
3584
  if (resolvedOutput.path) childParams = { ...input.childParams, output: resolvedOutput.path };
3535
3585
  }
@@ -3543,7 +3593,7 @@ function prepareWorkflowChildLaunchParams(input: {
3543
3593
  return prepareWorkflowLaunchParams(input.workflowDefaults, childParams, input.parentWorkflowRunId, input.workflowKey, { ...input.options, externalAsyncRequired, outputClaimPath: input.outputClaimPath });
3544
3594
  }
3545
3595
 
3546
- function finalizeSingleWorktreeHandoff(input: {
3596
+ async function finalizeSingleWorktreeHandoff(input: {
3547
3597
  worktreeSetup: WorktreeSetup;
3548
3598
  artifactsDir: string;
3549
3599
  runId: string;
@@ -3552,55 +3602,78 @@ function finalizeSingleWorktreeHandoff(input: {
3552
3602
  result: SingleResult;
3553
3603
  workflowKey?: string;
3554
3604
  lane?: import("../../shared/types.ts").WorkflowLaneMetadata;
3555
- }): { suffix: string; reference?: NonNullable<Details["parallelHandoff"]> } {
3556
- const diffsDir = path.join(input.artifactsDir, "worktree-diffs", input.runId);
3557
- const diffs = diffWorktrees(input.worktreeSetup, [input.agent], diffsDir);
3558
- const diffSummary = formatWorktreeDiffSummary(diffs);
3559
- const manifestPath = parallelHandoffPath(input.artifactsDir, input.runId);
3560
- const handoff = {
3561
- manifestPath,
3562
- runId: input.runId,
3563
- mode: "single" as const,
3564
- source: "foreground" as const,
3565
- cwd: input.cwd,
3566
- stepIndex: 0,
3567
- flatStartIndex: 0,
3568
- setup: input.worktreeSetup,
3569
- laneBindings: input.workflowKey || input.lane ? [{ index: 0, taskIndex: 0, ...(input.workflowKey ? { workflowKey: input.workflowKey } : {}), runId: input.workflowKey ? input.runId : undefined, ...(input.lane ? { lane: input.lane } : {}) }] : undefined,
3570
- diffs,
3571
- results: [{
3572
- agent: input.result.agent,
3573
- ...(input.workflowKey ? { workflowKey: input.workflowKey } : {}),
3574
- ...(input.workflowKey ? { runId: input.runId } : {}),
3575
- ...(input.lane ? { lane: input.lane } : {}),
3576
- status: resolveSubagentResultStatus(omitUndefinedProperties({
3577
- exitCode: input.result.exitCode,
3578
- interrupted: input.result.interrupted,
3579
- detached: input.result.detached,
3580
- state: input.result.stopped ? "stopped" : undefined,
3581
- processSignal: input.result.processSignal,
3582
- timedOut: input.result.timedOut,
3583
- stopped: input.result.stopped,
3584
- turnBudgetExceeded: input.result.turnBudgetExceeded,
3585
- })),
3586
- summary: resultSummaryForIntercom(input.result),
3587
- ...(input.result.artifactPaths?.outputPath ? { outputPath: input.result.artifactPaths.outputPath } : {}),
3588
- ...(input.result.structuredOutput !== undefined ? { structuredOutput: input.result.structuredOutput } : {}),
3589
- ...(input.result.structuredOutputPath ? { structuredOutputPath: input.result.structuredOutputPath } : {}),
3590
- ...(input.result.sessionFile ? { sessionPath: input.result.sessionFile } : {}),
3591
- }],
3592
- };
3605
+ }): Promise<{ suffix: string; reference?: NonNullable<Details["parallelHandoff"]> }> {
3606
+ let admitted = false;
3593
3607
  try {
3594
- writeParallelHandoffGroup(handoff);
3595
- const cleanup = cleanupWorktrees(input.worktreeSetup, { kind: "preserve", capturedDiffs: diffs, handoffManifestPath: manifestPath });
3596
- const reference = writeParallelHandoffGroup({ ...handoff, cleanup });
3597
- return {
3598
- suffix: [diffSummary, formatParallelHandoffReference(reference)].filter(Boolean).join("\n\n"),
3599
- reference,
3600
- };
3608
+ return await withWorktreeTransaction(() => {
3609
+ admitted = true;
3610
+ const diffsDir = path.join(input.artifactsDir, "worktree-diffs", input.runId);
3611
+ const diffs = diffWorktrees(input.worktreeSetup, [input.agent], diffsDir);
3612
+ const diffSummary = formatWorktreeDiffSummary(diffs);
3613
+ const manifestPath = parallelHandoffPath(input.artifactsDir, input.runId);
3614
+ const handoff = {
3615
+ manifestPath,
3616
+ runId: input.runId,
3617
+ mode: "single" as const,
3618
+ source: "foreground" as const,
3619
+ cwd: input.cwd,
3620
+ stepIndex: 0,
3621
+ flatStartIndex: 0,
3622
+ setup: input.worktreeSetup,
3623
+ laneBindings: input.workflowKey || input.lane ? [{ index: 0, taskIndex: 0, ...(input.workflowKey ? { workflowKey: input.workflowKey } : {}), runId: input.workflowKey ? input.runId : undefined, ...(input.lane ? { lane: input.lane } : {}) }] : undefined,
3624
+ diffs,
3625
+ results: [{
3626
+ agent: input.result.agent,
3627
+ ...(input.workflowKey ? { workflowKey: input.workflowKey } : {}),
3628
+ ...(input.workflowKey ? { runId: input.runId } : {}),
3629
+ ...(input.lane ? { lane: input.lane } : {}),
3630
+ status: resolveSubagentResultStatus(omitUndefinedProperties({
3631
+ exitCode: input.result.exitCode,
3632
+ interrupted: input.result.interrupted,
3633
+ detached: input.result.detached,
3634
+ state: input.result.stopped ? "stopped" : undefined,
3635
+ processSignal: input.result.processSignal,
3636
+ timedOut: input.result.timedOut,
3637
+ stopped: input.result.stopped,
3638
+ turnBudgetExceeded: input.result.turnBudgetExceeded,
3639
+ })),
3640
+ summary: resultSummaryForIntercom(input.result),
3641
+ ...(input.result.artifactPaths?.outputPath ? { outputPath: input.result.artifactPaths.outputPath } : {}),
3642
+ ...(input.result.structuredOutput !== undefined ? { structuredOutput: input.result.structuredOutput } : {}),
3643
+ ...(input.result.structuredOutputPath ? { structuredOutputPath: input.result.structuredOutputPath } : {}),
3644
+ ...(input.result.sessionFile ? { sessionPath: input.result.sessionFile } : {}),
3645
+ }],
3646
+ };
3647
+ try {
3648
+ writeParallelHandoffGroup(handoff);
3649
+ const cleanup = cleanupWorktrees(input.worktreeSetup, { kind: "preserve", capturedDiffs: diffs, handoffManifestPath: manifestPath });
3650
+ const reference = writeParallelHandoffGroup({ ...handoff, cleanup });
3651
+ return {
3652
+ suffix: [diffSummary, formatParallelHandoffReference(reference)].filter(Boolean).join("\n\n"),
3653
+ reference,
3654
+ };
3655
+ } catch (error) {
3656
+ return { suffix: [diffSummary, formatParallelHandoffError(error)].filter(Boolean).join("\n\n") };
3657
+ }
3658
+ });
3601
3659
  } catch (error) {
3602
- return { suffix: [diffSummary, formatParallelHandoffError(error)].filter(Boolean).join("\n\n") };
3603
- }
3660
+ if (admitted) throw error;
3661
+ const reference = retainSingleWorktreeHandoff(input, error);
3662
+ return { suffix: `${formatParallelHandoffError(error)}\n\n${formatParallelHandoffReference(reference)}`, reference };
3663
+ }
3664
+ }
3665
+
3666
+ function retainSingleWorktreeHandoff(input: { worktreeSetup: WorktreeSetup; artifactsDir: string; runId: string; cwd: string }, error: unknown): NonNullable<Details["parallelHandoff"]> {
3667
+ const reason = `Worktree finalization retained; manual reconciliation required: ${error instanceof Error ? error.message : String(error)}`;
3668
+ return writeParallelHandoffGroup({
3669
+ manifestPath: parallelHandoffPath(input.artifactsDir, input.runId), runId: input.runId,
3670
+ mode: "single", source: "foreground", cwd: input.cwd, stepIndex: 0, flatStartIndex: 0,
3671
+ setup: input.worktreeSetup, diffs: [], results: [],
3672
+ cleanup: { state: "partial", pruned: false, errors: [reason], tasks: input.worktreeSetup.worktrees.map((worktree) => ({
3673
+ index: worktree.index, path: worktree.path, branch: worktree.branch, provider: worktree.provider, naming: worktree.naming,
3674
+ worktreeRemoved: false, branchRemoved: false, preserved: true, reason,
3675
+ })) },
3676
+ });
3604
3677
  }
3605
3678
 
3606
3679
  async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Promise<AgentToolResult<Details>> {
@@ -3675,26 +3748,26 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3675
3748
  const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
3676
3749
  let effectiveOutput = normalizeSingleOutputOverride(rawOutput, agentConfig.output);
3677
3750
  const effectiveOutputMode = params.outputMode ?? agentConfig.outputMode ?? "inline";
3678
- const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
3751
+ const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
3679
3752
  const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
3680
3753
 
3681
3754
 
3682
3755
  const sourceCwd = effectiveCwd;
3683
3756
  let pendingHandoff: Details["parallelHandoff"];
3684
- const { setup: worktreeSetup, errorResult: worktreeSetupError } = createSingleWorktreeSetup(
3685
- params.worktree,
3757
+ const { setup: worktreeSetup, errorResult: worktreeSetupError } = params.worktree ? await createSingleWorktreeSetup(
3686
3758
  sourceCwd,
3687
3759
  runId,
3688
3760
  params.agent!,
3689
3761
  deps.config.worktreeSetupHook,
3690
3762
  deps.config.worktreeSetupHookTimeoutMs,
3691
3763
  deps.config.worktreeBaseDir,
3764
+ params.baseRef,
3692
3765
  deps.config.worktreeProvider,
3693
3766
  deps.config.worktreeBranchPrefix,
3694
3767
  params.lane?.key ?? params.workflowKey,
3695
3768
  task,
3696
- (plannedSetup) => {
3697
- pendingHandoff = writePendingParallelHandoff({
3769
+ (progress) => {
3770
+ pendingHandoff = writeWorktreeSetupHandoff({
3698
3771
  manifestPath: parallelHandoffPath(artifactsDir, runId),
3699
3772
  runId,
3700
3773
  mode: "single",
@@ -3702,13 +3775,42 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3702
3775
  cwd: sourceCwd,
3703
3776
  stepIndex: 0,
3704
3777
  flatStartIndex: 0,
3705
- setup: plannedSetup,
3778
+ progress,
3706
3779
  laneBindings: params.workflowKey || lane ? [{ index: 0, taskIndex: 0, ...(params.workflowKey ? { workflowKey: params.workflowKey, runId } : {}), ...(lane ? { lane } : {}) }] : undefined,
3707
3780
  });
3708
3781
  },
3709
- );
3710
- if (worktreeSetupError) return worktreeSetupError;
3782
+ signal,
3783
+ data.deadlineAt,
3784
+ ) : {};
3785
+ if (worktreeSetupError) {
3786
+ if (pendingHandoff) {
3787
+ worktreeSetupError.details!.parallelHandoff = pendingHandoff;
3788
+ worktreeSetupError.content.push({ type: "text", text: formatParallelHandoffReference(pendingHandoff) });
3789
+ }
3790
+ return worktreeSetupError;
3791
+ }
3711
3792
  const singleCwd = worktreeSetup?.worktrees[0]?.agentCwd ?? sourceCwd;
3793
+ const cleanupSingleWorktree = async () => {
3794
+ if (!worktreeSetup) return;
3795
+ let admitted = false;
3796
+ try { await withWorktreeTransaction(() => {
3797
+ if (data.deadlineAt !== undefined && Date.now() >= data.deadlineAt) throw new Error("Run deadline expired before unlaunched worktree cleanup");
3798
+ admitted = true;
3799
+ const cleanup = cleanupWorktrees(worktreeSetup);
3800
+ pendingHandoff = writeParallelHandoffGroup({
3801
+ manifestPath: parallelHandoffPath(artifactsDir, runId), runId, mode: "single", source: "foreground",
3802
+ cwd: sourceCwd, stepIndex: 0, flatStartIndex: 0, setup: worktreeSetup, diffs: [], results: [], cleanup,
3803
+ });
3804
+ }); }
3805
+ catch (error) {
3806
+ if (admitted) throw error;
3807
+ pendingHandoff = retainSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd }, error);
3808
+ }
3809
+ };
3810
+ if (worktreeSetup && (signal?.aborted || (data.deadlineAt !== undefined && Date.now() >= data.deadlineAt))) {
3811
+ await cleanupSingleWorktree();
3812
+ return { content: [{ type: "text", text: "Subagent setup canceled before child launch." }], isError: true, details: { mode: "single", results: [], parallelHandoff: pendingHandoff } };
3813
+ }
3712
3814
 
3713
3815
  const authoredTask = task;
3714
3816
  if (shouldForkAgent(contextPolicy, params.agent!)) {
@@ -3718,8 +3820,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3718
3820
  const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, singleCwd, resolveSingleRunOutputBaseDir(deps, artifactsDir, runId));
3719
3821
  const validationError = validateFileOnlyOutputMode(effectiveOutputMode, outputPath, `Single run (${params.agent})`);
3720
3822
  if (validationError) {
3721
- if (worktreeSetup) cleanupWorktrees(worktreeSetup);
3722
- return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [] } };
3823
+ await cleanupSingleWorktree();
3824
+ return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [], parallelHandoff: pendingHandoff } };
3723
3825
  }
3724
3826
  const structuredRuntime = params.outputSchema
3725
3827
  ? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ? path.join(artifactsDir, "structured-output", runId) : undefined, { acceptanceReport: resolveAcceptanceReportMode(params.acceptance) })
@@ -3742,6 +3844,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3742
3844
  }
3743
3845
  const interruptController = new AbortController();
3744
3846
  let detachForeground: ((reason?: string) => boolean) | undefined;
3847
+ let childSessionControls: ForegroundChildSessionControls | undefined;
3745
3848
  const foregroundControl = deps.state.foregroundControls.get(runId);
3746
3849
  if (foregroundControl) {
3747
3850
  const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin));
@@ -3762,9 +3865,23 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3762
3865
  return true;
3763
3866
  },
3764
3867
  detach: () => detachForeground?.("user request") === true,
3868
+ steer: async (input: ForegroundSteerInput): Promise<ForegroundSteerOutcome> => {
3869
+ if (!childSessionControls) return { state: "failed", reason: CHILD_SESSION_NOT_RUNNING_YET };
3870
+ try {
3871
+ if (input.mode === "follow_up") {
3872
+ await childSessionControls.followUp(input.message);
3873
+ return { state: "queued" };
3874
+ }
3875
+ await childSessionControls.steer(input.message);
3876
+ return { state: "delivered" };
3877
+ } catch (error) {
3878
+ return { state: "failed", reason: error instanceof Error ? error.message : String(error) };
3879
+ }
3880
+ },
3765
3881
  }));
3766
3882
  }
3767
3883
 
3884
+ const modelResponseAliases = deps.config.modelResponseAliases === undefined ? undefined : structuredClone(deps.config.modelResponseAliases);
3768
3885
  const forwardSingleUpdate = onUpdate
3769
3886
  ? (update: AgentToolResult<Details>) => {
3770
3887
  if (foregroundControl) updateForegroundChild(foregroundControl, 0, update.details?.progress?.[0]);
@@ -3784,7 +3901,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3784
3901
  runtimeSnapshotHost: deps.pi,
3785
3902
  parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
3786
3903
  llmIntentArbiter: createTaskMutationArbiter(ctx),
3787
- ...workflowForegroundSteeringLaunchOptions(foregroundControl, 0),
3904
+ childRuntime: deps.childRuntime,
3905
+ onChildSession: (controls) => { childSessionControls = controls; },
3788
3906
  context: data.contextPolicy.contextForAgent(params.agent!),
3789
3907
  unknownAgentDiagnosticContext: data.unknownAgentDiagnosticContext,
3790
3908
  runFanoutBudget: params.runFanoutAdmitted ? data.runFanoutBudget : { ...data.runFanoutBudget, parentPath: `${data.runFanoutBudget.parentPath ? `${data.runFanoutBudget.parentPath}/` : ""}single` },
@@ -3823,6 +3941,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3823
3941
  thinkingCeiling: agentConfig.maxThinking,
3824
3942
  extensionBindings: params.extensionBindings,
3825
3943
  availableModels,
3944
+ modelResponseAliases,
3826
3945
  preferredModelProvider: currentProvider,
3827
3946
  modelScope: modelScopes,
3828
3947
  skills: effectiveSkills,
@@ -3835,14 +3954,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3835
3954
  onDetachReady: (detach) => {
3836
3955
  detachForeground = detach;
3837
3956
  },
3838
- onDetachedExit: (result) => {
3957
+ onDetachedExit: async (result) => {
3839
3958
  if (resolveDetachedWorkflowChild) {
3840
3959
  resolveDetachedWorkflowChild(result);
3841
3960
  return;
3842
3961
  }
3843
3962
  try {
3844
3963
  if (worktreeSetup) {
3845
- finalizeSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd, agent: params.agent!, result, workflowKey: params.workflowKey, lane });
3964
+ await finalizeSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd, agent: params.agent!, result, workflowKey: params.workflowKey, lane });
3846
3965
  }
3847
3966
  try {
3848
3967
  updateRememberedForegroundChild(deps.state, { runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, index: 0, result, events: deps.pi.events, notify: true });
@@ -3867,7 +3986,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3867
3986
  try {
3868
3987
  if (foregroundControl) finishForegroundChild(foregroundControl, 0);
3869
3988
  } finally {
3870
- removeForegroundControlIfIdle(deps.state, runId);
3989
+ removeForegroundControlIfIdle(deps.state, runId, deps.trackRetainedNestedRoute);
3871
3990
  }
3872
3991
  }
3873
3992
  }
@@ -3878,12 +3997,13 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3878
3997
  toolTimeoutMs: params.toolTimeoutMs,
3879
3998
  configToolTimeoutMs: data.configToolTimeoutMs,
3880
3999
  toolBudget: effectiveToolBudget.toolBudget,
4000
+ usageBudget: data.usageBudget ?? data.inheritedUsageBudget,
3881
4001
  capabilityCeiling: data.capabilityCeiling,
3882
4002
  allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
3883
4003
  }));
3884
4004
  r = launched.detached && detachedWorkflowChild ? await detachedWorkflowChild : launched;
3885
4005
  } catch (error) {
3886
- if (worktreeSetup) cleanupWorktrees(worktreeSetup);
4006
+ await cleanupSingleWorktree();
3887
4007
  throw error;
3888
4008
  } finally {
3889
4009
  // An attached runSync rejection still owns its child and structured runtime.
@@ -3898,11 +4018,11 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3898
4018
  recordRun(params.agent!, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0, r);
3899
4019
  }
3900
4020
 
3901
- let worktreeHandoff: ReturnType<typeof finalizeSingleWorktreeHandoff> | undefined;
4021
+ let worktreeHandoff: Awaited<ReturnType<typeof finalizeSingleWorktreeHandoff>> | undefined;
3902
4022
  if (worktreeSetup) {
3903
4023
  worktreeHandoff = r.detached
3904
4024
  ? { suffix: pendingHandoff ? formatParallelHandoffReference(pendingHandoff) : "", reference: pendingHandoff }
3905
- : finalizeSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd, agent: params.agent!, result: r, workflowKey: params.workflowKey, lane });
4025
+ : await finalizeSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd, agent: params.agent!, result: r, workflowKey: params.workflowKey, lane });
3906
4026
  }
3907
4027
 
3908
4028
  if (r.progress) allProgress.push(r.progress);
@@ -3940,7 +4060,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3940
4060
  usageBudget: usageBudgetState(data.usageBudget, totalCost),
3941
4061
  ...(worktreeHandoff?.reference ? { parallelHandoff: worktreeHandoff.reference } : {}),
3942
4062
  }));
3943
- rememberForegroundRun(deps.state, { runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: details.results, params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings });
4063
+ rememberForegroundRun(deps.state, { modelResponseAliases, runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: details.results, params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings });
3944
4064
 
3945
4065
  const suppressRoutineResultIntercom = shouldSuppressRoutineResultIntercom({ suppressRoutineResultIntercom: params.suppressRoutineResultIntercom, results: [r] });
3946
4066
  if (!r.detached && !r.interrupted && !suppressRoutineResultIntercom) {
@@ -4016,6 +4136,7 @@ function duplicateSubagentCallResult(params: SubagentParamsLike): AgentToolResul
4016
4136
  }
4017
4137
 
4018
4138
  const workflowLaunchObservers = new WeakMap<object, (launch: { agent: string; sessionName?: string; sessionFile?: string; async: boolean; runId?: string }) => void>();
4139
+ const workflowOwnedUsageBudgets = new WeakMap<object, UsageBudgetConfig>();
4019
4140
 
4020
4141
  /**
4021
4142
  * Terminal-mission retention can remove a mission while its children still
@@ -4096,9 +4217,13 @@ function workflowChildResult(
4096
4217
  ? result.details.results[0].finalOutput
4097
4218
  : receiptOutput;
4098
4219
  const childError = result.details.results.map((child) => child.error).find((error): error is string => Boolean(error));
4099
- const failureError = childError && receiptOutput && receiptOutput !== childError
4100
- ? `${childError}\n\n${receiptOutput}`
4220
+ const failureErrorBase = childError && receiptOutput
4221
+ ? receiptOutput.includes(childError) ? receiptOutput : `${childError}\n\n${receiptOutput}`
4101
4222
  : childError || receiptOutput || output || "Child run failed.";
4223
+ const savedOutputEvidence = [...new Set(result.details.results.map((child) => child.savedOutputPath).filter((value): value is string => Boolean(value)))]
4224
+ .filter((savedOutputPath) => !failureErrorBase.includes(savedOutputPath))
4225
+ .map((savedOutputPath) => `Saved output: ${savedOutputPath}`);
4226
+ const failureError = [failureErrorBase, ...savedOutputEvidence].join("\n");
4102
4227
  const detached = result.details.results.some((child) => child.detached);
4103
4228
  const interrupted = result.details.results.some((child) => child.interrupted);
4104
4229
  const stopped = result.details.results.some((child) => child.stopped);
@@ -4137,8 +4262,6 @@ function workflowChildResult(
4137
4262
  }
4138
4263
  const requestedContext = childParams.context === "fresh" || childParams.context === "fork" ? childParams.context : undefined;
4139
4264
  const resolvedContext = result.details.context ?? (resolvedContexts.length === 1 ? resolvedContexts[0] : resolvedContexts.length > 1 ? "mixed" : undefined);
4140
- const resumeSourceRunId = typeof childParams.resume === "string" && childParams.resume.trim() ? childParams.resume.trim() : undefined;
4141
- const continuationRunIds = [...new Set([resumeSourceRunId, runId].filter((value): value is string => Boolean(value)))];
4142
4265
  const outputReference = result.details.results.find((child) => child.savedOutputPath)?.savedOutputPath
4143
4266
  ?? result.details.results.find((child) => child.outputReference?.path)?.outputReference?.path;
4144
4267
  const outputPathMapping = typeof childParams.task === "string" ? outputPathMappingFromTask(childParams.task, outputReference) : undefined;
@@ -4171,7 +4294,7 @@ function workflowChildResult(
4171
4294
  ...(outputPathMapping ? { outputPathMapping } : {}),
4172
4295
  ...(externalAdapter ? { externalAdapter } : {}),
4173
4296
  resumability,
4174
- continuation: { runIds: continuationRunIds },
4297
+ continuation: { runIds: runId ? [runId] : [] },
4175
4298
  artifactPaths: [...artifactPaths],
4176
4299
  results: result.details.results,
4177
4300
  };
@@ -4216,6 +4339,7 @@ function workflowSteerReceipt(key: string, result: AgentToolResult<Details>): Wo
4216
4339
  };
4217
4340
  }
4218
4341
 
4342
+ const CHILD_SESSION_NOT_RUNNING_YET = "Child session is not running yet.";
4219
4343
  const MAX_WORKFLOW_RESUME_HINT_BYTES = 1024;
4220
4344
  const MAX_WORKFLOW_CHILD_RUN_ID_BYTES = 256;
4221
4345
  const WORKFLOW_RESUME_HINT_PARENT_STATES = new Set(["complete", "failed", "partial"]);
@@ -4283,10 +4407,33 @@ function missingWorkflowReceiptResumeHint(reference: WorkflowReceiptResumeRefere
4283
4407
  }
4284
4408
  }
4285
4409
 
4286
- function resolveKeyedWorkflowResume(
4287
- reference: WorkflowReceiptResumeReference,
4288
- state: SubagentState,
4289
- ): { runId: string; runIds: string[] } {
4410
+ function resolveWorkflowResume(
4411
+ reference: WorkflowReceiptResumeReference | string,
4412
+ deps: ExecutorDeps,
4413
+ parentSessionFile: string | null,
4414
+ index?: number,
4415
+ ): string | { runId: string; runIds: string[] } {
4416
+ const state = deps.state;
4417
+ if (typeof reference === "string") {
4418
+ const target = resolveRequestedResumeTarget({ id: reference.trim(), ...(index !== undefined ? { index } : {}) }, deps, parentSessionFile);
4419
+ // Live routing stays with action=resume and does not claim retained lineage.
4420
+ if (target.kind !== "revive") return reference.trim();
4421
+ const runId = target.runId;
4422
+ const status = target.source === "async" && target.asyncDir ? readStatus(target.asyncDir) : undefined;
4423
+ if (status?.runId === runId && status.parentWorkflowRunId && status.workflowKey) {
4424
+ // Only follow the validated child's recorded owner; never search other workflows.
4425
+ const receiptPath = workflowReceiptPath(DIRS.async, status.parentWorkflowRunId);
4426
+ try {
4427
+ fs.statSync(receiptPath);
4428
+ } catch (error) {
4429
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return { runId, runIds: [runId] };
4430
+ throw error;
4431
+ }
4432
+ const entry = readWorkflowReceipt(DIRS.async, status.parentWorkflowRunId).entries[status.workflowKey];
4433
+ if (entry?.latestRunId === runId) return { runId, runIds: entry.continuation.runIds };
4434
+ }
4435
+ return { runId, runIds: [runId] };
4436
+ }
4290
4437
  try {
4291
4438
  const entry = resolveWorkflowReceiptResumeEntry({
4292
4439
  reference,
@@ -4296,9 +4443,7 @@ function resolveKeyedWorkflowResume(
4296
4443
  if (target.kind !== "revive") throw new Error(`Workflow receipt child '${reference.key}' latest run '${runId}' is still running.`);
4297
4444
  },
4298
4445
  });
4299
- const runId = entry.latestRunId;
4300
- if (!runId) throw new Error(`Workflow receipt child '${reference.key}' has no retained run id.`);
4301
- return { runId, runIds: entry.continuation.runIds };
4446
+ return { runId: entry.latestRunId, runIds: entry.continuation.runIds };
4302
4447
  } catch (error) {
4303
4448
  const workflowRunId = reference.workflowRunId.trim();
4304
4449
  if (isMissingWorkflowReceiptDiagnostic(error, workflowRunId)) {
@@ -4351,7 +4496,6 @@ export async function steerWorkflowChildByKey(input: {
4351
4496
  while (true) {
4352
4497
  const control = [...input.state.foregroundControls.values()].find((candidate) => candidate.parentWorkflowRunId === input.workflowRunId
4353
4498
  && candidate.workflowKey === input.key
4354
- && Boolean(candidate.workflowSteeringDir)
4355
4499
  && (candidate.activeChildren?.size ?? 0) > 0);
4356
4500
  if (control) {
4357
4501
  const result = await steerWorkflowForegroundTarget({
@@ -4359,10 +4503,9 @@ export async function steerWorkflowChildByKey(input: {
4359
4503
  message: input.message,
4360
4504
  mode: input.options.mode,
4361
4505
  index: input.options.index,
4362
- ackTimeoutMs: Math.max(1, deadline - Date.now()),
4363
- signal: input.signal,
4364
4506
  });
4365
- return workflowSteerReceipt(input.key, result);
4507
+ // The control registers before its child session exists; keep polling until the steer can route.
4508
+ if (!result.details.steering?.targets.some((target) => target.reason === CHILD_SESSION_NOT_RUNNING_YET) || Date.now() >= deadline) return workflowSteerReceipt(input.key, result);
4366
4509
  }
4367
4510
 
4368
4511
  const workflowStatus = readStatus(path.join(asyncDirRoot, input.workflowRunId));
@@ -4435,10 +4578,11 @@ export function prepareWorkflowLaunchParams(
4435
4578
  const toolBudget = childParams.toolBudget ?? workflowDefaults.toolBudget;
4436
4579
  const intercomBridge = childParams.intercomBridge ?? workflowDefaults.intercomBridge;
4437
4580
  const worktree = childParams.worktree ?? workflowDefaults.worktree;
4581
+ const baseRef = Object.hasOwn(childParams, "baseRef") ? childParams.baseRef : undefined;
4438
4582
  const outputSchema = Object.hasOwn(childParams, "outputSchema") ? childParams.outputSchema : workflowDefaults.outputSchema;
4439
4583
  if (outputSchema !== undefined) assertJsonSchemaObject(outputSchema, "outputSchema");
4440
4584
  const agentContract = Object.hasOwn(childParams, "agentContract") ? childParams.agentContract : workflowDefaults.agentContract;
4441
- if (agentContract !== undefined && !isAgentContractV1(agentContract as AgentContract)) throw new Error("agentContract must be { version: 1 }.");
4585
+ if (agentContract !== undefined && !isAgentContract(agentContract as AgentContract)) throw new Error("agentContract must be { version: 1 }.");
4442
4586
  const acceptance = Object.hasOwn(childParams, "acceptance") ? childParams.acceptance : workflowDefaults.acceptance;
4443
4587
  const output = Object.hasOwn(childParams, "output") ? childParams.output : workflowDefaults.output;
4444
4588
  if (output !== undefined && typeof output !== "string" && typeof output !== "boolean") throw new Error("output must be a path string or boolean.");
@@ -4452,8 +4596,10 @@ export function prepareWorkflowLaunchParams(
4452
4596
  message: typeof childParams.task === "string" ? childParams.task.trim() : "",
4453
4597
  workflowParentRunId: parentWorkflowRunId,
4454
4598
  workflowKey,
4599
+ ...(options.outputClaimPath ? { workflowOutputClaimPath: options.outputClaimPath } : {}),
4455
4600
  ...(lane ? { lane } : {}),
4456
4601
  ...(worktree !== undefined ? { worktree: worktree as boolean } : {}),
4602
+ ...(baseRef !== undefined ? { baseRef: baseRef as string } : {}),
4457
4603
  ...(outputSchema !== undefined ? { outputSchema: outputSchema as JsonSchemaObject } : {}),
4458
4604
  ...(agentContract !== undefined ? { agentContract: agentContract as AgentContract } : {}),
4459
4605
  ...(acceptance !== undefined ? { acceptance: acceptance as AcceptanceInput } : {}),
@@ -4469,12 +4615,13 @@ export function prepareWorkflowLaunchParams(
4469
4615
  };
4470
4616
  }
4471
4617
  const control = mergeWorkflowControlOverrides(workflowDefaults.control, childParams.control as ControlConfig | undefined);
4618
+ const asyncOmitted = childParams.async === undefined && workflowDefaults.async === undefined;
4472
4619
  const launchParams = {
4473
4620
  ...workflowDefaults,
4474
- async: options.externalAsyncRequired === true && childParams.async === undefined && workflowDefaults.async === undefined ? true : false,
4621
+ ...(options.externalAsyncRequired === true && asyncOmitted ? { async: true } : {}),
4475
4622
  ...childParams,
4476
4623
  ...(control !== undefined ? { control } : {}),
4477
- ...(options.externalAsyncRequired === true && childParams.async === undefined && workflowDefaults.async === undefined ? { workflowAwaitAsync: true } : {}),
4624
+ ...(asyncOmitted ? { workflowAwaitAsync: true } : {}),
4478
4625
  ...(options.missionDetached ? { mission: false } : {}),
4479
4626
  workflowParentRunId: parentWorkflowRunId,
4480
4627
  workflowKey,
@@ -4612,14 +4759,14 @@ function applyWorkflowLaneTrace(graph: WorkflowGraphSnapshot, trace: WorkflowScr
4612
4759
  else delete graph.currentNodeId;
4613
4760
  }
4614
4761
 
4615
- function workflowChatProgressUpdate(
4762
+ function workflowProgressUpdate(
4616
4763
  runId: string,
4617
4764
  chatProgress: WorkflowChatProgressProjection,
4618
4765
  workflow: NonNullable<Details["workflow"]>,
4619
4766
  workflowChildren?: Details["workflowChildren"],
4620
- preflight?: import("../../shared/types.ts").WorkflowPreflightV1,
4621
- ): AgentToolResult<Details> | undefined {
4622
- if (chatProgress.mode !== "live-card") return undefined;
4767
+ preflight?: import("../../shared/types.ts").WorkflowPreflight,
4768
+ ): AgentToolResult<Details> {
4769
+ // chatProgress controls the TUI projection, not transport-level progress.
4623
4770
  return {
4624
4771
  content: [{ type: "text", text: "Workflow running." }],
4625
4772
  details: { mode: "workflow", runId, results: [], workflow, ...(preflight ? { preflight } : {}), ...(workflowChildren ? { workflowChildren } : {}), chatProgress },
@@ -4695,6 +4842,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4695
4842
  signal: AbortSignal,
4696
4843
  ctx: ExtensionContext,
4697
4844
  ) => Promise<AgentToolResult<Details>>;
4845
+ /** Scheduled state visible to the current runtime supervisor owner only. */
4846
+ getCurrentSupervisorOwnerStates: () => Iterable<SubagentState>;
4698
4847
  } {
4699
4848
  const delegatedThinkingOverrides = new WeakMap<object, AgentConfig["thinking"]>();
4700
4849
  const delegatedZeroToolBudgets = new WeakSet<object>();
@@ -4703,7 +4852,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4703
4852
  const workflowResourcePermits = new WeakMap<object, WorkflowResourcePermit>();
4704
4853
  const workflowPermitContexts = new WeakMap<object, { root: WorkflowChildPermit } | { child: WorkflowChildPermitContext }>();
4705
4854
  const warnedArtifactPackageDirs = new Set<string>();
4706
- const scheduledOwnerExecutors = new Map<string, ReturnType<typeof createSubagentExecutor>>();
4855
+ const scheduledOwnerExecutors = new Map<string | null, Map<string, { state: SubagentState; executor: ReturnType<typeof createSubagentExecutor> }>>();
4707
4856
  const execute = async (
4708
4857
  _id: string,
4709
4858
  params: SubagentParamsLike,
@@ -4714,6 +4863,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4714
4863
  parentModelOverride?: ParentModel | null,
4715
4864
  ): Promise<AgentToolResult<Details>> => {
4716
4865
  const workflowLaunchObserver = workflowLaunchObservers.get(params);
4866
+ const inheritedUsageBudget = workflowOwnedUsageBudgets.get(params);
4717
4867
  const delegatedThinkingOverride = delegatedThinkingOverrides.get(params);
4718
4868
  const allowZeroToolBudget = delegatedZeroToolBudgets.has(params);
4719
4869
  const delegatedExecution = delegatedExecutions.has(params);
@@ -4731,7 +4881,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4731
4881
  let requestParams = normalizedGate.params;
4732
4882
  const capacityOverrideError = validateWorkflowCapacityOverrides(requestParams);
4733
4883
  if (capacityOverrideError) return buildRequestedModeError(requestParams, capacityOverrideError);
4734
- let workflowPreflight: import("../../shared/types.ts").WorkflowPreflightV1 | undefined;
4884
+ let workflowPreflight: import("../../shared/types.ts").WorkflowPreflight | undefined;
4735
4885
  try {
4736
4886
  if (requestParams.preflight !== undefined && requestParams.workflowScript === undefined && requestParams.workflowScriptPath === undefined) {
4737
4887
  throw new Error("preflight requires workflowScript or workflowScriptPath.");
@@ -4762,7 +4912,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4762
4912
  }
4763
4913
  const normalizedAction = typeof requestParams.action === "string" ? requestParams.action.trim() : requestParams.action;
4764
4914
  if (normalizedAction === "resume" && requestParams.extensionBindings !== undefined) return buildRequestedModeError(requestParams, "extensionBindings is not supported with action='resume'; resume uses the original retained child binding.");
4765
- let workflowResource: { permit: WorkflowResourcePermit; provenance: WorkflowResourceProvenanceV1; authority: WorkflowResourceAuthority } | undefined;
4915
+ let workflowResource: { permit: WorkflowResourcePermit; provenance: WorkflowResourceProvenance; authority: WorkflowResourceAuthority } | undefined;
4766
4916
  if (workflowResourcePermit) {
4767
4917
  if (typeof requestParams.workflowScript !== "string") return buildRequestedModeError(requestParams, "Resolved workflow resource is missing its workflow script.");
4768
4918
  const consumed = consumeWorkflowResourcePermit(workflowResourcePermit, requestParams.workflowScript);
@@ -4770,8 +4920,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4770
4920
  workflowResource = { permit: workflowResourcePermit, ...consumed };
4771
4921
  }
4772
4922
  if (requestParams.workflowScript !== undefined && normalizedAction === undefined) {
4923
+ const acceptanceErrors = validateAcceptanceInput(requestParams.acceptance);
4924
+ if (acceptanceErrors.length > 0) return buildRequestedModeError(requestParams, acceptanceErrors.join(" "));
4925
+ const foregroundWorkflowRunId = encodeIndexSegment(_id);
4773
4926
  if (delegatedWorkflowPermit) {
4774
- const permitError = validateWorkflowChildPermitRoot(delegatedWorkflowPermit, _id);
4927
+ const permitError = validateWorkflowChildPermitRoot(delegatedWorkflowPermit, foregroundWorkflowRunId);
4775
4928
  if (permitError) return buildRequestedModeError(requestParams, permitError);
4776
4929
  if (requestParams.async !== false) return buildRequestedModeError(requestParams, "Workflow child permit supports foreground workflow roots only; set async:false.");
4777
4930
  }
@@ -4818,11 +4971,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4818
4971
  const scriptFirstLine = requestParams.workflowScript.split(/\r?\n/).map((line) => line.trim()).find(Boolean) || "Workflow";
4819
4972
  const boundedScriptPreview = scriptFirstLine.length > 100 ? `${scriptFirstLine.slice(0, 97)}...` : scriptFirstLine;
4820
4973
  const derivedObjective = previewAgent ? `Workflow: ${previewAgent}` : boundedScriptPreview;
4821
- const workflowDepth = checkSubagentDepth(deps.config.maxSubagentDepth).depth;
4974
+ const workflowDepth = checkSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime).depth;
4822
4975
  const asyncWorkflow = requestParams.async !== false;
4823
4976
  const topLevelAsyncWorkflow = asyncWorkflow
4824
4977
  && workflowDepth === 0
4825
- && !resolveInheritedNestedRouteFromEnv()
4978
+ && !inheritedNestedRoute(deps)
4826
4979
  && !requestParams.workflowParentRunId;
4827
4980
  const workflowRunId = asyncWorkflow ? randomUUID() : undefined;
4828
4981
  let workflowCapacity: ActiveAsyncCapacityHandle | undefined;
@@ -4847,7 +5000,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4847
5000
  let workflowFanoutBudget: RunFanoutBudgetDescriptor;
4848
5001
  try {
4849
5002
  workflowFanoutBudget = requestParams.runFanoutBudget
4850
- ?? decodeRunFanoutBudgetDescriptor(process.env[RUN_FANOUT_BUDGET_ENV])
5003
+ ?? inheritedRunFanoutBudget(deps)
4851
5004
  ?? createRunFanoutBudget(_id, requestParams.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
4852
5005
  } catch (error) {
4853
5006
  workflowCapacity?.rollback();
@@ -4946,18 +5099,31 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4946
5099
  onSuccess: (_filePath, payload) => { indexedState = (payload as { state: AsyncStatus["state"] }).state; },
4947
5100
  onError: (error, filePath) => console.error(`Failed to update async workflow index '${filePath}':`, error),
4948
5101
  });
4949
- const queueActiveRunIndex = (): void => {
4950
- const state = status.state;
5102
+ const queueActiveRunIndex = (snapshot: AsyncStatus = status): void => {
5103
+ const state = snapshot.state;
4951
5104
  if (indexedState === state && indexPersistence.pendingCount() === 0) return;
4952
- indexPersistence.write(asyncDir, { state, toolCallId: status.toolCallId }, (_filePath, payload) => {
5105
+ indexPersistence.write(asyncDir, { state, toolCallId: snapshot.toolCallId }, (_filePath, payload) => {
4953
5106
  const indexPayload = payload as { state: AsyncStatus["state"]; toolCallId?: string };
4954
5107
  updateActiveRunIndex(asyncDir, indexPayload.state, indexPayload.toolCallId, { retryCapacityErrors: true });
4955
5108
  });
4956
5109
  };
5110
+ let pendingResultPublication: Promise<boolean> | undefined;
5111
+ let settleResultPublication: ((published: boolean) => void) | undefined;
5112
+ const reportResultWriteFailure = (error: unknown): void => {
5113
+ const message = `Failed to write async workflow result ${resultPath}: ${error instanceof Error ? error.message : String(error)}`;
5114
+ console.error(message, error);
5115
+ appendWorkflowEvent({ type: "subagent.workflow.result_write_failed", error: message });
5116
+ };
4957
5117
  const runPersistence = createCapacityResilientJsonWriter({
4958
5118
  keepAlive: true,
4959
- onSuccess: (filePath) => { if (filePath === statusPath) queueActiveRunIndex(); },
4960
- onError: (error, filePath) => console.error(`Failed to persist async workflow state '${filePath}':`, error),
5119
+ onSuccess: (filePath, payload) => {
5120
+ if (filePath === statusPath) queueActiveRunIndex(payload as AsyncStatus);
5121
+ if (filePath === resultPath) settleResultPublication?.(true);
5122
+ },
5123
+ onError: (error, filePath) => {
5124
+ if (filePath === resultPath) { reportResultWriteFailure(error); settleResultPublication?.(false); }
5125
+ else console.error(`Failed to persist async workflow state '${filePath}':`, error);
5126
+ },
4961
5127
  write: (filePath, payload) => filePath === resultPath
4962
5128
  ? writeAsyncResultFile(filePath, payload as Record<string, unknown>)
4963
5129
  : writeAtomicJson(filePath, payload),
@@ -4985,7 +5151,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4985
5151
  queueActiveRunIndex();
4986
5152
  } else if (options.tolerateStatusWriteFailure) {
4987
5153
  try {
4988
- runPersistence.write(statusPath, status);
5154
+ runPersistence.write(statusPath, { ...status });
4989
5155
  statusPersistenceDegraded = false;
4990
5156
  } catch (error) {
4991
5157
  const message = `Failed to persist async workflow state ${statusPath}: ${error instanceof Error ? error.message : String(error)}`;
@@ -5000,7 +5166,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5000
5166
  }
5001
5167
  }
5002
5168
  } else {
5003
- runPersistence.write(statusPath, status);
5169
+ runPersistence.write(statusPath, { ...status });
5004
5170
  }
5005
5171
  if (liveJob) {
5006
5172
  liveJob.status = status.state;
@@ -5028,12 +5194,15 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5028
5194
  };
5029
5195
  const writeWorkflowResult = (payload: Record<string, unknown>): boolean => {
5030
5196
  try {
5197
+ let resultPublished = false;
5198
+ settleResultPublication = (published) => { resultPublished = published; };
5199
+ pendingResultPublication = undefined;
5031
5200
  runPersistence.write(resultPath, payload);
5201
+ // Only capacity deferral yields; ordinary publication remains synchronous.
5202
+ if (!resultPublished) pendingResultPublication = new Promise<boolean>((resolve) => { settleResultPublication = resolve; });
5032
5203
  return true;
5033
5204
  } catch (error) {
5034
- const message = `Failed to write async workflow result ${resultPath}: ${error instanceof Error ? error.message : String(error)}`;
5035
- console.error(message, error);
5036
- appendWorkflowEvent({ type: "subagent.workflow.result_write_failed", error: message });
5205
+ reportResultWriteFailure(error);
5037
5206
  return false;
5038
5207
  }
5039
5208
  };
@@ -5073,7 +5242,148 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5073
5242
  return { content: [{ type: "text", text: `Failed to create async workflow storage: ${error instanceof Error ? error.message : String(error)}` }], isError: true, details: { mode: "workflow", results: [] } };
5074
5243
  }
5075
5244
  appendWorkflowEvent({ type: "subagent.workflow.started" });
5245
+ // The runner does not execute workflow scripts: this active owner must consume its own
5246
+ // durable steer inbox, deliver through in-process child controls, and persist receipts.
5247
+ const emitWorkflowSteerEvent = (type: string, requestId: string, index?: number, extra: Record<string, unknown> = {}): void => {
5248
+ appendWorkflowEvent({ type, requestId, ...(index !== undefined ? { index } : {}), ...extra });
5249
+ };
5250
+ const recordWorkflowSteerTargets = (request: SteerRequest, targets: Array<{ index: number; state: SteeringTargetState; reason?: string }>): void => {
5251
+ status.steering ??= createSteeringStatus();
5252
+ recordSteeringRequest(steeringStatus(status), {
5253
+ id: request.id,
5254
+ requestedAt: request.ts,
5255
+ ...(request.source ? { source: request.source } : {}),
5256
+ message: request.message,
5257
+ targets,
5258
+ });
5259
+ };
5260
+ const explicitWorkflowSteerIndexes = (request: SteerRequest): number[] | undefined => request.targetIndex !== undefined ? [request.targetIndex] : request.targetIndexes;
5261
+ const failWorkflowSteer = (request: SteerRequest, indexes: number[], reason: string): void => {
5262
+ const targets = indexes.map((index) => ({ index, state: "failed" as const, reason }));
5263
+ recordWorkflowSteerTargets(request, targets);
5264
+ emitWorkflowSteerEvent("subagent.steer.requested", request.id, undefined, { targets });
5265
+ for (const index of indexes) emitWorkflowSteerEvent("subagent.steer.failed", request.id, index, { reason });
5266
+ persist({ tolerateStatusWriteFailure: true });
5267
+ };
5268
+ const pendingWorkflowSteers = new Set<(state: "delivered" | "queued" | "failed", reason?: string) => void>();
5269
+ const deliverWorkflowSteerRequest = (request: SteerRequest): void => {
5270
+ let indexes = explicitWorkflowSteerIndexes(request);
5271
+ if (status.state !== "running") {
5272
+ failWorkflowSteer(request, indexes ?? [0], `run became ${status.state} before steering request was consumed`);
5273
+ return;
5274
+ }
5275
+ const liveControls = [...deps.state.foregroundControls.values()].filter((candidate) => candidate.parentWorkflowRunId === workflowRunId && (candidate.activeChildren?.size ?? 0) > 0);
5276
+ if (indexes === undefined) {
5277
+ if (liveControls.length !== 1) {
5278
+ failWorkflowSteer(request, [0], liveControls.length > 1
5279
+ ? `workflow has ${liveControls.length} live children; steer a child run id instead`
5280
+ : "workflow child is not live in this process");
5281
+ return;
5282
+ }
5283
+ const index = status.steps?.findIndex((step) => !!step.workflowKey && step.workflowKey === liveControls[0]!.workflowKey) ?? -1;
5284
+ if (index < 0) {
5285
+ failWorkflowSteer(request, [0], "live workflow child has no projected step");
5286
+ return;
5287
+ }
5288
+ indexes = [index];
5289
+ }
5290
+ // Normalize and record the whole set once: recordSteeringRequest is idempotent by id.
5291
+ // Explicit indexes resolve only by their projected key, never by a live sibling.
5292
+ const resolved = indexes.map((index) => {
5293
+ const step = status.steps?.[index];
5294
+ const control = step?.workflowKey ? liveControls.find((candidate) => candidate.workflowKey === step.workflowKey) : undefined;
5295
+ return { index, control, reason: !step ? "child index out of range" : !control ? "workflow child is not live in this process" : undefined };
5296
+ });
5297
+ const targets = resolved.map(({ index, reason }) => ({ index, state: reason ? "failed" as const : "routed" as const, ...(reason ? { reason } : {}) }));
5298
+ recordWorkflowSteerTargets(request, targets);
5299
+ emitWorkflowSteerEvent("subagent.steer.requested", request.id, undefined, { targets });
5300
+ persist({ tolerateStatusWriteFailure: true });
5301
+ for (const { index, control, reason } of resolved) {
5302
+ if (!control) {
5303
+ emitWorkflowSteerEvent("subagent.steer.failed", request.id, index, { reason });
5304
+ continue;
5305
+ }
5306
+ emitWorkflowSteerEvent("subagent.steer.routed", request.id, index);
5307
+ const applyWorkflowSteerDelivery = (state: "delivered" | "queued" | "failed", reason?: string): void => {
5308
+ // Shutdown settles unresolved receipts synchronously; late SDK callbacks cannot rewrite them.
5309
+ if (!pendingWorkflowSteers.has(applyWorkflowSteerDelivery)) return;
5310
+ // Queuing is not confirmation of delivery: retain ownership until terminal settlement.
5311
+ if (state !== "queued") pendingWorkflowSteers.delete(applyWorkflowSteerDelivery);
5312
+ const now = Date.now();
5313
+ const steering = steeringStatus(status);
5314
+ // This callback owns a pending target, even after bounded display history
5315
+ // evicts its request. Account independently; queued still represents one pending target.
5316
+ if (state !== "queued") steering.pending = Math.max(0, steering.pending - 1);
5317
+ if (state === "delivered") {
5318
+ steering.delivered++;
5319
+ steering.lastDeliveredAt = now;
5320
+ } else if (state === "failed") steering.failed++;
5321
+ const receipt = steering.recent.find((candidate) => candidate.id === request.id)?.targets.find((target) => target.index === index);
5322
+ if (receipt) {
5323
+ receipt.state = state;
5324
+ if (state === "delivered") receipt.deliveredAt = now;
5325
+ if (state === "failed") receipt.failedAt = now;
5326
+ if (reason) receipt.reason = reason;
5327
+ }
5328
+ if (state === "failed") {
5329
+ const failedStep = status.steps?.[index];
5330
+ if (failedStep) failedStep.activityState = "needs_attention";
5331
+ status.activityState = "needs_attention";
5332
+ }
5333
+ emitWorkflowSteerEvent(`subagent.steer.${state}`, request.id, index, state === "failed" ? { reason } : { deliveryStatus: state, message: request.message });
5334
+ persist({ tolerateStatusWriteFailure: true });
5335
+ };
5336
+ pendingWorkflowSteers.add(applyWorkflowSteerDelivery);
5337
+ void steerWorkflowForegroundTarget({
5338
+ target: { control, workflowRunId, sourceRunId: workflowRunId },
5339
+ message: request.message,
5340
+ ...(request.mode ? { mode: request.mode } : {}),
5341
+ }).then(
5342
+ (result) => {
5343
+ const target = result.details.steering?.targets[0];
5344
+ applyWorkflowSteerDelivery(target?.state === "delivered" ? "delivered" : target?.state === "queued" ? "queued" : "failed", target?.reason ?? (target ? undefined : "steering produced no target receipt"));
5345
+ },
5346
+ (error) => applyWorkflowSteerDelivery("failed", error instanceof Error ? error.message : String(error)),
5347
+ );
5348
+ }
5349
+ };
5350
+ let disposeWorkflowSteerInbox: (() => void) | undefined;
5351
+ try {
5352
+ disposeWorkflowSteerInbox = watchAsyncControlInbox(asyncDir, {
5353
+ onSteer: deliverWorkflowSteerRequest,
5354
+ onError: (error, phase, request) => {
5355
+ const reason = error instanceof Error ? error.message : String(error);
5356
+ appendWorkflowEvent({ type: "subagent.workflow.steer_inbox_unavailable", phase, error: reason });
5357
+ if (request) failWorkflowSteer(request, explicitWorkflowSteerIndexes(request) ?? [0], `steer consumer failed: ${reason}`);
5358
+ },
5359
+ });
5360
+ } catch (error) {
5361
+ // Steering is an optional control surface, so a watcher that cannot install must not fail the run.
5362
+ appendWorkflowEvent({ type: "subagent.workflow.steer_inbox_unavailable", error: error instanceof Error ? error.message : String(error) });
5363
+ }
5364
+ deps.activateSupervisorTransport?.();
5076
5365
  const { workflowScript, async: _workflowAsync, chatProgress: _chatProgress, ...workflowRequest } = requestParams;
5366
+ let workflowSteerInboxClosed = false;
5367
+ const settleWorkflowSteerInbox = (outcome = status.state): void => {
5368
+ if (workflowSteerInboxClosed) return;
5369
+ workflowSteerInboxClosed = true;
5370
+ disposeWorkflowSteerInbox?.();
5371
+ // Consumed deliveries are not files anymore. Fail unresolved SDK operations without awaiting them.
5372
+ for (const settle of pendingWorkflowSteers) settle("failed", `run became ${outcome} before steering delivery settled; delivery unconfirmed`);
5373
+ try {
5374
+ closeSteerInbox(asyncDir, outcome);
5375
+ // Separately drain requests that never reached the consumer.
5376
+ const undelivered = consumeSteerRequests(asyncDir, undefined, (error) => {
5377
+ appendWorkflowEvent({ type: "subagent.workflow.steer_inbox_unavailable", phase: "drain", error: error instanceof Error ? error.message : String(error) });
5378
+ });
5379
+ for (const request of undelivered) failWorkflowSteer(request, explicitWorkflowSteerIndexes(request) ?? [0], `run became ${outcome} before steering request was consumed`);
5380
+ } catch (error) {
5381
+ appendWorkflowEvent({ type: "subagent.workflow.steer_inbox_unavailable", phase: "close", error: error instanceof Error ? error.message : String(error) });
5382
+ }
5383
+ // Commit receipts while run status is still nonterminal. Result/index publication
5384
+ // can fail next; teardown must not publish the subsequently computed terminal status.
5385
+ persist();
5386
+ };
5077
5387
  void Promise.resolve().then(async () => {
5078
5388
  const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
5079
5389
  const workflowResults: SingleResult[] = [];
@@ -5081,7 +5391,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5081
5391
  const { action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, missionId: _missionId, mission: _mission, preflight: _preflight, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = workflowRequest;
5082
5392
  const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
5083
5393
  const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
5084
- const workflowAggregateOutputPath = resolveWorkflowAggregateOutputPath(workflowOutput, parentCwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, workflowRunId));
5394
+ const workflowAggregateOutputPath = resolveSingleOutputPath(workflowOutput, parentCwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, workflowRunId));
5085
5395
  const claimedOutputPaths = new Map<string, string>();
5086
5396
  const childOutputOverrides = new Map<string, string>();
5087
5397
  const childOutputClaimPaths = new Map<string, string>();
@@ -5206,6 +5516,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5206
5516
  globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
5207
5517
  timeoutMs: timeout,
5208
5518
  signal: controller.signal,
5519
+ continueAfterAbortWhenChildrenSettled: (abortError) => {
5520
+ if (abortError.message !== "Workflow stopped because the extension session was replaced or reloaded.") return false;
5521
+ const activeAsyncChild = [...(deps.state.asyncJobs?.values() ?? [])].some((job) => job.parentWorkflowRunId === workflowRunId && (job.status === "queued" || job.status === "running"));
5522
+ const activeForegroundChild = [...deps.state.foregroundControls.values()].some((control) => control.parentWorkflowRunId === workflowRunId && (control.activeChildren?.size ?? 0) > 0);
5523
+ return !activeAsyncChild && !activeForegroundChild;
5524
+ },
5209
5525
  registerStopChild: (stop) => {
5210
5526
  if (stop) deps.state.workflowChildStops?.set(workflowRunId, stop);
5211
5527
  else deps.state.workflowChildStops?.delete(workflowRunId);
@@ -5260,6 +5576,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5260
5576
  deps.asyncByDefault,
5261
5577
  );
5262
5578
  preparedChildParams = childRequest;
5579
+ if (workflowUsageBudget.budget) workflowOwnedUsageBudgets.set(childRequest, workflowUsageBudget.budget);
5263
5580
  workflowLaunchObservers.set(childRequest, (launch) => {
5264
5581
  const step = status.steps?.find((candidate) => candidate.workflowKey === key);
5265
5582
  if (step) {
@@ -5308,7 +5625,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5308
5625
  if (child.runId) workflowChildRunIds.set(key, child.runId);
5309
5626
  const step = status.steps?.find((candidate) => candidate.workflowKey === key);
5310
5627
  if (step) {
5311
- step.async = Boolean(result.details.asyncId || result.details.asyncDir);
5628
+ step.async = step.async === true || Boolean(result.details.asyncId || result.details.asyncDir);
5312
5629
  if (child.runId) step.runId = child.runId;
5313
5630
  if (child.lane) step.lane = child.lane;
5314
5631
  }
@@ -5330,7 +5647,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5330
5647
  return child;
5331
5648
  },
5332
5649
  status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession, workflowParentModel)),
5333
- resolveResume: (reference) => resolveKeyedWorkflowResume(reference, deps.state),
5650
+ resolveResume: (reference, _signal, index) => resolveWorkflowResume(reference, deps, ctx.sessionManager.getSessionFile() ?? null, index),
5334
5651
  steer: (key, message, options, workflowSignal) => steerWorkflowChildByKey({ state: deps.state, workflowRunId, key, message, options, signal: workflowSignal, resolveRunId: () => workflowChildRunIds.get(key) }),
5335
5652
  });
5336
5653
  const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, workflow.trace, { settled: true });
@@ -5341,16 +5658,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5341
5658
  const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, summary, producedChildOutputPaths);
5342
5659
  const resultSummary = appendWorkflowOutputWarning(summary, outputWarning);
5343
5660
  const workflowUsage = sumResultsUsage(workflowResults);
5661
+ settleWorkflowSteerInbox("complete");
5344
5662
  const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children, steps: status.steps });
5345
5663
  status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
5346
5664
  const receipt = terminalWorkflowReceipt(workflowRunId, "complete", workflow.children, workflowChildren, undefined, validHostStepNodes(status.workflowGraph), workflowResource?.provenance);
5665
+ delete status.workflowReceiptPath;
5347
5666
  let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
5348
5667
  try {
5349
5668
  workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
5669
+ status.workflowReceiptPath = workflowReceipt.path;
5350
5670
  } catch (receiptError) {
5351
5671
  appendWorkflowEvent({ type: "subagent.workflow.receipt_write_failed", error: `Failed to persist async workflow receipt: ${receiptError instanceof Error ? receiptError.message : String(receiptError)}` });
5352
5672
  }
5353
5673
  if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary: resultSummary, output: resultSummary, workflowChildren, results: workflow.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(status.steps?.find((step) => step.workflowKey === child.key)?.sessionName ? { sessionName: status.steps?.find((step) => step.workflowKey === child.key)?.sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(child.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, ...(workflowReceipt ? { workflowReceipt } : {}), asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
5674
+ if (pendingResultPublication && !await pendingResultPublication) return;
5354
5675
  persist();
5355
5676
  deps.refreshResultDelivery?.();
5356
5677
  persistClosed = true;
@@ -5374,10 +5695,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5374
5695
  }
5375
5696
  }
5376
5697
  const workflowState = state === "paused" ? "paused" : state === "stopped" ? "stopped" : "failed";
5698
+ settleWorkflowSteerInbox(state);
5377
5699
  const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState, inventoryComplete: true, trace: partial.trace, children: partial.children, steps: status.steps });
5378
5700
  const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, partial.trace, { settled: true });
5379
5701
  const finalPreflightTrace = annotateWorkflowPreflightTrace(partial.trace, workflowPreflight);
5380
- status = compactOptional<AsyncStatus>({ ...status, state, stopped: stopped || undefined, activityState: pauseForDetached ? "needs_attention" : undefined, error: workflowFailureMessage(error, workflowRunId, partial.children), endedAt: Date.now(), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren });
5702
+ status = compactOptional<AsyncStatus>({ ...status, state, stopped: stopped || undefined, activityState: pauseForDetached || status.activityState === "needs_attention" ? "needs_attention" : undefined, error: workflowFailureMessage(error, workflowRunId, partial.children), endedAt: Date.now(), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren });
5381
5703
  if (pauseForDetached) {
5382
5704
  const promoted = promotePausedWorkflowIfSettled(status);
5383
5705
  if (promoted) status = promoted;
@@ -5390,18 +5712,27 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5390
5712
  const receiptState: WorkflowReceiptState = status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
5391
5713
  const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
5392
5714
  const receipt = terminalWorkflowReceipt(workflowRunId, receiptState, partial.children, workflowChildren, terminalOutcome, validHostStepNodes(status.workflowGraph), workflowResource?.provenance);
5715
+ delete status.workflowReceiptPath;
5393
5716
  let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
5394
5717
  try {
5395
5718
  workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
5719
+ status.workflowReceiptPath = workflowReceipt.path;
5396
5720
  } catch (receiptError) {
5397
5721
  appendWorkflowEvent({ type: "subagent.workflow.receipt_write_failed", error: `Failed to persist async workflow receipt: ${receiptError instanceof Error ? receiptError.message : String(receiptError)}` });
5398
5722
  }
5399
5723
  if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: status.state === "complete", state: status.state, summary: resultSummary, error: status.state === "complete" ? undefined : status.error, stopped: status.stopped, activityState: status.activityState, workflowChildren, ...(terminalOutcome ? { terminalOutcome } : {}), results: partial.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(status.steps?.find((step) => step.workflowKey === child.key)?.sessionName ? { sessionName: status.steps?.find((step) => step.workflowKey === child.key)?.sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(child.terminalOutcome ? { terminalOutcome: child.terminalOutcome } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(child.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(child.detached && status.state !== "complete" ? { detached: true } : {}), ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, ...(workflowReceipt ? { workflowReceipt } : {}), asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
5724
+ if (pendingResultPublication && !await pendingResultPublication) return;
5400
5725
  persist();
5401
5726
  deps.refreshResultDelivery?.();
5402
5727
  persistClosed = true;
5403
5728
  appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(terminalOutcome ? { terminalOutcome } : {}), ...(status.error ? { error: status.error } : {}), ...(status.activityState ? { activityState: status.activityState } : {}) });
5404
5729
  } finally {
5730
+ // Idempotent cleanup only: a failed result/index write must not authorize terminal status.
5731
+ try {
5732
+ settleWorkflowSteerInbox();
5733
+ } catch (error) {
5734
+ console.error(`Failed to close async workflow steer inbox '${asyncDir}':`, error);
5735
+ }
5405
5736
  persistClosed = true;
5406
5737
  deps.state.workflowControllers?.delete(workflowRunId);
5407
5738
  deps.state.workflowChildStops?.delete(workflowRunId);
@@ -5417,7 +5748,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5417
5748
  const { workflowScript: _workflowScript, action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, async: _async, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, chatProgress: _chatProgress, missionId: _missionId, mission: _mission, preflight: _preflight, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = requestParams;
5418
5749
  const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
5419
5750
  const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
5420
- const workflowAggregateOutputPath = resolveWorkflowAggregateOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, _id));
5751
+ const workflowAggregateOutputPath = resolveSingleOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, foregroundWorkflowRunId));
5421
5752
  const claimedOutputPaths = new Map<string, string>();
5422
5753
  const childOutputOverrides = new Map<string, string>();
5423
5754
  const childOutputClaimPaths = new Map<string, string>();
@@ -5427,7 +5758,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5427
5758
  const runHostCommand = workflowHostCommandRunner({
5428
5759
  workflowCwd,
5429
5760
  artifactsDir: workflowArtifactsDir,
5430
- workflowRunId: _id,
5761
+ workflowRunId: foregroundWorkflowRunId,
5431
5762
  claimedOutputPaths,
5432
5763
  producedOutputPaths: producedChildOutputPaths,
5433
5764
  ...(workflowResource ? { authorize: (key, params) => authorizeWorkflowResourceHost(workflowResource!.permit, key, params.command) } : {}),
@@ -5437,19 +5768,26 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5437
5768
  : workflowResource
5438
5769
  ? workflowResource.authority.host ? runHostCommand : undefined
5439
5770
  : publicExecution ? undefined : runHostCommand;
5440
- const workflowHostSteps = new Map<string, HostStepNodeV1>();
5771
+ const workflowHostSteps = new Map<string, HostStepNode>();
5441
5772
  let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [], ...(workflowResource ? { resource: workflowResource.provenance } : {}) };
5442
- let liveWorkflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "running", inventoryComplete: false });
5773
+ const childProgress = new Map<string, ReturnType<typeof workflowChildProgress>>();
5774
+ let activityTimer: ReturnType<typeof setTimeout> | undefined;
5775
+ let lastProgressAt = 0;
5776
+ let workflowProgressClosed = false;
5443
5777
  const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
5444
5778
  const workflowCapabilityCeiling = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(resolveCurrentSessionId(ctx.sessionManager)));
5445
5779
  const sendWorkflowProgress = () => {
5446
- const update = workflowChatProgressUpdate(_id, chatProgress, liveWorkflow, liveWorkflowChildren, workflowPreflight);
5447
- if (update) onUpdate?.(update);
5780
+ if (workflowProgressClosed) return;
5781
+ if (activityTimer) clearTimeout(activityTimer);
5782
+ activityTimer = undefined;
5783
+ lastProgressAt = Date.now();
5784
+ const liveWorkflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "running", inventoryComplete: false, trace: liveWorkflow.trace, progress: childProgress });
5785
+ onUpdate?.(workflowProgressUpdate(foregroundWorkflowRunId, chatProgress, liveWorkflow, liveWorkflowChildren, workflowPreflight));
5448
5786
  };
5449
5787
  try {
5450
5788
  const workflow = await runWorkflowScript({
5451
5789
  script: requestParams.workflowScript,
5452
- ...(delegatedWorkflowPermit ? { oneUsePermit: { claim: (key: string) => claimWorkflowChildPermit(delegatedWorkflowPermit, _id, key) } } : {}),
5790
+ ...(delegatedWorkflowPermit ? { oneUsePermit: { claim: (key: string) => claimWorkflowChildPermit(delegatedWorkflowPermit, foregroundWorkflowRunId, key) } } : {}),
5453
5791
  globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
5454
5792
  timeoutMs: timeout,
5455
5793
  signal,
@@ -5462,11 +5800,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5462
5800
  const projectedTrace = annotateWorkflowPreflightTrace(trace, workflowPreflight);
5463
5801
  const preflightWarnings = workflowPreflightWarnings(workflowPreflight, trace);
5464
5802
  liveWorkflow = { ...liveWorkflow, trace: projectedTrace, ...(preflightWarnings.length ? { preflightWarnings } : {}) };
5465
- liveWorkflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "running", inventoryComplete: false, trace });
5803
+ for (const entry of trace) {
5804
+ if (entry.operation === "run" && ["completed", "failed", "stopped", "detached"].includes(entry.state)) childProgress.delete(entry.key);
5805
+ }
5466
5806
  sendWorkflowProgress();
5467
5807
  },
5468
5808
  admit: (calls) => {
5469
- const outputClaims = workflowChildOutputClaims({ ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId: _id, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, state: deps.state, claimedOutputPaths, entries: calls });
5809
+ const outputClaims = workflowChildOutputClaims({ ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId: foregroundWorkflowRunId, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, state: deps.state, claimedOutputPaths, entries: calls });
5470
5810
  if (outputClaims.error) throw new Error(outputClaims.error);
5471
5811
  claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
5472
5812
  if (outputClaims.claims) applyWorkflowChildOutputClaims(claimedOutputPaths, outputClaims.claims);
@@ -5486,7 +5826,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5486
5826
  if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)), childParams, deps.state, { state: "partial", reason: "budget_exhausted" });
5487
5827
  const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
5488
5828
  const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
5489
- recordMissionWorkflowChild(missionBinding, _id, key, {
5829
+ recordMissionWorkflowChild(missionBinding, foregroundWorkflowRunId, key, {
5490
5830
  status: "running",
5491
5831
  ...(typeof childParams.agent === "string" && childParams.agent.trim() ? { agent: childParams.agent.trim() } : {}),
5492
5832
  ...(childLabel ? { label: childLabel } : {}),
@@ -5494,13 +5834,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5494
5834
  heartbeat: { status: "running", ...(childPhase ? { phase: childPhase } : {}) },
5495
5835
  });
5496
5836
  let preparedChildParams: SubagentParamsLike | undefined;
5497
- const result = await runMissionWorkflowChild(missionBinding, _id, key, childPhase, () => {
5837
+ const result = await runMissionWorkflowChild(missionBinding, foregroundWorkflowRunId, key, childPhase, () => {
5498
5838
  const childRequest = bindMissionWorkflowChildAsyncLaunch(
5499
- { ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams, parentWorkflowRunId: _id, workflowKey: key, ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, outputOverride: childOutputOverrides.get(key), outputClaimPath: childOutputClaimPaths.get(key), options: { missionDetached: detachWorkflowChildMissions, suppressRoutineResultIntercom: chatProgress.mode === "live-card", runFanoutBudget: workflowFanoutBudget, parentDeadlineAt: workflowDeadlineAt, capabilityCeiling: workflowCapabilityCeiling } }), runFanoutAdmitted: admission.admitted },
5839
+ { ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams: delegatedWorkflowPermit ? { ...childParams, async: false } : childParams, parentWorkflowRunId: foregroundWorkflowRunId, workflowKey: key, ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, outputOverride: childOutputOverrides.get(key), outputClaimPath: childOutputClaimPaths.get(key), options: { missionDetached: detachWorkflowChildMissions, suppressRoutineResultIntercom: chatProgress.mode === "live-card", runFanoutBudget: workflowFanoutBudget, parentDeadlineAt: workflowDeadlineAt, capabilityCeiling: workflowCapabilityCeiling } }), runFanoutAdmitted: admission.admitted },
5500
5840
  missionBinding,
5501
5841
  deps.asyncByDefault,
5502
5842
  );
5503
5843
  preparedChildParams = childRequest;
5844
+ if (workflowUsageBudget.budget) workflowOwnedUsageBudgets.set(childRequest, workflowUsageBudget.budget);
5504
5845
  if (delegatedWorkflowPermit) {
5505
5846
  if (childRequest.async !== false) throw new Error("Workflow child permit supports one foreground child only.");
5506
5847
  const childCwd = resolveRequestedCwd(workflowCwd, childRequest.cwd);
@@ -5508,9 +5849,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5508
5849
  ? resolveAgentName(childRequest.agent, discoverWorkflowAgents(childCwd, resolveExecutionAgentScope(childRequest.agentScope)).agents).agent
5509
5850
  : undefined;
5510
5851
  if (childAgent?.runner?.type === "external-cli" || childAgent?.runner?.type === "external-job") throw new Error("Workflow child permit supports native Pi children only.");
5511
- workflowPermitContexts.set(childRequest, { child: { permit: delegatedWorkflowPermit, workflowRunId: _id, childKey: key } });
5852
+ workflowPermitContexts.set(childRequest, { child: { permit: delegatedWorkflowPermit, workflowRunId: foregroundWorkflowRunId, childKey: key } });
5512
5853
  }
5513
- workflowLaunchObservers.set(childRequest, (launch) => recordMissionWorkflowChild(missionBinding, _id, key, {
5854
+ workflowLaunchObservers.set(childRequest, (launch) => recordMissionWorkflowChild(missionBinding, foregroundWorkflowRunId, key, {
5514
5855
  status: "running",
5515
5856
  agent: launch.agent,
5516
5857
  ...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}),
@@ -5518,8 +5859,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5518
5859
  return execute(randomUUID(), childRequest, workflowSignal, (update) => {
5519
5860
  const progress = update.details.progress?.[0];
5520
5861
  if (!progress) return;
5862
+ if (onUpdate && !workflowProgressClosed) {
5863
+ if (progress.status === "running") childProgress.set(key, workflowChildProgress(progress));
5864
+ else childProgress.delete(key);
5865
+ if (!activityTimer) {
5866
+ const delay = 100 - (Date.now() - lastProgressAt);
5867
+ if (delay <= 0) sendWorkflowProgress();
5868
+ else activityTimer = setTimeout(sendWorkflowProgress, delay);
5869
+ }
5870
+ }
5521
5871
  const progressStatus = progress.status === "completed" ? "completed" : progress.status === "failed" ? "failed" : "running";
5522
- recordMissionWorkflowChild(missionBinding, _id, key, {
5872
+ recordMissionWorkflowChild(missionBinding, foregroundWorkflowRunId, key, {
5523
5873
  status: progressStatus,
5524
5874
  heartbeat: { status: progressStatus, ...(childPhase ? { phase: childPhase } : {}) },
5525
5875
  });
@@ -5533,7 +5883,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5533
5883
  const child = workflowChildResult(key, result, preparedChildParams ?? childParams, deps.state);
5534
5884
  if (child.runId) workflowChildRunIds.set(key, child.runId);
5535
5885
  const childStatus = missionWorkflowChildStatus(result);
5536
- recordMissionWorkflowChild(missionBinding, _id, key, {
5886
+ recordMissionWorkflowChild(missionBinding, foregroundWorkflowRunId, key, {
5537
5887
  status: childStatus,
5538
5888
  ...(child.runId ? { runId: child.runId } : {}),
5539
5889
  ...(result.details.results[0]?.agent ? { agent: result.details.results[0].agent } : {}),
@@ -5545,13 +5895,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5545
5895
  return child;
5546
5896
  },
5547
5897
  status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession, workflowParentModel)),
5548
- resolveResume: (reference) => resolveKeyedWorkflowResume(reference, deps.state),
5549
- steer: (key, message, options, workflowSignal) => steerWorkflowChildByKey({ state: deps.state, workflowRunId: _id, key, message, options, signal: workflowSignal, resolveRunId: () => workflowChildRunIds.get(key) }),
5898
+ resolveResume: (reference, _signal, index) => resolveWorkflowResume(reference, deps, ctx.sessionManager.getSessionFile() ?? null, index),
5899
+ steer: (key, message, options, workflowSignal) => steerWorkflowChildByKey({ state: deps.state, workflowRunId: foregroundWorkflowRunId, key, message, options, signal: workflowSignal, resolveRunId: () => workflowChildRunIds.get(key) }),
5550
5900
  });
5551
5901
  const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, workflow.trace, { settled: true });
5552
5902
  const finalPreflightTrace = annotateWorkflowPreflightTrace(workflow.trace, workflowPreflight);
5553
- const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children });
5554
- const receipt = terminalWorkflowReceipt(_id, "complete", workflow.children, workflowChildren, undefined, [...workflowHostSteps.values()], workflowResource?.provenance);
5903
+ const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children });
5904
+ const receipt = terminalWorkflowReceipt(foregroundWorkflowRunId, "complete", workflow.children, workflowChildren, undefined, [...workflowHostSteps.values()], workflowResource?.provenance);
5555
5905
  const traceLines = finalPreflightTrace.map((entry) => `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.durationMs !== undefined ? ` in ${entry.durationMs}ms` : ""}${entry.warning ? ` — ${entry.warning}` : ""}${entry.error ? ` — ${entry.error}` : ""}`);
5556
5906
  const sections = [
5557
5907
  ...(workflowPreflight ? [formatWorkflowPreflight(workflowPreflight)] : []),
@@ -5569,11 +5919,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5569
5919
  const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
5570
5920
  return attachWorkflowMission(withRunFanoutBudget({
5571
5921
  content: [{ type: "text", text: displayText }],
5572
- details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflowDetailsResults(workflow.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
5922
+ details: compactOptional<Details>({ mode: "workflow", runId: foregroundWorkflowRunId, results: workflowDetailsResults(workflow.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
5573
5923
  }, workflowFanoutBudget));
5574
5924
  } catch (error) {
5575
5925
  const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
5576
- const text = workflowFailureMessage(error, _id, partial.children);
5926
+ const text = workflowFailureMessage(error, foregroundWorkflowRunId, partial.children);
5577
5927
  const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, partial.trace, { settled: true });
5578
5928
  const finalPreflightTrace = annotateWorkflowPreflightTrace(partial.trace, workflowPreflight);
5579
5929
  const traceLines = finalPreflightTrace.map((entry) => `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.warning ? ` — ${entry.warning}` : ""}${entry.error ? ` — ${entry.error}` : ""}`);
@@ -5590,14 +5940,18 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5590
5940
  const workflowText = sections.join("\n\n");
5591
5941
  const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, workflowText, producedChildOutputPaths);
5592
5942
  const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
5593
- const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "failed", inventoryComplete: true, trace: partial.trace, children: partial.children });
5943
+ const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "failed", inventoryComplete: true, trace: partial.trace, children: partial.children });
5594
5944
  const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
5595
- const receipt = terminalWorkflowReceipt(_id, "failed", partial.children, workflowChildren, terminalOutcome, [...workflowHostSteps.values()], workflowResource?.provenance);
5945
+ const receipt = terminalWorkflowReceipt(foregroundWorkflowRunId, "failed", partial.children, workflowChildren, terminalOutcome, [...workflowHostSteps.values()], workflowResource?.provenance);
5596
5946
  return attachWorkflowMission(withRunFanoutBudget({
5597
5947
  content: [{ type: "text", text: displayText }],
5598
5948
  isError: true,
5599
- details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflowDetailsResults(partial.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
5949
+ details: compactOptional<Details>({ mode: "workflow", runId: foregroundWorkflowRunId, results: workflowDetailsResults(partial.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
5600
5950
  }, workflowFanoutBudget));
5951
+ } finally {
5952
+ workflowProgressClosed = true;
5953
+ if (activityTimer) clearTimeout(activityTimer);
5954
+ childProgress.clear();
5601
5955
  }
5602
5956
  }
5603
5957
  const directParams = requestParams;
@@ -5668,10 +6022,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5668
6022
  }
5669
6023
  if (!confirmed) return { content: [{ type: "text", text: "Worktree discard canceled; preserved worktrees were not changed." }], details: { mode: "management", results: [] } };
5670
6024
  try {
5671
- const discarded = discardPreservedWorktrees(
5672
- path.isAbsolute(paramsWithResolvedCwd.handoffPath) ? paramsWithResolvedCwd.handoffPath : path.resolve(requestCwd, paramsWithResolvedCwd.handoffPath),
6025
+ const manifestPath = path.isAbsolute(paramsWithResolvedCwd.handoffPath) ? paramsWithResolvedCwd.handoffPath : path.resolve(requestCwd, paramsWithResolvedCwd.handoffPath);
6026
+ const discarded = await withWorktreeTransaction(() => discardPreservedWorktrees(
6027
+ manifestPath,
5673
6028
  { kind: decision === "confirm" ? "confirmed" : "policy", ...(deps.config.authorityPolicy ? { policy: deps.config.authorityPolicy } : {}) },
5674
- );
6029
+ ));
5675
6030
  return { content: [{ type: "text", text: discarded.text }], details: { mode: "management", results: [] } };
5676
6031
  } catch (error) {
5677
6032
  return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
@@ -5940,7 +6295,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5940
6295
  }
5941
6296
  return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots, abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })));
5942
6297
  }
5943
- if (paramsWithResolvedCwd.view === "fleet") {
6298
+ if (paramsWithResolvedCwd.view === "fleet" || paramsWithResolvedCwd.view === "transcript") {
5944
6299
  return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots, abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })));
5945
6300
  }
5946
6301
  if (targetRunId) {
@@ -5949,12 +6304,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5949
6304
  if (resolved?.kind === "foreground") {
5950
6305
  const foreground = getForegroundControl(deps.state, resolved.id);
5951
6306
  if (foreground) {
5952
- if (paramsWithResolvedCwd.view === "transcript") {
5953
- return withBudget({
5954
- content: [{ type: "text", text: "Live foreground transcript is already visible in the expanded running subagent result. Persisted session transcript becomes inspectable after the foreground run completes when sessions are enabled." }],
5955
- details: { mode: "management", results: [] },
5956
- });
5957
- }
5958
6307
  return withBudget(foregroundStatusResult(foreground));
5959
6308
  }
5960
6309
  }
@@ -5964,13 +6313,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5964
6313
  }
5965
6314
  } else if (!hasDirectoryTarget) {
5966
6315
  const foreground = getForegroundControl(deps.state, undefined);
5967
- if (foreground && paramsWithResolvedCwd.view !== "transcript") return withBudget(foregroundStatusResult(foreground));
5968
- if (foreground && paramsWithResolvedCwd.view === "transcript") {
5969
- return withBudget({
5970
- content: [{ type: "text", text: "Live foreground transcript is already visible in the expanded running subagent result. Pass an async run id to inspect a background transcript." }],
5971
- details: { mode: "management", results: [] },
5972
- });
5973
- }
6316
+ if (foreground) return withBudget(foregroundStatusResult(foreground));
5974
6317
  }
5975
6318
  return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots, abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })));
5976
6319
  }
@@ -5991,9 +6334,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5991
6334
  const runId = location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir);
5992
6335
  const directoryStatus = location.asyncDir ? readStatus(location.asyncDir) : null;
5993
6336
  if (directoryStatus?.mode === "workflow") {
5994
- const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: directoryStatus.runId || runId, asyncDirRoot: DIRS.async });
5995
- if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
5996
- return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
6337
+ return steerWorkflowRun({ state: deps.state, runId, asyncDir: location.asyncDir!, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
5997
6338
  }
5998
6339
  if (location.asyncDir) {
5999
6340
  const unsupported = externalRunnerControlError(location.asyncDir, "steer");
@@ -6001,6 +6342,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6001
6342
  }
6002
6343
  return steerAsyncRun(compactOptional<Parameters<typeof steerAsyncRun>[0]>({
6003
6344
  state: deps.state,
6345
+ findPendingAsks: deps.findPendingAsks,
6004
6346
  runId,
6005
6347
  message,
6006
6348
  mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode),
@@ -6033,14 +6375,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6033
6375
  if (resolved?.kind === "foreground") {
6034
6376
  const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, childRunId: resolved.id, asyncDirRoot: DIRS.async });
6035
6377
  if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
6036
- return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
6378
+ return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index });
6037
6379
  }
6038
6380
  if (resolved?.kind !== "async") return { content: [{ type: "text", text: `No async run found for '${targetRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
6039
6381
  const resolvedStatus = resolved.location.asyncDir ? readStatus(resolved.location.asyncDir) : null;
6040
6382
  if (resolvedStatus?.mode === "workflow") {
6041
- const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: resolvedStatus.runId || resolved.id, asyncDirRoot: DIRS.async });
6042
- if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
6043
- return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
6383
+ return steerWorkflowRun({ state: deps.state, runId: resolved.id, asyncDir: resolved.location.asyncDir!, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
6044
6384
  }
6045
6385
  if (resolved.location.asyncDir) {
6046
6386
  const unsupported = externalRunnerControlError(resolved.location.asyncDir, "steer");
@@ -6048,6 +6388,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6048
6388
  }
6049
6389
  return steerAsyncRun(compactOptional<Parameters<typeof steerAsyncRun>[0]>({
6050
6390
  state: deps.state,
6391
+ findPendingAsks: deps.findPendingAsks,
6051
6392
  runId: resolved.id,
6052
6393
  message,
6053
6394
  mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode),
@@ -6119,9 +6460,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6119
6460
  if (action === "stop") {
6120
6461
  const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
6121
6462
  const workflowController = targetRunId ? deps.state.workflowControllers?.get(targetRunId) : undefined;
6122
- if (workflowController) {
6463
+ if (workflowController && targetRunId) {
6464
+ const stopChild = deps.state.workflowChildStops?.get(targetRunId);
6123
6465
  if (paramsWithResolvedCwd.childId !== undefined) {
6124
- const workflowRunId = targetRunId!;
6466
+ const workflowRunId = targetRunId;
6125
6467
  const asyncJob = deps.state.asyncJobs.get(workflowRunId);
6126
6468
  if (!asyncJob?.asyncDir) return { content: [{ type: "text", text: `Status file not found for async workflow '${workflowRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
6127
6469
  const status = readStatus(asyncJob.asyncDir);
@@ -6129,7 +6471,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6129
6471
  const resolution = resolveAsyncStatusChild(status, paramsWithResolvedCwd.childId);
6130
6472
  if (!resolution.ok) return { content: [{ type: "text", text: resolution.message }], isError: true, details: { mode: "management", results: [] } };
6131
6473
  if (!isStoppableAsyncStatusStep(resolution.child.step)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in async run '${targetRunId}' is ${resolution.child.step.status}; stop only supports pending or running children.` }], isError: true, details: { mode: "management", results: [] } };
6132
- const stopChild = deps.state.workflowChildStops?.get(targetRunId!);
6133
6474
  if (!stopChild) return { content: [{ type: "text", text: `Workflow ${targetRunId} child stop is unavailable in this extension runtime.` }], isError: true, details: { mode: "management", results: [] } };
6134
6475
  if (!stopChild(resolution.child.id, `Workflow child '${resolution.child.id}' stopped.`)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in workflow ${workflowRunId} is not available to stop.` }], isError: true, details: { mode: "management", results: [] } };
6135
6476
  try {
@@ -6154,6 +6495,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6154
6495
  }
6155
6496
  return { content: [{ type: "text", text: `Stop requested for child ${resolution.child.id} in async workflow ${workflowRunId}.` }], details: { mode: "management", results: [] } };
6156
6497
  }
6498
+ const asyncJob = deps.state.asyncJobs.get(targetRunId);
6499
+ const status = asyncJob?.asyncDir ? readStatus(asyncJob.asyncDir) : undefined;
6500
+ if (status) stopStoppableAsyncStatusChildren(status, stopChild, "Workflow stopped.");
6157
6501
  workflowController.abort(new Error("Workflow stopped."));
6158
6502
  return { content: [{ type: "text", text: `Stop requested for async workflow ${targetRunId}.` }], details: { mode: "management", results: [] } };
6159
6503
  }
@@ -6246,10 +6590,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6246
6590
  config: deps.config,
6247
6591
  currentSessionId: deps.state.currentSessionId ?? ctx.sessionManager.getSessionId() ?? undefined,
6248
6592
  runtimeAgentOwner: deps.pi,
6593
+ onAgentsChanged: deps.onAgentsChanged,
6249
6594
  });
6250
6595
  }
6251
6596
 
6252
- const { blocked, depth, maxDepth } = checkSubagentDepth(deps.config.maxSubagentDepth);
6597
+ const { blocked, depth, maxDepth } = checkSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
6253
6598
  if (blocked) {
6254
6599
  return {
6255
6600
  content: [
@@ -6326,8 +6671,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6326
6671
  });
6327
6672
  const agents = applyScopedIntercomBridgeToAgents(discoveredAgents, intercomBridge, contextPolicy);
6328
6673
  const runId = randomUUID();
6329
- const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
6330
- const nestedParentAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
6674
+ const inheritedNestedRouteValue = inheritedNestedRoute(deps);
6675
+ const nestedParentAddress = inheritedNestedRouteValue ? inheritedNestedParentAddress(deps) : undefined;
6331
6676
  const shareEnabled = effectiveParams.share === true;
6332
6677
  const hasChain = (effectiveParams.chain?.length ?? 0) > 0;
6333
6678
  const hasTasks = (effectiveParams.tasks?.length ?? 0) > 0;
@@ -6448,7 +6793,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6448
6793
  const asyncRunId = requestedWorkflowChildAsyncId && path.basename(requestedWorkflowChildAsyncId) === requestedWorkflowChildAsyncId
6449
6794
  ? requestedWorkflowChildAsyncId
6450
6795
  : randomUUID();
6451
- const topLevelAsyncCapacityEligible = depth === 0 && !inheritedNestedRoute && !effectiveParams.workflowParentRunId;
6796
+ const topLevelAsyncCapacityEligible = depth === 0 && !inheritedNestedRouteValue && !effectiveParams.workflowParentRunId;
6452
6797
  const topLevelAsync = effectiveAsync && topLevelAsyncCapacityEligible;
6453
6798
  let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
6454
6799
  if (topLevelAsync) {
@@ -6473,9 +6818,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6473
6818
 
6474
6819
  let runFanoutBudget: RunFanoutBudgetDescriptor;
6475
6820
  try {
6476
- const inheritedRunFanoutBudget = effectiveParams.runFanoutBudget ? undefined : decodeRunFanoutBudgetDescriptor(process.env[RUN_FANOUT_BUDGET_ENV]);
6821
+ const inheritedRunFanoutBudgetValue = effectiveParams.runFanoutBudget ? undefined : inheritedRunFanoutBudget(deps);
6477
6822
  runFanoutBudget = effectiveParams.runFanoutBudget
6478
- ?? (inheritedRunFanoutBudget ? { ...inheritedRunFanoutBudget, parentPath: `${inheritedRunFanoutBudget.parentPath ? `${inheritedRunFanoutBudget.parentPath}/` : ""}${runId}` } : undefined)
6823
+ ?? (inheritedRunFanoutBudgetValue ? { ...inheritedRunFanoutBudgetValue, parentPath: `${inheritedRunFanoutBudgetValue.parentPath ? `${inheritedRunFanoutBudgetValue.parentPath}/` : ""}${runId}` } : undefined)
6479
6824
  ?? createRunFanoutBudget(runId, resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
6480
6825
  if (!effectiveParams.runFanoutAdmitted) claimRunFanoutBatch(runFanoutBudget, staticRunFanoutPaths(effectiveParams));
6481
6826
  } catch (error) {
@@ -6483,7 +6828,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6483
6828
  if (error instanceof RunFanoutLimitError) return runFanoutErrorResult(error, foregroundMode);
6484
6829
  return buildRequestedModeError(effectiveParams, error instanceof Error ? error.message : String(error));
6485
6830
  }
6486
- const nestedRoute = inheritedNestedRoute ?? createNestedRoute(runId);
6831
+ const nestedRoute = inheritedNestedRouteValue ?? createNestedRoute(runId);
6487
6832
 
6488
6833
  const artifactConfig: ArtifactConfig = omitUndefinedProperties({
6489
6834
  ...DEFAULT_ARTIFACT_CONFIG,
@@ -6499,7 +6844,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6499
6844
 
6500
6845
  let sessionRoot: string;
6501
6846
  if (effectiveParams.sessionDir) {
6502
- sessionRoot = path.resolve(deps.expandTilde(effectiveParams.sessionDir));
6847
+ // An explicit sessionDir is a root keyed by this launch's run id so
6848
+ // concurrent children resolve distinct per-child session files.
6849
+ sessionRoot = path.join(path.resolve(deps.expandTilde(effectiveParams.sessionDir)), runId);
6503
6850
  } else {
6504
6851
  const baseSessionRoot = deps.config.defaultSessionDir
6505
6852
  ? path.resolve(deps.expandTilde(deps.config.defaultSessionDir))
@@ -6637,6 +6984,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6637
6984
  timeoutMs: foregroundTimeout.timeoutMs,
6638
6985
  toolBudget: runToolBudget.toolBudget,
6639
6986
  usageBudget: usageBudget.budget,
6987
+ inheritedUsageBudget,
6640
6988
  allowZeroToolBudget,
6641
6989
  configToolBudget: configToolBudget.toolBudget,
6642
6990
  configToolTimeoutMs: deps.config.toolTimeoutMs,
@@ -6655,17 +7003,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6655
7003
  const foregroundDescription = selectedAgentNames.length === 1
6656
7004
  ? `${selectedAgentNames[0]} child`
6657
7005
  : `${selectedAgentNames.length} live children`;
6658
- const parentWorkflowStatus = effectiveParams.workflowParentRunId
6659
- ? readStatus(path.join(DIRS.async, effectiveParams.workflowParentRunId))
6660
- : null;
6661
- const workflowSteeringDir = effectiveParams.workflowParentRunId
6662
- && requestSessionId
6663
- && deps.state.workflowControllers?.has(effectiveParams.workflowParentRunId)
6664
- && parentWorkflowStatus?.mode === "workflow"
6665
- && (parentWorkflowStatus.state === "running" || parentWorkflowStatus.state === "queued")
6666
- && parentWorkflowStatus.sessionId === requestSessionId
6667
- ? workflowForegroundSteeringDir(DIRS.async, effectiveParams.workflowParentRunId, runId)
6668
- : undefined;
6669
7006
  const foregroundControl: ForegroundRunControl | undefined = effectiveAsync
6670
7007
  ? undefined
6671
7008
  : compactOptional<ForegroundRunControl>({
@@ -6674,7 +7011,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6674
7011
  mode: foregroundMode,
6675
7012
  ...(effectiveParams.workflowParentRunId ? { parentWorkflowRunId: effectiveParams.workflowParentRunId } : {}),
6676
7013
  ...(effectiveParams.workflowKey ? { workflowKey: effectiveParams.workflowKey } : {}),
6677
- workflowSteeringDir,
6678
7014
  startedAt: Date.now(),
6679
7015
  updatedAt: Date.now(),
6680
7016
  cwd: effectiveCwd,
@@ -6727,7 +7063,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6727
7063
  }
6728
7064
 
6729
7065
  const writeNestedForegroundEvent = (type: "subagent.nested.started" | "subagent.nested.completed", result?: AgentToolResult<Details>): void => {
6730
- if (!inheritedNestedRoute || !nestedParentAddress) return;
7066
+ if (!inheritedNestedRouteValue || !nestedParentAddress) return;
6731
7067
  const now = Date.now();
6732
7068
  const details = result?.details;
6733
7069
  const state = type === "subagent.nested.started"
@@ -6761,7 +7097,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6761
7097
  ? resolveSubagentIntercomTarget(runId, agentsForSummary[0], 0)
6762
7098
  : undefined;
6763
7099
  try {
6764
- writeNestedEvent(inheritedNestedRoute, compactOptional<Parameters<typeof writeNestedEvent>[1]>({
7100
+ writeNestedEvent(inheritedNestedRouteValue, compactOptional<Parameters<typeof writeNestedEvent>[1]>({
6765
7101
  type,
6766
7102
  ts: now,
6767
7103
  parentRunId: nestedParentAddress.parentRunId,
@@ -6772,7 +7108,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6772
7108
  parentStepIndex: nestedParentAddress.parentStepIndex,
6773
7109
  depth: nestedParentAddress.depth,
6774
7110
  path: nestedParentAddress.path,
6775
- ownerIntercomTarget: process.env.SELESAI_SUBAGENT_INTERCOM_SESSION_NAME,
7111
+ ownerIntercomTarget: deps.childRuntime?.intercomSessionName,
6776
7112
  leafIntercomTarget,
6777
7113
  intercomTarget: leafIntercomTarget,
6778
7114
  ownerState: state === "running" ? "live" : "gone",
@@ -6862,7 +7198,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6862
7198
  if (activeAsyncCapacity && !activeAsyncCapacity.owner.runnerStartedAt) activeAsyncCapacity.rollback();
6863
7199
  if (foregroundControl) {
6864
7200
  settleForegroundSchedulingOwner(foregroundControl);
6865
- removeForegroundControlIfIdle(deps.state, runId);
7201
+ removeForegroundControlIfIdle(deps.state, runId, deps.trackRetainedNestedRoute);
6866
7202
  }
6867
7203
  }
6868
7204
 
@@ -6883,7 +7219,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6883
7219
  const normalizedAction = typeof params.action === "string" ? params.action.trim() : params.action;
6884
7220
  const requestParams = normalizedAction ? { ...params, action: normalizedAction } : params;
6885
7221
  if (normalizedAction) return execute(id, requestParams, signal, onUpdate, ctx).then(withAggregatedToolUsage);
6886
- const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth);
7222
+ const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
6887
7223
  const dispatchParams = applyForceTopLevelAsyncOverride(requestParams, depth, deps.config.forceTopLevelAsync === true);
6888
7224
  const runsForeground = dispatchParams.clarify === true || (dispatchParams.async ?? deps.asyncByDefault) !== true;
6889
7225
  if (!runsForeground) return execute(id, requestParams, signal, onUpdate, ctx).then(withAggregatedToolUsage);
@@ -6909,7 +7245,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6909
7245
  }
6910
7246
  let publicParams = normalized.params as SubagentParamsLike;
6911
7247
  if (publicParams.workflow !== undefined) {
6912
- const resolved = resolveWorkflowResource(publicParams.workflow, publicParams.args);
7248
+ const resolved = resolveWorkflowResource(publicParams.workflow, publicParams.args, ctx.sessionManager.getSessionId() ?? undefined);
6913
7249
  if (!resolved.ok) return Promise.resolve({ content: [{ type: "text", text: resolved.error }], isError: true, details: { mode: "workflow", results: [] } });
6914
7250
  const { workflow: _workflow, args: _args, ...withoutResourceInput } = publicParams;
6915
7251
  publicParams = { ...withoutResourceInput, workflowScript: resolved.resource.script };
@@ -6956,16 +7292,27 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6956
7292
  ctx: ExtensionContext,
6957
7293
  ) => {
6958
7294
  const ownerSessionId = resolveCurrentSessionId(ctx.sessionManager);
6959
- let ownerExecutor = scheduledOwnerExecutors.get(ownerSessionId);
6960
- if (!ownerExecutor) {
6961
- ownerExecutor = createSubagentExecutor({
6962
- ...deps,
6963
- state: createScheduledOwnerState(deps.state, ownerSessionId, ctx),
6964
- });
6965
- scheduledOwnerExecutors.set(ownerSessionId, ownerExecutor);
7295
+ const runtimeOwnerId = ctx.sessionManager.getSessionId() || null;
7296
+ let ownerExecutors = scheduledOwnerExecutors.get(runtimeOwnerId);
7297
+ if (!ownerExecutors) {
7298
+ ownerExecutors = new Map();
7299
+ scheduledOwnerExecutors.set(runtimeOwnerId, ownerExecutors);
6966
7300
  }
6967
- return ownerExecutor.executePublic(id, params, signal, undefined, ctx);
7301
+ let owner = ownerExecutors.get(ownerSessionId);
7302
+ if (!owner) {
7303
+ const state = createScheduledOwnerState(deps.state, ownerSessionId, ctx);
7304
+ owner = { state, executor: createSubagentExecutor({ ...deps, state }) };
7305
+ ownerExecutors.set(ownerSessionId, owner);
7306
+ }
7307
+ return owner.executor.executePublic(id, params, signal, undefined, ctx);
6968
7308
  };
6969
7309
 
6970
- return { execute: executeWithSingleDispatchGuard, executePublic, executeDelegated, executeScheduled };
6971
- }
7310
+ function* getCurrentSupervisorOwnerStates(): Iterable<SubagentState> {
7311
+ const ownerId = deps.state.supervisorOwnerSessionId;
7312
+ if (!ownerId) return;
7313
+ // File transitions remain separate entries; other runtime owners are never scanned.
7314
+ for (const owner of scheduledOwnerExecutors.get(ownerId)?.values() ?? []) yield owner.state;
7315
+ }
7316
+
7317
+ return { execute: executeWithSingleDispatchGuard, executePublic, executeDelegated, executeScheduled, getCurrentSupervisorOwnerStates };
7318
+ }