@duckmind/dm-windows-x64 0.61.4 → 0.61.9

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 (279) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +211 -67
  3. package/extensions/dm-subagents/agents/claude-code-writer.md +15 -0
  4. package/extensions/dm-subagents/agents/claude-code.md +15 -0
  5. package/extensions/dm-subagents/agents/codex-exec-writer.md +15 -0
  6. package/extensions/dm-subagents/agents/codex-exec.md +15 -0
  7. package/extensions/dm-subagents/agents/cursor-agent-writer.md +14 -0
  8. package/extensions/dm-subagents/agents/cursor-agent.md +14 -0
  9. package/extensions/dm-subagents/agents/delegate.md +3 -2
  10. package/extensions/dm-subagents/agents/oracle.md +10 -5
  11. package/extensions/dm-subagents/agents/researcher.md +2 -2
  12. package/extensions/dm-subagents/agents/reviewer.md +17 -7
  13. package/extensions/dm-subagents/agents/scout.md +5 -5
  14. package/extensions/dm-subagents/agents/worker.md +6 -2
  15. package/extensions/dm-subagents/async-retention-discovery-worker.mjs +167 -0
  16. package/extensions/dm-subagents/index.js +4 -0
  17. package/extensions/dm-subagents/inspector-runner.mjs +10 -0
  18. package/extensions/dm-subagents/install.mjs +3 -2
  19. package/extensions/dm-subagents/package.json +2 -2
  20. package/extensions/dm-subagents/prompts/council.md +60 -0
  21. package/extensions/dm-subagents/prompts/parallel-review.md +5 -1
  22. package/extensions/dm-subagents/prompts/review-loop.md +13 -7
  23. package/extensions/dm-subagents/skills/council-mode/SKILL.md +59 -0
  24. package/extensions/dm-subagents/skills/council-mode/references/pass-contracts.md +150 -0
  25. package/extensions/dm-subagents/skills/dm-subagents/SKILL.md +96 -911
  26. package/extensions/dm-subagents/skills/dm-subagents/references/constraints-and-recipes.md +70 -0
  27. package/extensions/dm-subagents/skills/dm-subagents/references/execution-controls.md +539 -0
  28. package/extensions/dm-subagents/skills/dm-subagents/references/management-authoring-rpc.md +161 -0
  29. package/extensions/dm-subagents/skills/dm-subagents/references/multi-lane-orchestration.md +51 -0
  30. package/extensions/dm-subagents/skills/dm-subagents/references/prompting-and-roles.md +295 -0
  31. package/extensions/dm-subagents/skills/dm-subagents/references/review-and-validation.md +73 -0
  32. package/extensions/dm-subagents/src/agents/agent-management.js +716 -484
  33. package/extensions/dm-subagents/src/agents/agent-refinements.js +563 -0
  34. package/extensions/dm-subagents/src/agents/agent-serializer.js +70 -5
  35. package/extensions/dm-subagents/src/agents/agents.js +1447 -299
  36. package/extensions/dm-subagents/src/agents/builtin-names.js +15 -0
  37. package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -7
  38. package/extensions/dm-subagents/src/agents/frontmatter.js +64 -14
  39. package/extensions/dm-subagents/src/agents/identity.js +1 -1
  40. package/extensions/dm-subagents/src/agents/proactive-skills.js +14 -11
  41. package/extensions/dm-subagents/src/agents/runtime-agent-events.js +49 -0
  42. package/extensions/dm-subagents/src/agents/runtime-agent-registry.js +412 -0
  43. package/extensions/dm-subagents/src/agents/skills.js +52 -35
  44. package/extensions/dm-subagents/src/api/agents.js +6 -0
  45. package/extensions/dm-subagents/src/api/background-work.js +151 -0
  46. package/extensions/dm-subagents/src/api/capability-ceiling.js +12 -0
  47. package/extensions/dm-subagents/src/api/control-channel.js +3 -0
  48. package/extensions/dm-subagents/src/api/delegation.js +5 -0
  49. package/extensions/dm-subagents/src/api/dm-args.js +3 -0
  50. package/extensions/dm-subagents/src/api/external-job-provider.js +137 -0
  51. package/extensions/dm-subagents/src/api/external-runs.js +233 -0
  52. package/extensions/dm-subagents/src/api/intercom-bridge.js +3 -0
  53. package/extensions/dm-subagents/src/api/preflight.js +322 -0
  54. package/extensions/dm-subagents/src/api/project-panes.js +11 -0
  55. package/extensions/dm-subagents/src/api/shared-types.js +4 -0
  56. package/extensions/dm-subagents/src/extension/config.js +175 -0
  57. package/extensions/dm-subagents/src/extension/control-notices.js +4 -43
  58. package/extensions/dm-subagents/src/extension/doctor.js +71 -17
  59. package/extensions/dm-subagents/src/extension/fanout-child.js +49 -32
  60. package/extensions/dm-subagents/src/extension/index.js +711 -265
  61. package/extensions/dm-subagents/src/extension/public-execution.js +114 -0
  62. package/extensions/dm-subagents/src/extension/rpc.js +427 -22
  63. package/extensions/dm-subagents/src/extension/schemas.js +158 -65
  64. package/extensions/dm-subagents/src/extension/steering-notices.js +23 -0
  65. package/extensions/dm-subagents/src/extension/subagent-guide.js +31 -0
  66. package/extensions/dm-subagents/src/extension/tool-description.js +92 -74
  67. package/extensions/dm-subagents/src/extension/tool-result.js +7 -0
  68. package/extensions/dm-subagents/src/inspectors/herdr/actions.js +218 -0
  69. package/extensions/dm-subagents/src/inspectors/herdr/client.js +123 -0
  70. package/extensions/dm-subagents/src/inspectors/herdr/focus.js +47 -0
  71. package/extensions/dm-subagents/src/inspectors/herdr/inspector-runner.js +160 -0
  72. package/extensions/dm-subagents/src/inspectors/herdr/project-panes.js +618 -0
  73. package/extensions/dm-subagents/src/inspectors/herdr/session-roots-codec.js +21 -0
  74. package/extensions/dm-subagents/src/inspectors/herdr/shell-command.js +15 -0
  75. package/extensions/dm-subagents/src/integrations/herdr-status.js +377 -0
  76. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +17 -13
  77. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +371 -79
  78. package/extensions/dm-subagents/src/intercom/result-intercom.js +47 -7
  79. package/extensions/dm-subagents/src/missions/actions.js +394 -0
  80. package/extensions/dm-subagents/src/missions/goal-driver.js +149 -0
  81. package/extensions/dm-subagents/src/missions/lifecycle.js +331 -0
  82. package/extensions/dm-subagents/src/missions/store.js +548 -0
  83. package/extensions/dm-subagents/src/missions/types.js +9 -0
  84. package/extensions/dm-subagents/src/missions/workflow-state.js +245 -0
  85. package/extensions/dm-subagents/src/policy/authority.js +37 -0
  86. package/extensions/dm-subagents/src/profiles/profiles.js +36 -18
  87. package/extensions/dm-subagents/src/runs/background/active-async-capacity.js +427 -0
  88. package/extensions/dm-subagents/src/runs/background/active-run-index.js +122 -0
  89. package/extensions/dm-subagents/src/runs/background/async-execution.js +925 -137
  90. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +515 -148
  91. package/extensions/dm-subagents/src/runs/background/async-resume.js +378 -51
  92. package/extensions/dm-subagents/src/runs/background/async-retention.js +828 -0
  93. package/extensions/dm-subagents/src/runs/background/async-status-snapshot.js +31 -0
  94. package/extensions/dm-subagents/src/runs/background/async-status.js +259 -22
  95. package/extensions/dm-subagents/src/runs/background/auto-drain.js +46 -0
  96. package/extensions/dm-subagents/src/runs/background/chain-append.js +50 -15
  97. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +67 -12
  98. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +5 -1
  99. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +3 -11
  100. package/extensions/dm-subagents/src/runs/background/completion-replay.js +245 -0
  101. package/extensions/dm-subagents/src/runs/background/control-channel.js +423 -36
  102. package/extensions/dm-subagents/src/runs/background/fleet-view.js +132 -59
  103. package/extensions/dm-subagents/src/runs/background/index-segment.js +38 -0
  104. package/extensions/dm-subagents/src/runs/background/inspect-rpc.js +373 -0
  105. package/extensions/dm-subagents/src/runs/background/notify.js +357 -57
  106. package/extensions/dm-subagents/src/runs/background/owned-process-tree.js +86 -0
  107. package/extensions/dm-subagents/src/runs/background/process-terminal.js +269 -0
  108. package/extensions/dm-subagents/src/runs/background/result-delivery-ownership.js +34 -0
  109. package/extensions/dm-subagents/src/runs/background/result-files.js +469 -0
  110. package/extensions/dm-subagents/src/runs/background/result-watcher.js +540 -75
  111. package/extensions/dm-subagents/src/runs/background/resume-guidance.js +44 -0
  112. package/extensions/dm-subagents/src/runs/background/retained-children.js +119 -0
  113. package/extensions/dm-subagents/src/runs/background/run-id-query.js +5 -0
  114. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +93 -9
  115. package/extensions/dm-subagents/src/runs/background/run-status.js +303 -32
  116. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +784 -376
  117. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +75 -34
  118. package/extensions/dm-subagents/src/runs/background/steering.js +221 -0
  119. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +3106 -844
  120. package/extensions/dm-subagents/src/runs/background/subagent-wait.js +529 -0
  121. package/extensions/dm-subagents/src/runs/background/terminal-run-index.js +106 -0
  122. package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -1
  123. package/extensions/dm-subagents/src/runs/background/wait-completions.js +155 -0
  124. package/extensions/dm-subagents/src/runs/background/wait-config.js +46 -0
  125. package/extensions/dm-subagents/src/runs/background/wait-subscriptions.js +278 -0
  126. package/extensions/dm-subagents/src/runs/background/wait-tool.js +47 -0
  127. package/extensions/dm-subagents/src/runs/foreground/async-dismiss-action.js +81 -0
  128. package/extensions/dm-subagents/src/runs/foreground/async-steering-action.js +245 -0
  129. package/extensions/dm-subagents/src/runs/foreground/async-stop-action.js +74 -0
  130. package/extensions/dm-subagents/src/runs/foreground/execution.js +1401 -342
  131. package/extensions/dm-subagents/src/runs/foreground/foreground-control.js +133 -0
  132. package/extensions/dm-subagents/src/runs/foreground/foreground-history.js +148 -0
  133. package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +139 -0
  134. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +4278 -1441
  135. package/extensions/dm-subagents/src/runs/foreground/workflow-detach-reconcile.js +278 -0
  136. package/extensions/dm-subagents/src/runs/foreground/workflow-foreground-steering.js +155 -0
  137. package/extensions/dm-subagents/src/runs/shared/abort-recovery.js +97 -0
  138. package/extensions/dm-subagents/src/runs/shared/acceptance.js +597 -148
  139. package/extensions/dm-subagents/src/runs/shared/agent-contract.js +35 -0
  140. package/extensions/dm-subagents/src/runs/shared/async-status-projection.js +472 -0
  141. package/extensions/dm-subagents/src/runs/shared/background-process-options.js +6 -0
  142. package/extensions/dm-subagents/src/runs/shared/capability-ceiling.js +175 -0
  143. package/extensions/dm-subagents/src/runs/shared/child-identity.js +32 -0
  144. package/extensions/dm-subagents/src/runs/shared/child-launch-plan.js +65 -0
  145. package/extensions/dm-subagents/src/runs/shared/child-protocol.js +447 -0
  146. package/extensions/dm-subagents/src/runs/shared/claude-code-adapter.js +120 -0
  147. package/extensions/dm-subagents/src/runs/shared/codex-exec-adapter.js +129 -0
  148. package/extensions/dm-subagents/src/runs/shared/completion-evidence.js +40 -0
  149. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +140 -83
  150. package/extensions/dm-subagents/src/runs/shared/context-mode.js +38 -0
  151. package/extensions/dm-subagents/src/runs/shared/cursor-agent-adapter.js +101 -0
  152. package/extensions/dm-subagents/src/runs/shared/dm-args.js +445 -72
  153. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +27 -16
  154. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +19 -6
  155. package/extensions/dm-subagents/src/runs/shared/extension-bindings.js +81 -0
  156. package/extensions/dm-subagents/src/runs/shared/external-cli-contract.js +134 -0
  157. package/extensions/dm-subagents/src/runs/shared/external-cli-preflight.js +98 -0
  158. package/extensions/dm-subagents/src/runs/shared/external-cli-runner.js +419 -0
  159. package/extensions/dm-subagents/src/runs/shared/external-job-bridge.js +404 -0
  160. package/extensions/dm-subagents/src/runs/shared/external-job-runner.js +334 -0
  161. package/extensions/dm-subagents/src/runs/shared/fast-mode-extension.js +8 -0
  162. package/extensions/dm-subagents/src/runs/shared/host-step-status.js +228 -0
  163. package/extensions/dm-subagents/src/runs/shared/lane-metadata.js +104 -0
  164. package/extensions/dm-subagents/src/runs/shared/launch-cwd.js +17 -0
  165. package/extensions/dm-subagents/src/runs/shared/llm-intent-arbiter.js +190 -0
  166. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +48 -3
  167. package/extensions/dm-subagents/src/runs/shared/mcp-config-sources.js +387 -0
  168. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +212 -137
  169. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-grant.js +131 -0
  170. package/extensions/dm-subagents/src/runs/shared/model-exclusions.js +207 -0
  171. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +225 -55
  172. package/extensions/dm-subagents/src/runs/shared/model-scope.js +85 -28
  173. package/extensions/dm-subagents/src/runs/shared/mutation-evidence.js +182 -0
  174. package/extensions/dm-subagents/src/runs/shared/nested-events.js +264 -102
  175. package/extensions/dm-subagents/src/runs/shared/nested-render.js +15 -5
  176. package/extensions/dm-subagents/src/runs/shared/orca-progress-tabs.js +505 -0
  177. package/extensions/dm-subagents/src/runs/shared/parallel-handoff.js +653 -0
  178. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +29 -12
  179. package/extensions/dm-subagents/src/runs/shared/permissions.js +108 -0
  180. package/extensions/dm-subagents/src/runs/shared/process-signal.js +13 -0
  181. package/extensions/dm-subagents/src/runs/shared/run-fanout-budget.js +257 -0
  182. package/extensions/dm-subagents/src/runs/shared/run-history.js +133 -13
  183. package/extensions/dm-subagents/src/runs/shared/runtime-acknowledged-extensions.js +62 -0
  184. package/extensions/dm-subagents/src/runs/shared/session-lease.js +225 -0
  185. package/extensions/dm-subagents/src/runs/shared/single-output.js +129 -27
  186. package/extensions/dm-subagents/src/runs/shared/spawn-budget.js +95 -0
  187. package/extensions/dm-subagents/src/runs/shared/structured-output.js +129 -10
  188. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +66 -11
  189. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +548 -70
  190. package/extensions/dm-subagents/src/runs/shared/subagent-startup-retry.js +50 -0
  191. package/extensions/dm-subagents/src/runs/shared/task-intent.js +130 -0
  192. package/extensions/dm-subagents/src/runs/shared/tool-availability.js +59 -0
  193. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +7 -5
  194. package/extensions/dm-subagents/src/runs/shared/tool-timeout.js +64 -0
  195. package/extensions/dm-subagents/src/runs/shared/usage-budget.js +74 -0
  196. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +22 -0
  197. package/extensions/dm-subagents/src/runs/shared/worktree-cleanup-plan.js +721 -0
  198. package/extensions/dm-subagents/src/runs/shared/worktree.js +168 -19
  199. package/extensions/dm-subagents/src/shared/accessible-dir.js +35 -0
  200. package/extensions/dm-subagents/src/shared/agent-stream-options.js +3 -0
  201. package/extensions/dm-subagents/src/shared/artifacts.js +170 -11
  202. package/extensions/dm-subagents/src/shared/atomic-json.js +36 -38
  203. package/extensions/dm-subagents/src/shared/capacity-resilient-json.js +77 -0
  204. package/extensions/dm-subagents/src/shared/child-session-name.js +15 -0
  205. package/extensions/dm-subagents/src/shared/child-transcript.js +57 -2
  206. package/extensions/dm-subagents/src/shared/completion-owner.js +7 -0
  207. package/extensions/dm-subagents/src/shared/display-text.js +142 -0
  208. package/extensions/dm-subagents/src/shared/extension-context.js +17 -0
  209. package/extensions/dm-subagents/src/shared/file-coalescer.js +9 -0
  210. package/extensions/dm-subagents/src/shared/file-system-retry.js +56 -0
  211. package/extensions/dm-subagents/src/shared/fork-context.js +96 -33
  212. package/extensions/dm-subagents/src/shared/formatters.js +21 -7
  213. package/extensions/dm-subagents/src/shared/launch-contract.js +94 -0
  214. package/extensions/dm-subagents/src/shared/model-info.js +10 -5
  215. package/extensions/dm-subagents/src/shared/node-executable.js +19 -0
  216. package/extensions/dm-subagents/src/shared/prompt-resources.js +10 -0
  217. package/extensions/dm-subagents/src/shared/pruned-fork.js +427 -0
  218. package/extensions/dm-subagents/src/shared/session-file-trust.js +19 -0
  219. package/extensions/dm-subagents/src/shared/session-tokens.js +14 -3
  220. package/extensions/dm-subagents/src/shared/settings.js +39 -47
  221. package/extensions/dm-subagents/src/shared/shortcuts.js +16 -0
  222. package/extensions/dm-subagents/src/shared/status-format.js +9 -2
  223. package/extensions/dm-subagents/src/shared/thinking-ceiling.js +41 -0
  224. package/extensions/dm-subagents/src/shared/types.js +47 -7
  225. package/extensions/dm-subagents/src/shared/utf8.js +12 -0
  226. package/extensions/dm-subagents/src/shared/utils.js +151 -131
  227. package/extensions/dm-subagents/src/shared/watch-strategy.js +3 -0
  228. package/extensions/dm-subagents/src/shared/workflow-child-permit.js +84 -0
  229. package/extensions/dm-subagents/src/slash/delegation-adapters.js +274 -0
  230. package/extensions/dm-subagents/src/slash/delegation-json.js +113 -0
  231. package/extensions/dm-subagents/src/slash/delegation-request.js +152 -0
  232. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +294 -243
  233. package/extensions/dm-subagents/src/slash/prompt-workflows.js +35 -73
  234. package/extensions/dm-subagents/src/slash/selector.js +101 -0
  235. package/extensions/dm-subagents/src/slash/slash-bridge.js +17 -1
  236. package/extensions/dm-subagents/src/slash/slash-commands.js +722 -732
  237. package/extensions/dm-subagents/src/slash/slash-live-state.js +37 -19
  238. package/extensions/dm-subagents/src/slash/subagents-admin.js +410 -0
  239. package/extensions/dm-subagents/src/tui/fleet-status.js +824 -0
  240. package/extensions/dm-subagents/src/tui/fleet-transcript.js +479 -0
  241. package/extensions/dm-subagents/src/tui/fleet.js +1326 -0
  242. package/extensions/dm-subagents/src/tui/render-helpers.js +22 -0
  243. package/extensions/dm-subagents/src/tui/render.js +1511 -261
  244. package/extensions/dm-subagents/src/watchdog/change-signature.js +220 -0
  245. package/extensions/dm-subagents/src/watchdog/child-status.js +151 -0
  246. package/extensions/dm-subagents/src/watchdog/emission-guard.js +90 -0
  247. package/extensions/dm-subagents/src/watchdog/lsp-diagnostics.js +484 -0
  248. package/extensions/dm-subagents/src/watchdog/model-selection.js +154 -0
  249. package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +138 -0
  250. package/extensions/dm-subagents/src/watchdog/register-child.js +112 -0
  251. package/extensions/dm-subagents/src/watchdog/register-main.js +419 -0
  252. package/extensions/dm-subagents/src/watchdog/render.js +54 -0
  253. package/extensions/dm-subagents/src/watchdog/review.js +251 -0
  254. package/extensions/dm-subagents/src/watchdog/runtime.js +803 -0
  255. package/extensions/dm-subagents/src/watchdog/scope.js +56 -0
  256. package/extensions/dm-subagents/src/watchdog/settings.js +515 -0
  257. package/extensions/dm-subagents/src/watchdog/tool-actions.js +151 -0
  258. package/extensions/dm-subagents/src/watchdog/turn-delta.js +169 -0
  259. package/extensions/dm-subagents/src/watchdog/types.js +29 -0
  260. package/extensions/dm-subagents/src/watchdog/warning-format.js +58 -0
  261. package/extensions/dm-subagents/src/workflows/chat-progress.js +116 -0
  262. package/extensions/dm-subagents/src/workflows/host-command.js +227 -0
  263. package/extensions/dm-subagents/src/workflows/scripted-workflow.js +2011 -0
  264. package/extensions/dm-subagents/src/workflows/workflow-child-summary.js +116 -0
  265. package/extensions/dm-subagents/src/workflows/workflow-preflight.js +243 -0
  266. package/extensions/dm-subagents/src/workflows/workflow-receipt.js +387 -0
  267. package/extensions/dm-subagents/src/workflows/workflow-settlement.js +189 -0
  268. package/package.json +4 -3
  269. package/extensions/dm-fff/package.json +0 -21
  270. package/extensions/dm-fff/src/index.js +0 -691
  271. package/extensions/dm-fff/src/query.js +0 -60
  272. package/extensions/dm-subagents/agents/context-builder.md +0 -46
  273. package/extensions/dm-subagents/agents/planner.md +0 -55
  274. package/extensions/dm-subagents/prompts/parallel-context-build.md +0 -55
  275. package/extensions/dm-subagents/prompts/parallel-handoff-plan.md +0 -61
  276. package/extensions/dm-subagents/src/runs/background/wait.js +0 -206
  277. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +0 -1013
  278. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +0 -981
  279. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +0 -50
@@ -1,34 +1,56 @@
1
1
  import { spawn } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
2
3
  import * as fs from "node:fs";
3
4
  import * as path from "node:path";
4
- import { createRequire } from "node:module";
5
5
  import { fileURLToPath } from "node:url";
6
- import { applyThinkingSuffix, resolveRuntimeExtensionPath } from "../shared/dm-args.js";
6
+ import { createRequire } from "node:module";
7
+ import { discoverAgents, formatUnknownAgentError, unknownAgentDiagnosticContext } from "../../agents/agents.js";
8
+ import { appendAgentRefinementOverlay } from "../../agents/agent-refinements.js";
9
+ import { writePrivateAtomicJson } from "../../shared/atomic-json.js";
10
+ import { currentCompletionOwnerId } from "../../shared/completion-owner.js";
11
+ import { planChildLaunch, resolveStepBehavior, suppressProgressForReadOnlyTask } from "../shared/child-launch-plan.js";
12
+ import { applyThinkingSuffix, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan } from "../shared/dm-args.js";
7
13
  import { injectOutputPathSystemPrompt, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.js";
8
- import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveStepBehavior, suppressProgressForReadOnlyTask, writeInitialProgressFile } from "../../shared/settings.js";
14
+ import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveExistingReadPaths, writeInitialProgressFile } from "../../shared/settings.js";
9
15
  import { resolvePiPackageRoot } from "../shared/dm-spawn.js";
16
+ import { preflightLaunchCwd } from "../shared/launch-cwd.js";
17
+ import { resolveNodeExecutable } from "../../shared/node-executable.js";
18
+ import { backgroundProcessOptions } from "../shared/background-process-options.js";
10
19
  import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.js";
11
20
  import { buildAgentMemoryInjection } from "../../agents/agent-memory.js";
12
- import { PI_CODING_AGENT_PACKAGE_ROOT_ENV, resolveChildCwd } from "../../shared/utils.js";
13
- import { buildModelCandidates, resolveSubagentModelOverride } from "../shared/model-fallback.js";
14
- import { resolveEffectiveThinking } from "../../shared/model-info.js";
21
+ import { DM_CODING_AGENT_PACKAGE_ROOT_ENV, PROMPT_REDACTED, resolveChildCwd } from "../../shared/utils.js";
22
+ import { buildModelCandidates, inheritsParentModel, resolveEffectiveSubagentModel, resolveSubagentModelOverride } from "../shared/model-fallback.js";
23
+ import { resolveToolTimeoutMs, toolTimeoutFromEnv } from "../shared/tool-timeout.js";
24
+ import { resolveModelScopesForAgent } from "../shared/model-scope.js";
25
+ import { findModelInfo, resolveEffectiveThinking } from "../../shared/model-info.js";
26
+ import { assertThinkingWithinCeiling, decodeThinkingCeiling, intersectThinkingCeilings, SUBAGENT_THINKING_CEILING_ENV } from "../../shared/thinking-ceiling.js";
15
27
  import { resolveExpectedWorktreeAgentCwd } from "../shared/worktree.js";
16
28
  import { buildWorkflowGraphSnapshot } from "../shared/workflow-graph.js";
17
29
  import { ChainOutputValidationError, validateChainOutputBindings } from "../shared/chain-outputs.js";
18
30
  import { createStructuredOutputRuntime } from "../shared/structured-output.js";
19
- import { resolveEffectiveAcceptance } from "../shared/acceptance.js";
31
+ import { resolveEffectiveAcceptance, validateAcceptanceInput, validateExecutionAcceptance } from "../shared/acceptance.js";
32
+ import { createRunFanoutBudget, writeRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.js";
33
+ import { validateImplementationToolContract } from "../shared/completion-guard.js";
20
34
  import {
21
- ASYNC_DIR,
22
- RESULTS_DIR,
35
+ DIRS,
23
36
  SUBAGENT_ASYNC_STARTED_EVENT,
24
37
  SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
25
38
  TEMP_ROOT_DIR,
26
39
  getAsyncConfigPath,
27
40
  resolveChildMaxSubagentDepth
28
41
  } from "../../shared/types.js";
29
- import { nestedResultsPath, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.js";
30
- import { initialTurnBudgetState } from "../shared/turn-budget.js";
42
+ import { nestedResultsPath, nestedSummaryFromAsyncStatus, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.js";
43
+ import { resultFilePath } from "./result-files.js";
31
44
  import { validateToolBudgetConfig } from "../shared/tool-budget.js";
45
+ import { usageBudgetState } from "../shared/usage-budget.js";
46
+ import { finalizeProcessTerminal, readProcessTerminal } from "./process-terminal.js";
47
+ import { statusStepDescription } from "./chain-append.js";
48
+ import { SUBAGENT_PROCESS_TERMINAL_EVENT } from "../../shared/types.js";
49
+ import { assertAgentAllowedByCapabilityCeiling, decodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV } from "../shared/capability-ceiling.js";
50
+ import { agentDefinitionDigest, launchBindingDigest } from "../../shared/launch-contract.js";
51
+ import { resolvePermissionRules } from "../shared/permissions.js";
52
+ import { normalizeExtensionBindings, omitExtensionBindingsEnv } from "../shared/extension-bindings.js";
53
+ import { assertWorkflowLaneKey, normalizeWorkflowLaneMetadata } from "../shared/lane-metadata.js";
32
54
  const require = createRequire(import.meta.url);
33
55
  const piPackageRoot = resolvePiPackageRoot();
34
56
  function resolveJitiCliFromPackageJson(packageJsonPath) {
@@ -71,37 +93,25 @@ function resolveJitiCliPath() {
71
93
  return;
72
94
  }
73
95
  const jitiCliPath = resolveJitiCliPath();
74
- export function formatAsyncStartedMessage(headline) {
75
- return [
76
- headline,
77
- "",
96
+ export const DEFAULT_ASYNC_TIMEOUT_MS = 30 * 60 * 1000;
97
+ export function formatAsyncStartedMessage(headline, interactive) {
98
+ const guidance = interactive ? [
99
+ "The async run is detached and running in the background.",
100
+ "You are in an interactive session. Return control to the user now; DM will wake you through the native completion notification when this subagent completes or needs attention. Do not run sleep/polling loops to wait for this async subagent; it does not need a wait call.",
101
+ "Use bg_wait only for provider, detached, or other background work that lacks a native completion notification.",
102
+ "If the current turn must receive results from work without a native notification before it ends, call blocking bg_wait(); ordinary async subagent runs do not need a wait call because their completion is delivered natively.",
103
+ 'Otherwise, continue any independent work or return control to the user. Use subagent({ action: "status", id: "..." }) for a one-shot status/result or to inspect a blocked/stale run, never as a wait loop.'
104
+ ] : [
78
105
  "The async run is detached. Do not run sleep timers or polling loops just to wait for it.",
79
- "If you have independent work, continue that work. When you have nothing left to do until the async result arrives, call wait() — it blocks until the run finishes and delivers the completion here. Only if you are certain you will get another turn (an interactive session where the user will prompt you again) can you instead stop and let DM wake you; inside a skill that must run to completion, or in a non-interactive run, there is no next turn, so use wait().",
80
- 'Use subagent({ action: "status", id: "..." }) when you need a one-shot status/result or to inspect a blocked/stale run. To block until completion, prefer wait(). Do not poll in a loop just to wait.'
81
- ].join(`
106
+ "This is a non-interactive run: DM auto-drains current-session subagent work at agent_end so detached children are not abandoned. Use bg_wait only when this turn must receive provider, detached, or other background-work results that have no native completion notification.",
107
+ 'Use subagent({ action: "status", id: "..." }) when you need a one-shot status/result or to inspect a blocked/stale run; do not poll in a loop.'
108
+ ];
109
+ return [headline, "", ...guidance].join(`
82
110
  `);
83
111
  }
84
112
  export function isAsyncAvailable() {
85
113
  return jitiCliPath !== undefined;
86
114
  }
87
- function isNodeExecutableName(execPath) {
88
- const basename = path.basename(execPath).toLowerCase();
89
- return basename === "node" || basename === "node.exe" || basename === "nodejs" || basename === "nodejs.exe";
90
- }
91
- function canUseCurrentNodeExecutable(execPath) {
92
- try {
93
- fs.accessSync(execPath, process.platform === "win32" ? fs.constants.F_OK : fs.constants.X_OK);
94
- return true;
95
- } catch {
96
- return false;
97
- }
98
- }
99
- function resolveAsyncRunnerNodeCommand() {
100
- if (isNodeExecutableName(process.execPath) && canUseCurrentNodeExecutable(process.execPath)) {
101
- return process.execPath;
102
- }
103
- return process.platform === "win32" ? "node.exe" : "node";
104
- }
105
115
  export function resolveAsyncRunnerLogPaths(cfg) {
106
116
  const asyncDir = typeof cfg.asyncDir === "string" ? cfg.asyncDir : undefined;
107
117
  if (!asyncDir)
@@ -118,29 +128,151 @@ function closeFd(fd) {
118
128
  fs.closeSync(fd);
119
129
  } catch {}
120
130
  }
121
- export function resolveAsyncRunnerPath(moduleUrl = import.meta.url) {
122
- const modulePath = fileURLToPath(moduleUrl);
123
- const runnerStem = path.basename(modulePath).startsWith("async-execution.") ? "subagent-runner" : path.join("..", "runs", "background", "subagent-runner");
124
- return resolveRuntimeExtensionPath(moduleUrl, runnerStem);
131
+ const RUNNER_STARTUP_TIMEOUT_MS = 1e4;
132
+ const RUNNER_STARTUP_WAIT_BUFFER = typeof SharedArrayBuffer !== "undefined" ? new SharedArrayBuffer(4) : undefined;
133
+ const RUNNER_STARTUP_WAIT_VIEW = RUNNER_STARTUP_WAIT_BUFFER ? new Int32Array(RUNNER_STARTUP_WAIT_BUFFER) : undefined;
134
+ function waitForStartupInterval(delayMs = 20) {
135
+ if (RUNNER_STARTUP_WAIT_VIEW) {
136
+ Atomics.wait(RUNNER_STARTUP_WAIT_VIEW, 0, 0, delayMs);
137
+ return;
138
+ }
139
+ const waitUntil = Date.now() + delayMs;
140
+ while (Date.now() < waitUntil) {}
125
141
  }
126
- function spawnRunner(cfg, suffix, cwd) {
127
- if (!jitiCliPath) {
128
- return { error: "upstream jiti for TypeScript execution could not be found; ensure package dependencies are installed" };
142
+ function readRunnerStartup(startupPath, expectedState, expectedToken) {
143
+ if (!fs.existsSync(startupPath))
144
+ return;
145
+ try {
146
+ const payload = JSON.parse(fs.readFileSync(startupPath, "utf-8"));
147
+ if (payload.state === "error" && typeof payload.error === "string")
148
+ return { ok: false, error: payload.error, startupDidNotProceed: true };
149
+ if (payload.state !== expectedState)
150
+ return;
151
+ if (typeof payload.token !== "string" || expectedToken !== undefined && payload.token !== expectedToken) {
152
+ return { ok: false, error: `Async runner wrote an invalid ${expectedState} startup handshake: ${startupPath}`, startupDidNotProceed: true };
153
+ }
154
+ return { ok: true, token: payload.token };
155
+ } catch (error) {
156
+ return { ok: false, error: `Failed to read async runner startup handshake '${startupPath}': ${error instanceof Error ? error.message : String(error)}`, startupDidNotProceed: true };
129
157
  }
158
+ }
159
+ function waitForRunnerStartup(startupPath, expectedState, timeoutMs, expectedToken) {
160
+ const deadline = Date.now() + timeoutMs;
161
+ for (;; ) {
162
+ const result = readRunnerStartup(startupPath, expectedState, expectedToken);
163
+ if (result)
164
+ return result;
165
+ if (Date.now() >= deadline)
166
+ break;
167
+ waitForStartupInterval(Math.min(20, Math.max(1, deadline - Date.now())));
168
+ }
169
+ const finalResult = readRunnerStartup(startupPath, expectedState, expectedToken);
170
+ if (finalResult)
171
+ return finalResult;
172
+ return { ok: false, error: `Timed out after ${timeoutMs}ms waiting for the async runner startup state '${expectedState}'.`, startupDidNotProceed: true };
173
+ }
174
+ function writeRunnerStartupControl(filePath, payload) {
175
+ writePrivateAtomicJson(filePath, payload);
176
+ }
177
+ function runnerIsAlive(pid) {
130
178
  try {
131
- const cwdStats = fs.statSync(cwd);
132
- if (!cwdStats.isDirectory()) {
133
- return { error: `cwd is not a directory: ${cwd}` };
179
+ process.kill(pid, 0);
180
+ return true;
181
+ } catch (error) {
182
+ return error.code === "EPERM";
183
+ }
184
+ }
185
+ function terminateRunnerBeforeProceed(pid) {
186
+ for (const signal of ["SIGTERM", "SIGKILL"]) {
187
+ if (!runnerIsAlive(pid))
188
+ return true;
189
+ try {
190
+ process.kill(pid, signal);
191
+ } catch {
192
+ if (!runnerIsAlive(pid))
193
+ return true;
134
194
  }
135
- } catch {
136
- return { error: `cwd does not exist: ${cwd}` };
195
+ const deadline = Date.now() + 1000;
196
+ while (runnerIsAlive(pid) && Date.now() < deadline)
197
+ waitForStartupInterval();
198
+ }
199
+ return !runnerIsAlive(pid);
200
+ }
201
+ function persistPreProceedStartupFailure(asyncDir, runId, runnerProcessInstanceId, sessionId, completionOwnerId, message) {
202
+ const now = Date.now();
203
+ try {
204
+ const statusPath = path.join(asyncDir, "status.json");
205
+ let status = {};
206
+ try {
207
+ status = JSON.parse(fs.readFileSync(statusPath, "utf-8"));
208
+ } catch {}
209
+ writePrivateAtomicJson(statusPath, {
210
+ ...status,
211
+ runId,
212
+ ...sessionId ? { sessionId } : {},
213
+ ...completionOwnerId ? { completionOwnerId } : {},
214
+ state: "failed",
215
+ lastUpdate: now,
216
+ error: message,
217
+ processTerminal: {
218
+ version: 1,
219
+ state: "not-started",
220
+ runId,
221
+ runnerProcessInstanceId
222
+ }
223
+ });
224
+ writePrivateAtomicJson(path.join(asyncDir, "process-terminal-candidate.json"), {
225
+ version: 1,
226
+ runId,
227
+ runnerProcessInstanceId,
228
+ writers: {},
229
+ expectedWriters: { 0: 0 }
230
+ });
231
+ } catch {}
232
+ }
233
+ function isStaleExtensionContextError(error) {
234
+ return error instanceof Error && /extension ctx is stale|stale after session replacement or reload/i.test(error.message);
235
+ }
236
+ export function emitProcessTerminalEvent(ctx, proof) {
237
+ try {
238
+ ctx.dm.events.emit(SUBAGENT_PROCESS_TERMINAL_EVENT, proof);
239
+ } catch (error) {
240
+ if (isStaleExtensionContextError(error))
241
+ return;
242
+ console.error("Failed to emit subagent process-terminal event:", error);
243
+ }
244
+ }
245
+ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, onProcessTerminal, requestedCwd = cwd) {
246
+ const cwdError = preflightLaunchCwd(requestedCwd, cwd);
247
+ if (cwdError)
248
+ return { error: cwdError };
249
+ if (!jitiCliPath) {
250
+ return { error: "upstream jiti for TypeScript execution could not be found; ensure package dependencies are installed" };
137
251
  }
138
252
  fs.mkdirSync(TEMP_ROOT_DIR, { recursive: true });
139
253
  const cfgPath = getAsyncConfigPath(suffix);
140
- fs.writeFileSync(cfgPath, JSON.stringify(cfg));
141
- const runner = resolveAsyncRunnerPath();
142
- const nodeCommand = resolveAsyncRunnerNodeCommand();
143
- const logPaths = resolveAsyncRunnerLogPaths(cfg);
254
+ const runnerProcessInstanceId = randomUUID();
255
+ const hasRevivalLease = typeof cfg.revivalLease === "object";
256
+ const launchBarrierToken = hasRevivalLease ? undefined : runnerProcessInstanceId;
257
+ const launchConfig = { ...cfg, runnerProcessInstanceId, ...launchBarrierToken ? { launchBarrierToken } : {} };
258
+ writePrivateAtomicJson(cfgPath, launchConfig);
259
+ const runner = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-runner.js");
260
+ const nodeCommand = resolveNodeExecutable();
261
+ const launchForStartup = launchConfig;
262
+ const launchAsyncDir = typeof launchForStartup.asyncDir === "string" ? launchForStartup.asyncDir : undefined;
263
+ const launchRunId = typeof launchForStartup.id === "string" ? launchForStartup.id : suffix;
264
+ const launchSessionId = typeof launchForStartup.sessionId === "string" ? launchForStartup.sessionId : undefined;
265
+ const launchCompletionOwnerId = typeof launchForStartup.completionOwnerId === "string" ? launchForStartup.completionOwnerId : undefined;
266
+ const startupPath = typeof launchForStartup.revivalLease === "object" && launchAsyncDir ? path.join(launchAsyncDir, "runner-startup.json") : undefined;
267
+ const startupAckPath = startupPath ? path.join(path.dirname(startupPath), "runner-startup-ack.json") : undefined;
268
+ const startupProceedPath = launchAsyncDir && (startupPath || launchBarrierToken) ? path.join(launchAsyncDir, "runner-startup-proceed.json") : undefined;
269
+ if (startupPath)
270
+ fs.rmSync(startupPath, { force: true });
271
+ if (startupAckPath)
272
+ fs.rmSync(startupAckPath, { force: true });
273
+ if (startupProceedPath)
274
+ fs.rmSync(startupProceedPath, { force: true });
275
+ const logPaths = resolveAsyncRunnerLogPaths(launchConfig);
144
276
  let stdoutFd;
145
277
  let stderrFd;
146
278
  try {
@@ -151,12 +283,11 @@ function spawnRunner(cfg, suffix, cwd) {
151
283
  }
152
284
  const proc = spawn(nodeCommand, [jitiCliPath, runner, cfgPath], {
153
285
  cwd,
154
- detached: true,
286
+ ...backgroundProcessOptions(),
155
287
  stdio: ["ignore", stdoutFd ?? "ignore", stderrFd ?? "ignore"],
156
- windowsHide: true,
157
288
  env: {
158
- ...process.env,
159
- ...piPackageRoot ? { [PI_CODING_AGENT_PACKAGE_ROOT_ENV]: piPackageRoot } : {}
289
+ ...omitExtensionBindingsEnv(process.env),
290
+ ...piPackageRoot ? { [DM_CODING_AGENT_PACKAGE_ROOT_ENV]: piPackageRoot } : {}
160
291
  }
161
292
  });
162
293
  closeFd(stdoutFd);
@@ -164,11 +295,122 @@ function spawnRunner(cfg, suffix, cwd) {
164
295
  proc.on("error", (error) => {
165
296
  console.error(`[dm-subagents] async spawn failed: ${error.message}`);
166
297
  });
298
+ proc.once("close", (exitCode, signal) => {
299
+ const launch = launchConfig;
300
+ const asyncDir = launch.asyncDir;
301
+ const runId = launch.id;
302
+ if (typeof asyncDir !== "string" || typeof runId !== "string")
303
+ return;
304
+ finalizeProcessTerminal(asyncDir, runId, {
305
+ processInstanceId: runnerProcessInstanceId,
306
+ closeObservedAt: Date.now(),
307
+ exitCode,
308
+ signal
309
+ });
310
+ const persisted = readProcessTerminal(asyncDir, { runId, runnerProcessInstanceId });
311
+ if (!persisted)
312
+ return;
313
+ if (launch.nestedRoute && launch.nestedSelf) {
314
+ try {
315
+ let status;
316
+ try {
317
+ status = JSON.parse(fs.readFileSync(path.join(asyncDir, "status.json"), "utf-8"));
318
+ status.processTerminal = persisted;
319
+ } catch {
320
+ status = {
321
+ runId,
322
+ mode: "single",
323
+ state: persisted.state === "observed" ? "complete" : "failed",
324
+ startedAt: persisted.observedAt ?? Date.now(),
325
+ lastUpdate: Date.now(),
326
+ processTerminal: persisted
327
+ };
328
+ }
329
+ writeNestedEvent(launch.nestedRoute, {
330
+ type: "subagent.nested.completed",
331
+ ts: Date.now(),
332
+ parentRunId: launch.nestedSelf.parentRunId,
333
+ parentStepIndex: launch.nestedSelf.parentStepIndex,
334
+ child: nestedSummaryFromAsyncStatus(status, asyncDir, {
335
+ id: runId,
336
+ parentRunId: launch.nestedSelf.parentRunId,
337
+ parentStepIndex: launch.nestedSelf.parentStepIndex,
338
+ depth: launch.nestedSelf.depth,
339
+ path: launch.nestedSelf.path,
340
+ mode: status.mode,
341
+ ts: Date.now()
342
+ })
343
+ });
344
+ } catch (error) {
345
+ console.error("Failed to emit final nested process-terminal status:", error);
346
+ }
347
+ }
348
+ onProcessTerminal?.(persisted);
349
+ });
167
350
  if (typeof proc.pid !== "number") {
168
351
  return { error: `async runner did not produce a pid for cwd: ${cwd}` };
169
352
  }
353
+ try {
354
+ writePrivateAtomicJson(initialStatusPath, {
355
+ ...initialStatus,
356
+ pid: proc.pid,
357
+ processTerminal: { version: 1, state: "pending", runId: initialStatus.runId, runnerProcessInstanceId }
358
+ });
359
+ } catch (error) {
360
+ const message = `Failed to persist initial async status: ${error instanceof Error ? error.message : String(error)}`;
361
+ const terminationObserved = terminateRunnerBeforeProceed(proc.pid);
362
+ return { pid: proc.pid, runnerProcessInstanceId, error: message, terminationObserved, startupDidNotProceed: true };
363
+ }
364
+ if (launchBarrierToken && startupProceedPath) {
365
+ try {
366
+ writeRunnerStartupControl(startupProceedPath, { action: "proceed", token: launchBarrierToken });
367
+ } catch (error) {
368
+ const message = `Failed to authorize async runner startup: ${error instanceof Error ? error.message : String(error)}`;
369
+ if (launchAsyncDir)
370
+ persistPreProceedStartupFailure(launchAsyncDir, launchRunId, runnerProcessInstanceId, launchSessionId, launchCompletionOwnerId, message);
371
+ const terminationObserved = terminateRunnerBeforeProceed(proc.pid);
372
+ return { pid: proc.pid, runnerProcessInstanceId, error: message, terminationObserved, startupDidNotProceed: true };
373
+ }
374
+ }
170
375
  proc.unref();
171
- return { pid: proc.pid };
376
+ if (startupPath && startupAckPath && startupProceedPath) {
377
+ const persistStartupFailure = (message) => {
378
+ if (launchAsyncDir)
379
+ persistPreProceedStartupFailure(launchAsyncDir, launchRunId, runnerProcessInstanceId, launchSessionId, launchCompletionOwnerId, message);
380
+ };
381
+ const ready = waitForRunnerStartup(startupPath, "ready", RUNNER_STARTUP_TIMEOUT_MS);
382
+ if (ready.ok === false) {
383
+ persistStartupFailure(ready.error);
384
+ const terminationObserved = terminateRunnerBeforeProceed(proc.pid);
385
+ return { pid: proc.pid, runnerProcessInstanceId, error: ready.error, terminationObserved, startupDidNotProceed: ready.startupDidNotProceed };
386
+ }
387
+ try {
388
+ writeRunnerStartupControl(startupAckPath, { action: "ack", token: ready.token });
389
+ } catch (error) {
390
+ const message = `Failed to acknowledge async runner startup: ${error instanceof Error ? error.message : String(error)}`;
391
+ persistStartupFailure(message);
392
+ const terminationObserved = terminateRunnerBeforeProceed(proc.pid);
393
+ return { pid: proc.pid, runnerProcessInstanceId, error: message, terminationObserved, startupDidNotProceed: true };
394
+ }
395
+ const acknowledged = waitForRunnerStartup(startupPath, "acknowledged", RUNNER_STARTUP_TIMEOUT_MS, ready.token);
396
+ if (acknowledged.ok === false) {
397
+ persistStartupFailure(acknowledged.error);
398
+ const terminationObserved = terminateRunnerBeforeProceed(proc.pid);
399
+ return { pid: proc.pid, runnerProcessInstanceId, error: acknowledged.error, terminationObserved, startupDidNotProceed: acknowledged.startupDidNotProceed };
400
+ }
401
+ try {
402
+ writeRunnerStartupControl(startupProceedPath, { action: "proceed", token: ready.token });
403
+ } catch (error) {
404
+ const message = `Failed to authorize async runner startup: ${error instanceof Error ? error.message : String(error)}`;
405
+ persistStartupFailure(message);
406
+ const terminationObserved = terminateRunnerBeforeProceed(proc.pid);
407
+ return { pid: proc.pid, runnerProcessInstanceId, error: message, terminationObserved, startupDidNotProceed: true };
408
+ }
409
+ try {
410
+ fs.rmSync(startupPath, { force: true });
411
+ } catch {}
412
+ }
413
+ return { pid: proc.pid, runnerProcessInstanceId };
172
414
  } catch (error) {
173
415
  closeFd(stdoutFd);
174
416
  closeFd(stderrFd);
@@ -225,12 +467,12 @@ export function buildAsyncRunnerSteps(id, params) {
225
467
  throw error;
226
468
  }
227
469
  const workflowGraph = buildWorkflowGraphSnapshot({ runId: id, mode: resultMode, steps: graphChain });
470
+ const diagnosticContext = params.unknownAgentDiagnosticContext ?? unknownAgentDiagnosticContext(discoverAgents(path.resolve(runnerCwd), "both"));
228
471
  for (const s of chain) {
229
472
  const stepAgents = isParallelStep(s) ? s.parallel.map((t) => t.agent) : isDynamicParallelStep(s) ? [s.parallel.agent] : [s.agent];
230
473
  for (const agentName of stepAgents) {
231
- if (!agents.find((x) => x.name === agentName)) {
232
- return { error: `Unknown agent: ${agentName}` };
233
- }
474
+ if (!agents.find((x) => x.name === agentName))
475
+ return { error: formatUnknownAgentError(agentName, diagnosticContext) };
234
476
  }
235
477
  }
236
478
  let progressInstructionCreated = false;
@@ -242,20 +484,64 @@ export function buildAsyncRunnerSteps(id, params) {
242
484
  ...s.reads !== undefined ? { reads: s.reads } : {},
243
485
  ...s.progress !== undefined ? { progress: s.progress } : {},
244
486
  ...stepSkillInput !== undefined ? { skills: stepSkillInput } : {},
245
- ...s.model ? { model: s.model } : {}
487
+ ...s.model !== undefined ? { model: s.model } : {},
488
+ ...s.fast !== undefined ? { fast: s.fast } : {}
246
489
  };
247
490
  };
248
- const buildSeqStep = (s, sessionFile, behaviorCwd, progressPrecreated = false, resolvedBehavior, flatIndex) => {
491
+ const buildSeqStep = (s, sessionFile, behaviorCwd, progressPrecreated = false, resolvedBehavior, flatIndex, parallelOutputNamespace, runFanoutPath) => {
249
492
  const a = agents.find((x) => x.name === s.agent);
493
+ const externalRunner = a.runner?.type === "external-cli" || a.runner?.type === "external-job";
494
+ const externalRunnerType = a.runner?.type;
495
+ if (externalRunner) {
496
+ const unsupported = [];
497
+ if (s.model !== undefined)
498
+ unsupported.push("model override");
499
+ if (s.outputSchema !== undefined)
500
+ unsupported.push("structured output");
501
+ if (s.acceptance !== undefined || params.agentContract !== undefined || s.agentContract !== undefined)
502
+ unsupported.push("acceptance/agent contract");
503
+ if (s.toolBudget !== undefined || params.toolBudget !== undefined || a.toolBudget !== undefined || params.configToolBudget !== undefined)
504
+ unsupported.push("tool budget");
505
+ if ((s.fast ?? params.fast ?? a.fast) === true)
506
+ unsupported.push("fast mode");
507
+ if (params.contextForAgent?.(s.agent) === "fork")
508
+ unsupported.push("fork context");
509
+ if (unsupported.length > 0)
510
+ throw new AsyncStartValidationError(`Agent '${a.name}' uses runner.type='${externalRunnerType}' and does not support: ${unsupported.join(", ")}.`);
511
+ }
512
+ try {
513
+ assertAgentAllowedByCapabilityCeiling(a.name, intersectSubagentCapabilityCeilings(params.capabilityCeiling, decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV])));
514
+ } catch (error) {
515
+ throw new AsyncStartValidationError(error instanceof Error ? error.message : String(error));
516
+ }
250
517
  const toolBudgetInput = s.toolBudget ?? params.toolBudget ?? a.toolBudget ?? params.configToolBudget;
251
518
  const resolvedToolBudget = validateToolBudgetConfig(toolBudgetInput, s.toolBudget ? "toolBudget" : a.toolBudget ? "agent.toolBudget" : "config.toolBudget");
252
519
  if (resolvedToolBudget.error)
253
520
  throw new AsyncStartValidationError(resolvedToolBudget.error);
254
- const stepCwd = resolveChildCwd(runnerCwd, s.cwd);
255
- const instructionCwd = behaviorCwd ?? stepCwd;
256
- const behavior = suppressProgressForReadOnlyTask(resolvedBehavior ?? resolveStepBehavior(a, buildStepOverrides(s), chainSkills), s.task, originalTask);
257
- const skillNames = behavior.skills === false ? [] : behavior.skills;
258
- const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(skillNames, stepCwd, ctx.cwd);
521
+ const resolvedToolTimeout = resolveToolTimeoutMs({
522
+ callValue: params.callToolTimeoutMs,
523
+ agentValue: a.defaultToolTimeoutMs,
524
+ configValue: params.configToolTimeoutMs,
525
+ envValue: params.toolTimeoutMsEnv ?? toolTimeoutFromEnv()
526
+ });
527
+ if (resolvedToolTimeout.error)
528
+ throw new AsyncStartValidationError(resolvedToolTimeout.error);
529
+ const launchPlan = planChildLaunch({
530
+ agentConfig: a,
531
+ stepOverrides: buildStepOverrides(s),
532
+ task: s.task,
533
+ originalTask,
534
+ runnerCwd,
535
+ runtimeCwd: ctx.cwd,
536
+ stepCwdInput: s.cwd,
537
+ behaviorCwd,
538
+ chainSkills,
539
+ outputBaseDir,
540
+ parallelOutputNamespace,
541
+ resolvedBehavior
542
+ });
543
+ const { stepCwd, instructionCwd, readExistenceCwd, behavior, namespaceOutputPath, outputPath, skillNames } = launchPlan;
544
+ const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(skillNames, stepCwd, ctx.cwd, a.skillPath, a.filePath ? path.dirname(a.filePath) : stepCwd);
259
545
  if (missingSkills.includes("dm-subagents"))
260
546
  throw new UnavailableSubagentSkillError(UNAVAILABLE_SUBAGENT_SKILL_ERROR);
261
547
  let systemPrompt = a.systemPrompt?.trim() ?? "";
@@ -271,62 +557,158 @@ ${injection}` : injection;
271
557
 
272
558
  ${memoryInjection}` : memoryInjection;
273
559
  }
274
- const readInstructions = buildChainInstructions({ ...behavior, output: false, progress: false }, instructionCwd, false);
560
+ systemPrompt = appendAgentRefinementOverlay(systemPrompt, { cwd: stepCwd, agentName: a.name });
561
+ const readInstructions = buildChainInstructions({ ...behavior, output: false, progress: false }, instructionCwd, false, undefined, readExistenceCwd);
275
562
  const isFirstProgressAgent = behavior.progress && !progressPrecreated && !progressInstructionCreated;
276
563
  if (behavior.progress)
277
564
  progressInstructionCreated = true;
278
565
  const progressInstructions = buildChainInstructions({ ...behavior, output: false, reads: false }, progressDir, isFirstProgressAgent);
279
- const outputPath = resolveSingleOutputPath(behavior.output, ctx.cwd, instructionCwd, outputBaseDir);
280
- systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath);
566
+ if (!namespaceOutputPath)
567
+ systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath, a);
281
568
  const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Async step (${s.agent})`);
282
569
  if (validationError)
283
570
  throw new AsyncStartValidationError(validationError);
284
571
  let taskTemplate = s.task ?? "{previous}";
285
572
  taskTemplate = taskTemplate.replace(/\{task\}/g, originalTask ?? "");
286
573
  taskTemplate = taskTemplate.replace(/\{chain_dir\}/g, runnerCwd);
287
- const task = injectSingleOutputInstruction(`${readInstructions.prefix}${taskTemplate}${progressInstructions.suffix}`, outputPath);
288
- const requestedModel = behavior.model ?? a.model;
289
- const primaryModel = resolveSubagentModelOverride(requestedModel, ctx.currentModel, availableModels, ctx.currentModelProvider, { scope: ctx.modelScope, source: behavior.model ? "explicit" : "inherited" });
574
+ const taskText = `${readInstructions.prefix}${taskTemplate}${progressInstructions.suffix}`;
575
+ const task = namespaceOutputPath ? taskText : injectSingleOutputInstruction(taskText, outputPath, a);
576
+ const modelScopes = resolveModelScopesForAgent(ctx.modelScope, a.name, ctx.currentModel);
577
+ const primaryModelFromParent = inheritsParentModel(s.model, a.model, ctx.currentModel);
578
+ const primaryModel = externalRunner ? undefined : resolveEffectiveSubagentModel(s.model, a.model, ctx.currentModel, availableModels, a.modelProvider ?? ctx.currentModelProvider, { scope: modelScopes });
290
579
  const thinkingOverride = flatIndex === undefined ? undefined : thinkingOverridesByFlatIndex?.[flatIndex];
291
- const effectiveThinking = thinkingOverride ?? a.thinking;
292
- const model = applyThinkingSuffix(primaryModel, effectiveThinking, thinkingOverride !== undefined);
580
+ const effectiveThinking = externalRunner ? undefined : thinkingOverride ?? a.thinking;
581
+ const model = externalRunner ? undefined : applyThinkingSuffix(primaryModel, effectiveThinking, thinkingOverride !== undefined);
582
+ const contextLimit = model ? findModelInfo(model, availableModels, a.modelProvider ?? ctx.currentModelProvider)?.contextWindow : undefined;
583
+ const thinkingCeiling = externalRunner ? undefined : intersectThinkingCeilings(params.thinkingCeiling, a.maxThinking, decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]));
584
+ if (!externalRunner) {
585
+ try {
586
+ assertThinkingWithinCeiling({ model, configThinking: effectiveThinking, ceiling: thinkingCeiling, agent: a.name, runId: id });
587
+ } catch (error) {
588
+ throw new AsyncStartValidationError(error instanceof Error ? error.message : String(error));
589
+ }
590
+ }
591
+ const agentContract = s.agentContract ?? params.agentContract;
592
+ const permissionRules = resolvePermissionRules(ctx.permissions, a.permissions);
593
+ const modelCandidates = externalRunner ? [] : buildModelCandidates(primaryModel, a.fallbackModels, availableModels, a.modelProvider ?? ctx.currentModelProvider, {
594
+ scope: modelScopes,
595
+ primaryModelFromParent
596
+ }).flatMap((candidate) => {
597
+ const resolved = applyThinkingSuffix(candidate, effectiveThinking, thinkingOverride !== undefined);
598
+ return resolved ? [resolved] : [];
599
+ });
600
+ if (!externalRunner) {
601
+ try {
602
+ for (const candidate of modelCandidates)
603
+ assertThinkingWithinCeiling({ model: candidate, configThinking: effectiveThinking, ceiling: thinkingCeiling, agent: a.name, runId: id });
604
+ } catch (error) {
605
+ throw new AsyncStartValidationError(error instanceof Error ? error.message : String(error));
606
+ }
607
+ }
608
+ const fast = s.fast ?? params.fast ?? a.fast;
609
+ const toolPlan = resolvePiLaunchToolPlan({
610
+ tools: a.tools,
611
+ allowNestedSubagents: a.allowNestedSubagents,
612
+ extensions: a.extensions,
613
+ subagentOnlyExtensions: a.subagentOnlyExtensions,
614
+ mcpDirectTools: a.mcpDirectTools,
615
+ cwd: stepCwd,
616
+ requireReadTool: Boolean(resolvedSkills.length),
617
+ structuredOutput: Boolean(s.outputSchema),
618
+ fast,
619
+ model,
620
+ modelCandidates,
621
+ capabilityCeiling: params.capabilityCeiling,
622
+ inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
623
+ agentName: a.name,
624
+ permissionRules,
625
+ runtimeSnapshotHost: ctx.dm
626
+ });
627
+ const launchResolvedExtensions = externalRunner ? undefined : projectLaunchResolvedChildExtensions(toolPlan);
628
+ if (externalRunner && permissionRules) {
629
+ throw new AsyncStartValidationError(`Agent '${a.name}' uses runner.type='${externalRunnerType}', which cannot enforce native DM child permission rules.`);
630
+ }
631
+ if (!externalRunner) {
632
+ const contractTools = toolPlan.explicitToolAllowlist ? toolPlan.effectiveToolAllowlist : undefined;
633
+ const contractError = validateImplementationToolContract({
634
+ agent: a.name,
635
+ task,
636
+ tools: contractTools,
637
+ mcpDirectTools: toolPlan.effectiveMcpTools,
638
+ configuredExtensions: toolPlan.configuredExtensions,
639
+ requestedTools: toolPlan.requestedBuiltinTools,
640
+ acceptanceRole: a.acceptanceRole,
641
+ completionGuard: a.completionGuard
642
+ });
643
+ if (contractError)
644
+ throw new AsyncStartValidationError(contractError);
645
+ }
293
646
  return {
294
647
  parentSessionId: ctx.parentSessionId ?? ctx.currentSessionId,
648
+ permissionRules,
649
+ ...params.capabilityCeiling ? { capabilityCeiling: params.capabilityCeiling } : {},
650
+ ...runFanoutPath ? { runFanoutPath } : {},
295
651
  agent: s.agent,
296
652
  task,
653
+ ...a.runner ? { runner: a.runner } : {},
654
+ ...params.contextForAgent ? { context: params.contextForAgent(s.agent) } : {},
655
+ ...agentContract ? { agentContract } : {},
297
656
  phase: s.phase,
298
657
  label: s.label,
299
658
  outputName: s.as,
300
659
  structured: Boolean(s.outputSchema),
301
660
  cwd: stepCwd,
661
+ requestedCwd: s.cwd ?? stepCwd,
302
662
  model,
663
+ ...contextLimit !== undefined ? { contextLimit } : {},
664
+ ...fast !== undefined ? { fast } : {},
303
665
  thinking: resolveEffectiveThinking(model, effectiveThinking),
304
- modelCandidates: buildModelCandidates(primaryModel, a.fallbackModels, availableModels, ctx.currentModelProvider, { scope: ctx.modelScope }).map((candidate) => applyThinkingSuffix(candidate, effectiveThinking, thinkingOverride !== undefined)),
666
+ ...thinkingCeiling ? { thinkingCeiling } : {},
667
+ launchResolvedExtensions,
668
+ ...toolPlan.mcpConfig ? { mcpConfig: toolPlan.mcpConfig } : {},
669
+ ...toolPlan.runtimeServerNames ? { runtimeServerNames: toolPlan.runtimeServerNames } : {},
670
+ modelCandidates: externalRunner ? undefined : modelCandidates,
671
+ ...primaryModelFromParent ? { skipPrimaryModelVerification: true } : {},
672
+ ...availableModels && availableModels.length > 0 ? { modelVerificationRegistry: availableModels } : {},
305
673
  tools: a.tools,
674
+ allowNestedSubagents: a.allowNestedSubagents,
306
675
  extensions: a.extensions,
307
676
  subagentOnlyExtensions: a.subagentOnlyExtensions,
308
677
  mcpDirectTools: a.mcpDirectTools,
678
+ mutationTools: a.mutationTools,
309
679
  completionGuard: a.completionGuard,
310
680
  systemPrompt,
311
681
  systemPromptMode: a.systemPromptMode,
312
682
  inheritProjectContext: a.inheritProjectContext,
683
+ inheritGlobalContext: a.inheritGlobalContext,
313
684
  inheritSkills: a.inheritSkills,
314
685
  skills: resolvedSkills.map((r) => r.name),
315
686
  outputPath,
687
+ ...namespaceOutputPath ? { namespaceOutputPath: true } : {},
316
688
  outputMode: behavior.outputMode,
317
689
  sessionFile,
318
690
  maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, a.maxSubagentDepth),
691
+ timeoutMs: a.defaultTimeoutMs ?? DEFAULT_ASYNC_TIMEOUT_MS,
692
+ toolTimeoutMs: resolvedToolTimeout.toolTimeoutMs,
693
+ waitToolEnabled: params.waitToolEnabled,
694
+ waitToolDefaultTimeoutMs: params.waitToolDefaultTimeoutMs,
319
695
  effectiveAcceptance: resolveEffectiveAcceptance({
320
696
  explicit: s.acceptance,
321
697
  agentName: s.agent,
322
- task: s.task,
698
+ acceptanceRole: a.acceptanceRole,
699
+ task,
323
700
  mode: resultMode,
324
701
  async: true,
325
- dynamic: false
702
+ dynamic: false,
703
+ agentContract
326
704
  }),
705
+ acceptanceInput: s.acceptance,
706
+ acceptanceRole: a.acceptanceRole,
707
+ ...s.gateOn ? { gateOn: s.gateOn } : {},
327
708
  ...s.outputSchema ? { structuredOutputSchema: s.outputSchema } : {},
328
- ...s.outputSchema ? { structuredOutput: createStructuredOutputRuntime(s.outputSchema, path.join(asyncDir, "structured-output")) } : {},
329
- ...resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}
709
+ ...s.outputSchema ? { structuredOutput: createStructuredOutputRuntime(s.outputSchema, path.join(asyncDir, "structured-output"), { captureAcceptanceReport: s.acceptance !== false }) } : {},
710
+ ...resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {},
711
+ ...s.worktree ? { worktree: true } : {}
330
712
  };
331
713
  };
332
714
  let flatStepIndex = 0;
@@ -365,7 +747,7 @@ ${memoryInjection}` : memoryInjection;
365
747
  }
366
748
  }
367
749
  const staticStep = nextFlatStep();
368
- return buildSeqStep(t, staticStep.sessionFile, behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex], staticStep.index);
750
+ return buildSeqStep({ ...t, agentContract: t.agentContract ?? s.agentContract, gateOn: t.gateOn ?? s.gateOn }, staticStep.sessionFile, behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex], staticStep.index, { stepIndex, taskIndex }, resultMode === "parallel" ? `tasks[${taskIndex}]` : `chain[${stepIndex}].parallel[${taskIndex}]`);
369
751
  }),
370
752
  concurrency: s.concurrency,
371
753
  failFast: s.failFast,
@@ -382,9 +764,10 @@ ${memoryInjection}` : memoryInjection;
382
764
  }
383
765
  const maxItems = s.expand.maxItems ?? params.dynamicFanoutMaxItems ?? 0;
384
766
  const dynamicFlatSteps = Array.from({ length: maxItems }, () => nextFlatStep());
767
+ const parallel = buildSeqStep({ ...s.parallel, agentContract: s.parallel.agentContract ?? s.agentContract, gateOn: s.parallel.gateOn ?? s.gateOn }, undefined, undefined, progressPrecreated, behavior, undefined, { stepIndex });
385
768
  return {
386
769
  expand: s.expand,
387
- parallel: buildSeqStep(s.parallel, undefined, undefined, progressPrecreated, behavior),
770
+ parallel,
388
771
  collect: s.collect,
389
772
  concurrency: s.concurrency,
390
773
  failFast: s.failFast,
@@ -395,15 +778,31 @@ ${memoryInjection}` : memoryInjection;
395
778
  effectiveAcceptance: resolveEffectiveAcceptance({
396
779
  explicit: s.acceptance,
397
780
  agentName: s.parallel.agent,
398
- task: s.parallel.task,
781
+ acceptanceRole: agent.acceptanceRole,
782
+ task: parallel.task,
399
783
  mode: resultMode,
400
784
  async: true,
401
- dynamicGroup: true
402
- })
785
+ dynamicGroup: true,
786
+ agentContract: s.agentContract ?? params.agentContract
787
+ }),
788
+ acceptanceInput: s.acceptance,
789
+ acceptanceRole: agent.acceptanceRole,
790
+ ...s.agentContract ?? params.agentContract ? { agentContract: s.agentContract ?? params.agentContract } : {},
791
+ ...s.gateOn ? { gateOn: s.gateOn } : {},
792
+ ...parallel.thinkingCeiling ? { thinkingCeiling: parallel.thinkingCeiling } : {}
403
793
  };
404
794
  }
795
+ const sequential = s;
796
+ let behaviorCwd;
797
+ if (sequential.worktree) {
798
+ try {
799
+ behaviorCwd = resolveExpectedWorktreeAgentCwd(runnerCwd, `${id}-s${stepIndex}`, 0, worktreeBaseDir);
800
+ } catch {
801
+ behaviorCwd = undefined;
802
+ }
803
+ }
405
804
  const staticStep = nextFlatStep();
406
- return buildSeqStep(s, staticStep.sessionFile, undefined, false, undefined, staticStep.index);
805
+ return buildSeqStep(sequential, staticStep.sessionFile, behaviorCwd, false, undefined, staticStep.index, undefined, `chain[${stepIndex}]`);
407
806
  });
408
807
  const steps = params.attachRoot ? [{
409
808
  agent: params.attachRoot.agent,
@@ -417,8 +816,24 @@ ${memoryInjection}` : memoryInjection;
417
816
  index: params.attachRoot.index
418
817
  },
419
818
  inheritProjectContext: false,
819
+ inheritGlobalContext: false,
420
820
  inheritSkills: false
421
821
  }, ...builtSteps] : builtSteps;
822
+ for (const step of steps) {
823
+ if (!("parallel" in step) || !Array.isArray(step.parallel))
824
+ continue;
825
+ const seen = new Map;
826
+ for (let index = 0;index < step.parallel.length; index++) {
827
+ const task = step.parallel[index];
828
+ if (!task.outputPath)
829
+ continue;
830
+ const previous = seen.get(task.outputPath);
831
+ if (previous) {
832
+ throw new AsyncStartValidationError(`Parallel tasks ${previous.index + 1} (${previous.agent}) and ${index + 1} (${task.agent}) resolve output to the same path: ${task.outputPath}. Use distinct output paths.`);
833
+ }
834
+ seen.set(task.outputPath, { index, agent: task.agent });
835
+ }
836
+ }
422
837
  return { steps, runnerCwd, workflowGraph, eventChain: graphChain, ...originalTask !== undefined ? { originalTask } : {} };
423
838
  } catch (error) {
424
839
  if (error instanceof UnavailableSubagentSkillError || error instanceof AsyncStartValidationError)
@@ -449,11 +864,26 @@ export function executeAsyncChain(id, params) {
449
864
  nestedRoute
450
865
  } = params;
451
866
  const resultMode = params.resultMode ?? "chain";
867
+ const acceptanceErrors = validateExecutionAcceptance({
868
+ chain: chain.map((step) => {
869
+ if (isParallelStep(step))
870
+ return { parallel: step.parallel };
871
+ if (isDynamicParallelStep(step))
872
+ return { acceptance: step.acceptance, parallel: step.parallel };
873
+ return { acceptance: step.acceptance };
874
+ })
875
+ });
876
+ if (acceptanceErrors.length > 0)
877
+ return formatAsyncStartError(resultMode, acceptanceErrors.join(" "));
878
+ const capabilityCeiling = params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
452
879
  const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
453
880
  const nestedAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
454
- const asyncDir = inheritedNestedRoute ? path.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute.rootRunId, id) : path.join(ASYNC_DIR, id);
881
+ const asyncDir = inheritedNestedRoute ? path.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute.rootRunId, id) : path.join(DIRS.async, id);
882
+ let runFanoutBudget;
455
883
  try {
884
+ runFanoutBudget = params.runFanoutBudget ?? createRunFanoutBudget(id, 64);
456
885
  fs.mkdirSync(asyncDir, { recursive: true });
886
+ writeRunFanoutBudgetDescriptor(asyncDir, runFanoutBudget);
457
887
  } catch (error) {
458
888
  const message = error instanceof Error ? error.message : String(error);
459
889
  return {
@@ -468,20 +898,31 @@ export function executeAsyncChain(id, params) {
468
898
  attachRoot: params.attachRoot,
469
899
  resultMode,
470
900
  agents,
901
+ unknownAgentDiagnosticContext: params.unknownAgentDiagnosticContext,
471
902
  ctx,
472
903
  availableModels: params.availableModels,
473
904
  cwd,
474
905
  chainSkills: params.chainSkills,
475
906
  sessionFilesByFlatIndex,
476
907
  thinkingOverridesByFlatIndex,
908
+ contextForAgent: params.contextForAgent,
477
909
  progressDir: params.progressDir ?? (artifactsDir ? path.join(artifactsDir, "progress", id) : resultMode === "parallel" ? path.join(asyncDir, "progress") : undefined),
910
+ agentContract: params.agentContract,
478
911
  outputBaseDir: artifactsDir ? path.join(artifactsDir, "outputs", id) : undefined,
479
912
  dynamicFanoutMaxItems: params.dynamicFanoutMaxItems,
480
913
  maxSubagentDepth,
914
+ waitToolEnabled: params.waitToolEnabled,
915
+ waitToolDefaultTimeoutMs: params.waitToolDefaultTimeoutMs,
481
916
  worktreeBaseDir,
482
917
  asyncDir,
918
+ fast: params.fast,
483
919
  toolBudget: params.toolBudget,
484
- configToolBudget: params.configToolBudget
920
+ configToolBudget: params.configToolBudget,
921
+ callToolTimeoutMs: params.callToolTimeoutMs,
922
+ configToolTimeoutMs: params.configToolTimeoutMs,
923
+ toolTimeoutMsEnv: params.toolTimeoutMsEnv ?? toolTimeoutFromEnv(),
924
+ capabilityCeiling,
925
+ thinkingCeiling: params.thinkingCeiling
485
926
  });
486
927
  if ("error" in built) {
487
928
  try {
@@ -491,10 +932,10 @@ export function executeAsyncChain(id, params) {
491
932
  }
492
933
  const { steps, runnerCwd, workflowGraph, eventChain } = built;
493
934
  const deadlineAt = params.timeoutMs !== undefined ? Date.now() + params.timeoutMs : undefined;
494
- const initialTurnBudget = params.turnBudget ? initialTurnBudgetState(params.turnBudget) : undefined;
935
+ const initialUsageBudget = usageBudgetState(params.usageBudget, undefined);
495
936
  let childTargetIndex = 0;
496
937
  const childIntercomTargets = childIntercomTarget ? steps.flatMap((step) => {
497
- if (!("parallel" in step) && step.importAsyncRoot) {
938
+ if (!("parallel" in step) && "importAsyncRoot" in step && step.importAsyncRoot) {
498
939
  childTargetIndex++;
499
940
  return [undefined];
500
941
  }
@@ -505,14 +946,27 @@ export function executeAsyncChain(id, params) {
505
946
  }
506
947
  return step.parallel.map((task) => childIntercomTarget(task.agent, childTargetIndex++));
507
948
  }
508
- return [childIntercomTarget(step.agent, childTargetIndex++)];
949
+ return "agent" in step ? [childIntercomTarget(step.agent, childTargetIndex++)] : [undefined];
509
950
  }) : undefined;
951
+ const initialStatusSteps = eventChain.flatMap((step) => isParallelStep(step) ? step.parallel.map((task) => ({ agent: task.agent, ...statusStepDescription(task.task) ? { description: statusStepDescription(task.task) } : {}, ...task.label ? { label: task.label } : {}, ...task.as ? { outputName: task.as } : {}, status: "pending" })) : isDynamicParallelStep(step) ? [{ agent: `expand:${step.parallel.agent}`, label: step.label ?? step.parallel.label ?? `Dynamic fanout (${step.collect.as})`, outputName: step.collect.as, status: "pending" }] : [{ agent: step.agent, ...statusStepDescription(step.task) ? { description: statusStepDescription(step.task) } : {}, ...step.label ? { label: step.label } : {}, ...step.as ? { outputName: step.as } : {}, status: "pending" }]);
952
+ const initialParallelGroups = [];
953
+ let initialFlatIndex = 0;
954
+ for (let stepIndex = 0;stepIndex < eventChain.length; stepIndex++) {
955
+ const step = eventChain[stepIndex];
956
+ if (isParallelStep(step))
957
+ initialParallelGroups.push({ start: initialFlatIndex, count: step.parallel.length, stepIndex });
958
+ else if (isDynamicParallelStep(step))
959
+ initialParallelGroups.push({ start: initialFlatIndex, count: 1, stepIndex });
960
+ initialFlatIndex += isParallelStep(step) ? step.parallel.length : 1;
961
+ }
962
+ const initialStatusAt = Date.now();
963
+ const initialCompletionOwnerId = ctx.completionOwnerId ?? currentCompletionOwnerId();
510
964
  let spawnResult = {};
511
965
  try {
512
966
  spawnResult = spawnRunner({
513
967
  id,
514
968
  steps,
515
- resultPath: inheritedNestedRoute ? nestedResultsPath(inheritedNestedRoute.rootRunId, id) : path.join(RESULTS_DIR, `${id}.json`),
969
+ resultPath: inheritedNestedRoute ? nestedResultsPath(inheritedNestedRoute.rootRunId, id) : resultFilePath(DIRS.results, id),
516
970
  cwd: runnerCwd,
517
971
  placeholder: "{previous}",
518
972
  maxOutput,
@@ -522,14 +976,16 @@ export function executeAsyncChain(id, params) {
522
976
  sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
523
977
  asyncDir,
524
978
  sessionId: ctx.currentSessionId,
979
+ completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
980
+ ...capabilityCeiling ? { capabilityCeiling } : {},
525
981
  piPackageRoot,
526
982
  piArgv1: process.argv[1],
527
983
  worktreeSetupHook,
528
984
  worktreeSetupHookTimeoutMs,
529
985
  worktreeBaseDir,
530
986
  controlConfig,
531
- turnBudget: params.turnBudget,
532
987
  toolBudget: params.toolBudget,
988
+ usageBudget: params.usageBudget,
533
989
  controlIntercomTarget,
534
990
  childIntercomTargets,
535
991
  resultMode,
@@ -537,7 +993,10 @@ export function executeAsyncChain(id, params) {
537
993
  timeoutMs: params.timeoutMs,
538
994
  deadlineAt,
539
995
  globalConcurrencyLimit: params.globalConcurrencyLimit,
996
+ runFanoutBudget,
540
997
  workflowGraph,
998
+ ...params.parentWorkflowRunId ? { parentWorkflowRunId: params.parentWorkflowRunId } : {},
999
+ ...params.workflowKey ? { workflowKey: params.workflowKey } : {},
541
1000
  nestedRoute: nestedRoute ?? inheritedNestedRoute,
542
1001
  nestedSelf: inheritedNestedRoute && nestedAddress ? {
543
1002
  parentRunId: nestedAddress.parentRunId,
@@ -545,17 +1004,45 @@ export function executeAsyncChain(id, params) {
545
1004
  depth: nestedAddress.depth,
546
1005
  path: nestedAddress.path
547
1006
  } : undefined
548
- }, id, runnerCwd);
1007
+ }, id, runnerCwd, {
1008
+ lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
1009
+ runId: id,
1010
+ ...ctx.currentSessionId ? { sessionId: ctx.currentSessionId } : {},
1011
+ ...initialCompletionOwnerId ? { completionOwnerId: initialCompletionOwnerId } : {},
1012
+ mode: resultMode,
1013
+ state: "running",
1014
+ startedAt: initialStatusAt,
1015
+ lastUpdate: initialStatusAt,
1016
+ currentStep: 0,
1017
+ chainStepCount: eventChain.length,
1018
+ ...initialParallelGroups.length ? { parallelGroups: initialParallelGroups } : {},
1019
+ steps: initialStatusSteps
1020
+ }, path.join(asyncDir, "status.json"), (proof) => emitProcessTerminalEvent(ctx, proof));
549
1021
  } catch (error) {
1022
+ params.activeAsyncCapacity?.rollback();
550
1023
  const message = error instanceof Error ? error.message : String(error);
551
1024
  return formatAsyncStartError(resultMode, `Failed to start async ${resultMode} '${id}': ${message}`);
552
1025
  }
553
1026
  if (spawnResult.error) {
1027
+ if (!spawnResult.pid || !spawnResult.runnerProcessInstanceId || spawnResult.startupDidNotProceed && spawnResult.terminationObserved)
1028
+ params.activeAsyncCapacity?.rollback();
1029
+ else
1030
+ params.activeAsyncCapacity?.markStarted(spawnResult.runnerProcessInstanceId);
554
1031
  return formatAsyncStartError(resultMode, `Failed to start async ${resultMode} '${id}': ${spawnResult.error}`);
555
1032
  }
1033
+ if (!spawnResult.pid || !spawnResult.runnerProcessInstanceId) {
1034
+ params.activeAsyncCapacity?.rollback();
1035
+ return formatAsyncStartError(resultMode, `Failed to start async ${resultMode} '${id}': runner identity unavailable`);
1036
+ }
1037
+ params.activeAsyncCapacity?.markStarted(spawnResult.runnerProcessInstanceId);
556
1038
  if (spawnResult.pid) {
557
1039
  const eventFirstStep = eventChain[0];
1040
+ if (!eventFirstStep) {
1041
+ return formatAsyncStartError(resultMode, `Failed to start async ${resultMode} '${id}': event chain has no steps`);
1042
+ }
558
1043
  const firstAgents = isParallelStep(eventFirstStep) ? eventFirstStep.parallel.map((t) => t.agent) : isDynamicParallelStep(eventFirstStep) ? [eventFirstStep.parallel.agent] : [eventFirstStep.agent];
1044
+ const firstTask = isParallelStep(eventFirstStep) ? eventFirstStep.parallel[0]?.task : isDynamicParallelStep(eventFirstStep) ? eventFirstStep.parallel.task : eventFirstStep.task;
1045
+ const workflowGoal = params.goal ?? (params.task?.trim() || firstTask);
559
1046
  const parallelGroups = [];
560
1047
  const flatAgents = [];
561
1048
  let flatStepStart = 0;
@@ -601,41 +1088,50 @@ export function executeAsyncChain(id, params) {
601
1088
  chainStepCount: eventChain.length,
602
1089
  parallelGroups,
603
1090
  ...params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {},
604
- ...initialTurnBudget ? { turnBudget: initialTurnBudget } : {},
605
1091
  startedAt: now,
606
- lastUpdate: now
1092
+ lastUpdate: now,
1093
+ ...capabilityCeiling ? { capabilityCeiling } : {}
607
1094
  }
608
1095
  });
609
1096
  } catch (error) {
610
1097
  console.error("Failed to emit nested async start event:", error);
611
1098
  }
612
1099
  }
613
- ctx.pi.events.emit(SUBAGENT_ASYNC_STARTED_EVENT, {
1100
+ ctx.dm.events.emit(SUBAGENT_ASYNC_STARTED_EVENT, {
614
1101
  lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
615
1102
  id,
616
1103
  pid: spawnResult.pid,
617
1104
  sessionId: ctx.currentSessionId,
1105
+ completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
618
1106
  mode: resultMode,
619
1107
  agent: firstAgents[0],
620
1108
  agents: flatAgents,
621
- task: isParallelStep(eventFirstStep) ? eventFirstStep.parallel[0]?.task?.slice(0, 50) : isDynamicParallelStep(eventFirstStep) ? eventFirstStep.parallel.task?.slice(0, 50) : eventFirstStep.task?.slice(0, 50),
1109
+ task: firstTask?.trim() ? PROMPT_REDACTED : undefined,
1110
+ goal: workflowGoal?.trim() ? PROMPT_REDACTED : undefined,
622
1111
  chain: eventChain.map((s) => isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : isDynamicParallelStep(s) ? `expand:${s.parallel.agent}` : s.agent),
623
1112
  chainStepCount: eventChain.length,
624
1113
  parallelGroups,
625
1114
  workflowGraph,
626
1115
  cwd: runnerCwd,
627
1116
  asyncDir,
1117
+ ...sessionRoot ? { sessionRoot } : {},
628
1118
  ...params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {},
629
- ...initialTurnBudget ? { turnBudget: initialTurnBudget } : {},
1119
+ ...initialUsageBudget ? { usageBudget: initialUsageBudget } : {},
1120
+ ...capabilityCeiling ? { capabilityCeiling } : {},
1121
+ ...params.parentWorkflowRunId ? { parentWorkflowRunId: params.parentWorkflowRunId } : {},
1122
+ ...params.workflowKey ? { workflowKey: params.workflowKey } : {},
630
1123
  nestedRoute
631
1124
  });
632
1125
  }
633
1126
  const chainDesc = chain.map((s) => isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : isDynamicParallelStep(s) ? `expand:${s.parallel.agent}` : s.agent).join(" -> ");
634
1127
  return {
635
- content: [{ type: "text", text: formatAsyncStartedMessage(`Async ${resultMode}: ${chainDesc} [${id}]`) }],
636
- details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir, workflowGraph, ...params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}, ...params.turnBudget ? { turnBudget: params.turnBudget } : {}, ...params.toolBudget ? { toolBudget: params.toolBudget } : {} }
1128
+ content: [{ type: "text", text: formatAsyncStartedMessage(`Async ${resultMode}: ${chainDesc} [${id}]`, ctx.interactive === true) }],
1129
+ details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir, workflowGraph, ...capabilityCeiling ? { capabilityCeiling } : {}, ...params.parentWorkflowRunId ? { parentWorkflowRunId: params.parentWorkflowRunId } : {}, ...params.workflowKey ? { workflowKey: params.workflowKey } : {}, ...params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}, ...params.toolBudget ? { toolBudget: params.toolBudget } : {}, ...initialUsageBudget ? { usageBudget: initialUsageBudget } : {} }
637
1130
  };
638
1131
  }
1132
+ export function workflowAwaitedAsyncResultPath(asyncDir) {
1133
+ return path.join(asyncDir, "workflow-result.json");
1134
+ }
639
1135
  export function executeAsyncSingle(id, params) {
640
1136
  const {
641
1137
  agent,
@@ -657,11 +1153,63 @@ export function executeAsyncSingle(id, params) {
657
1153
  childIntercomTarget,
658
1154
  nestedRoute
659
1155
  } = params;
1156
+ let lane;
1157
+ try {
1158
+ lane = normalizeWorkflowLaneMetadata(params.lane, "lane");
1159
+ assertWorkflowLaneKey(lane, params.workflowKey, "lane");
1160
+ } catch (error) {
1161
+ return formatAsyncStartError("single", error instanceof Error ? error.message : String(error));
1162
+ }
660
1163
  const task = params.task ?? "";
1164
+ let extensionBindings;
1165
+ try {
1166
+ extensionBindings = normalizeExtensionBindings(params.extensionBindings)?.value;
1167
+ } catch (error) {
1168
+ return formatAsyncStartError("single", error instanceof Error ? error.message : String(error));
1169
+ }
1170
+ const acceptanceErrors = validateAcceptanceInput(params.acceptance);
1171
+ if (acceptanceErrors.length > 0)
1172
+ return formatAsyncStartError("single", acceptanceErrors.join(" "));
1173
+ const externalRunner = agentConfig.runner?.type === "external-cli" || agentConfig.runner?.type === "external-job";
1174
+ const externalRunnerType = agentConfig.runner?.type;
1175
+ const permissionRules = resolvePermissionRules(ctx.permissions, agentConfig.permissions);
1176
+ if (externalRunner) {
1177
+ const unsupported = [];
1178
+ if (params.modelOverride !== undefined)
1179
+ unsupported.push("model override");
1180
+ if ((params.fast ?? agentConfig.fast) === true)
1181
+ unsupported.push("fast mode");
1182
+ if (params.thinkingOverride !== undefined)
1183
+ unsupported.push("thinking override");
1184
+ if (params.structuredOutputSchema !== undefined)
1185
+ unsupported.push("structured output");
1186
+ if (params.acceptance !== undefined || params.agentContract !== undefined)
1187
+ unsupported.push("acceptance/agent contract");
1188
+ if (params.toolBudget !== undefined || agentConfig.toolBudget !== undefined || params.configToolBudget !== undefined)
1189
+ unsupported.push("tool budget");
1190
+ if (params.context === "fork")
1191
+ unsupported.push("fork context");
1192
+ if ((params.skills?.length ?? 0) > 0)
1193
+ unsupported.push("skills");
1194
+ if (permissionRules)
1195
+ unsupported.push("native DM child permissions");
1196
+ if (extensionBindings !== undefined)
1197
+ unsupported.push("extension bindings");
1198
+ if (unsupported.length > 0)
1199
+ return formatAsyncStartError("single", `Agent '${agentConfig.name}' uses runner.type='${externalRunnerType}' and does not support: ${unsupported.join(", ")}.`);
1200
+ }
1201
+ const capabilityCeiling = intersectSubagentCapabilityCeilings(params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId), decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]));
1202
+ try {
1203
+ assertAgentAllowedByCapabilityCeiling(agentConfig.name, capabilityCeiling);
1204
+ } catch (error) {
1205
+ return formatAsyncStartError("single", error instanceof Error ? error.message : String(error));
1206
+ }
661
1207
  const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
1208
+ const instructionCwd = params.worktree === true ? resolveExpectedWorktreeAgentCwd(runnerCwd, `${id}-s0`, 0, worktreeBaseDir) : runnerCwd;
1209
+ const readExistenceCwd = params.worktree === true ? runnerCwd : instructionCwd;
662
1210
  const skillNames = params.skills ?? agentConfig.skills ?? [];
663
1211
  const availableModels = params.availableModels;
664
- const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(skillNames, runnerCwd, ctx.cwd);
1212
+ const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(skillNames, runnerCwd, ctx.cwd, agentConfig.skillPath, agentConfig.filePath ? path.dirname(agentConfig.filePath) : runnerCwd);
665
1213
  if (missingSkills.includes("dm-subagents"))
666
1214
  return formatAsyncStartError("single", UNAVAILABLE_SUBAGENT_SKILL_ERROR);
667
1215
  let systemPrompt = agentConfig.systemPrompt?.trim() ?? "";
@@ -677,11 +1225,15 @@ ${injection}` : injection;
677
1225
 
678
1226
  ${memoryInjection}` : memoryInjection;
679
1227
  }
1228
+ systemPrompt = appendAgentRefinementOverlay(systemPrompt, { cwd: runnerCwd, agentName: agentConfig.name });
680
1229
  const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
681
1230
  const nestedAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
682
- const asyncDir = inheritedNestedRoute ? path.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute.rootRunId, id) : path.join(ASYNC_DIR, id);
1231
+ const asyncDir = inheritedNestedRoute ? path.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute.rootRunId, id) : path.join(DIRS.async, id);
1232
+ let runFanoutBudget;
683
1233
  try {
1234
+ runFanoutBudget = params.runFanoutBudget ?? createRunFanoutBudget(id, 64);
684
1235
  fs.mkdirSync(asyncDir, { recursive: true });
1236
+ writeRunFanoutBudgetDescriptor(asyncDir, runFanoutBudget);
685
1237
  } catch (error) {
686
1238
  const message = error instanceof Error ? error.message : String(error);
687
1239
  return {
@@ -691,82 +1243,287 @@ ${memoryInjection}` : memoryInjection;
691
1243
  };
692
1244
  }
693
1245
  const effectiveOutput = normalizeSingleOutputOverride(params.output, agentConfig.output);
694
- const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, runnerCwd, params.outputBaseDir ?? (artifactsDir ? path.join(artifactsDir, "outputs", id) : undefined));
695
- systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath);
696
- const outputMode = params.outputMode ?? "inline";
1246
+ const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, instructionCwd, params.outputBaseDir ?? (artifactsDir ? path.join(artifactsDir, "outputs", id) : undefined));
1247
+ systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath, agentConfig);
1248
+ const outputMode = params.outputMode ?? agentConfig.outputMode ?? "inline";
697
1249
  const validationError = validateFileOnlyOutputMode(outputMode, outputPath, `Async single run (${agent})`);
698
1250
  if (validationError)
699
1251
  return formatAsyncStartError("single", validationError);
700
- const taskWithOutputInstruction = injectSingleOutputInstruction(task, outputPath);
701
- const primaryModel = resolveSubagentModelOverride(params.modelOverride ?? agentConfig.model, ctx.currentModel, availableModels, ctx.currentModelProvider);
702
- const effectiveThinking = params.thinkingOverride ?? agentConfig.thinking;
703
- const model = applyThinkingSuffix(primaryModel, effectiveThinking, params.thinkingOverride !== undefined);
1252
+ const taskWithOutputInstruction = injectSingleOutputInstruction(task, outputPath, agentConfig);
1253
+ const reads = params.reads !== undefined ? params.reads : agentConfig.defaultReads ?? false;
1254
+ const readPaths = Array.isArray(reads) ? resolveExistingReadPaths(reads, readExistenceCwd) : [];
1255
+ const readsInstruction = readPaths.length > 0 ? `[Read from: ${readPaths.join(", ")}]
1256
+
1257
+ ` : "";
1258
+ const taskText = readsInstruction + taskWithOutputInstruction;
1259
+ const modelScopes = resolveModelScopesForAgent(ctx.modelScope, agentConfig.name, ctx.currentModel);
1260
+ const primaryModel = externalRunner ? undefined : params.modelOverrideFromParent ? params.modelOverride : resolveSubagentModelOverride(params.modelOverride ?? agentConfig.model, ctx.currentModel, availableModels, ctx.currentModelProvider, { scope: modelScopes });
1261
+ const effectiveThinking = externalRunner ? undefined : params.thinkingOverride ?? agentConfig.thinking;
1262
+ const model = externalRunner ? undefined : applyThinkingSuffix(primaryModel, effectiveThinking, params.thinkingOverride !== undefined);
1263
+ const contextLimit = model ? findModelInfo(model, availableModels, agentConfig.modelProvider ?? ctx.currentModelProvider)?.contextWindow : undefined;
1264
+ const thinkingCeiling = externalRunner ? undefined : intersectThinkingCeilings(params.thinkingCeiling, agentConfig.maxThinking, decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]));
1265
+ if (!externalRunner) {
1266
+ try {
1267
+ assertThinkingWithinCeiling({ model, configThinking: effectiveThinking, ceiling: thinkingCeiling, agent: agentConfig.name, runId: id });
1268
+ } catch (error) {
1269
+ return formatAsyncStartError("single", error instanceof Error ? error.message : String(error));
1270
+ }
1271
+ }
704
1272
  const toolBudgetInput = params.toolBudget ?? agentConfig.toolBudget ?? params.configToolBudget;
705
1273
  const resolvedToolBudget = validateToolBudgetConfig(toolBudgetInput, params.toolBudget ? "toolBudget" : agentConfig.toolBudget ? "agent.toolBudget" : "config.toolBudget");
706
1274
  if (resolvedToolBudget.error)
707
1275
  return formatAsyncStartError("single", resolvedToolBudget.error);
708
- const deadlineAt = params.timeoutMs !== undefined ? Date.now() + params.timeoutMs : undefined;
709
- const initialTurnBudget = params.turnBudget ? initialTurnBudgetState(params.turnBudget) : undefined;
1276
+ const deadlineAt = params.absoluteDeadlineAt ?? (params.timeoutMs !== undefined ? Date.now() + params.timeoutMs : undefined);
1277
+ const timeoutMs = params.absoluteDeadlineAt !== undefined && deadlineAt !== undefined ? deadlineAt - Date.now() : params.timeoutMs;
1278
+ if (timeoutMs !== undefined && timeoutMs <= 0)
1279
+ return formatAsyncStartError("single", "The source run's absolute deadline expired before recovery could launch.");
1280
+ const resolvedToolTimeout = resolveToolTimeoutMs({
1281
+ callValue: params.toolTimeoutMs,
1282
+ agentValue: agentConfig.defaultToolTimeoutMs,
1283
+ configValue: params.configToolTimeoutMs,
1284
+ envValue: params.toolTimeoutMsEnv ?? toolTimeoutFromEnv()
1285
+ });
1286
+ if (resolvedToolTimeout.error)
1287
+ return formatAsyncStartError("single", resolvedToolTimeout.error);
1288
+ const toolTimeoutMs = resolvedToolTimeout.toolTimeoutMs;
1289
+ const initialUsageBudget = usageBudgetState(params.usageBudget, undefined);
1290
+ const resolvedSessionDir = params.sessionDir ?? (sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined);
1291
+ const structuredOutput = params.structuredOutputSchema ? createStructuredOutputRuntime(params.structuredOutputSchema, path.join(asyncDir, "structured-output"), { captureAcceptanceReport: params.acceptance !== false }) : undefined;
1292
+ const modelCandidates = externalRunner ? [] : buildModelCandidates(primaryModel, agentConfig.fallbackModels, availableModels, agentConfig.modelProvider ?? ctx.currentModelProvider, {
1293
+ scope: modelScopes,
1294
+ primaryModelFromParent: params.modelOverrideFromParent
1295
+ }).flatMap((candidate) => {
1296
+ const resolved = applyThinkingSuffix(candidate, effectiveThinking, params.thinkingOverride !== undefined);
1297
+ return resolved ? [resolved] : [];
1298
+ });
1299
+ if (!externalRunner) {
1300
+ try {
1301
+ for (const candidate of modelCandidates)
1302
+ assertThinkingWithinCeiling({ model: candidate, configThinking: effectiveThinking, ceiling: thinkingCeiling, agent: agentConfig.name, runId: id });
1303
+ } catch (error) {
1304
+ return formatAsyncStartError("single", error instanceof Error ? error.message : String(error));
1305
+ }
1306
+ }
1307
+ const toolPlan = resolvePiLaunchToolPlan({
1308
+ tools: agentConfig.tools,
1309
+ allowNestedSubagents: agentConfig.allowNestedSubagents,
1310
+ extensions: agentConfig.extensions,
1311
+ subagentOnlyExtensions: agentConfig.subagentOnlyExtensions,
1312
+ mcpDirectTools: agentConfig.mcpDirectTools,
1313
+ cwd: runnerCwd,
1314
+ requireReadTool: Boolean(resolvedSkills.length),
1315
+ structuredOutput: Boolean(params.structuredOutputSchema),
1316
+ fast: params.fast ?? agentConfig.fast,
1317
+ model,
1318
+ modelCandidates,
1319
+ capabilityCeiling,
1320
+ inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
1321
+ agentName: agentConfig.name,
1322
+ permissionRules: resolvePermissionRules(ctx.permissions, agentConfig.permissions),
1323
+ runtimeSnapshotHost: ctx.dm
1324
+ });
1325
+ const launchResolvedExtensions = externalRunner ? undefined : projectLaunchResolvedChildExtensions(toolPlan);
1326
+ if (!externalRunner) {
1327
+ const contractTools = toolPlan.explicitToolAllowlist ? toolPlan.effectiveToolAllowlist : undefined;
1328
+ const contractError = validateImplementationToolContract({
1329
+ agent: agentConfig.name,
1330
+ task: taskText,
1331
+ tools: contractTools,
1332
+ mcpDirectTools: toolPlan.effectiveMcpTools,
1333
+ configuredExtensions: toolPlan.configuredExtensions,
1334
+ requestedTools: toolPlan.requestedBuiltinTools,
1335
+ acceptanceRole: agentConfig.acceptanceRole,
1336
+ completionGuard: agentConfig.completionGuard
1337
+ });
1338
+ if (contractError)
1339
+ return formatAsyncStartError("single", contractError);
1340
+ }
1341
+ const launchContractDigest = launchBindingDigest({
1342
+ definitionDigest: agentDefinitionDigest(agentConfig),
1343
+ task,
1344
+ ...model ? { model } : {},
1345
+ modelCandidates,
1346
+ ...(params.fast ?? agentConfig.fast) !== undefined ? { fast: params.fast ?? agentConfig.fast } : {},
1347
+ ...resolveEffectiveThinking(model, effectiveThinking) ? { thinking: resolveEffectiveThinking(model, effectiveThinking) } : {},
1348
+ ...thinkingCeiling ? { thinkingCeiling } : {},
1349
+ systemPrompt,
1350
+ systemPromptMode: agentConfig.systemPromptMode,
1351
+ inheritProjectContext: agentConfig.inheritProjectContext,
1352
+ inheritGlobalContext: agentConfig.inheritGlobalContext,
1353
+ inheritSkills: agentConfig.inheritSkills,
1354
+ skills: resolvedSkills.map((skill) => skill.name),
1355
+ tools: toolPlan.effectiveToolAllowlist,
1356
+ extensions: toolPlan.extensionArgs,
1357
+ mcpDirectTools: toolPlan.effectiveMcpTools,
1358
+ ...outputPath ? { outputPath } : {},
1359
+ outputMode,
1360
+ ...params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {},
1361
+ ...extensionBindings ? { extensionBindings } : {}
1362
+ });
1363
+ const resolvedAcceptance = resolveEffectiveAcceptance({
1364
+ explicit: params.acceptance,
1365
+ agentName: agent,
1366
+ acceptanceRole: agentConfig.acceptanceRole,
1367
+ task,
1368
+ mode: "single",
1369
+ async: true,
1370
+ agentContract: params.agentContract
1371
+ });
1372
+ const recoveryAgentConfig = params.recoveryAgentConfig ?? agentConfig;
1373
+ const recoveryDescriptor = {
1374
+ version: 1,
1375
+ ...lane ? { lane } : {},
1376
+ launchContractDigest,
1377
+ ...extensionBindings ? { extensionBindings } : {},
1378
+ runFanoutBudget,
1379
+ sourceRunId: id,
1380
+ ...params.agentContract ? { agentContract: params.agentContract } : {},
1381
+ agent,
1382
+ launchResolvedExtensions,
1383
+ ...sessionFile ? { sessionFile } : {},
1384
+ cwd: runnerCwd,
1385
+ ...model ? { model } : {},
1386
+ ...params.fast ?? recoveryAgentConfig.fast ? { fast: params.fast ?? recoveryAgentConfig.fast } : {},
1387
+ ...recoveryAgentConfig.modelProvider ? { modelProvider: recoveryAgentConfig.modelProvider } : {},
1388
+ ...params.modelOverrideFromParent ? { modelOverrideFromParent: true } : {},
1389
+ ...recoveryAgentConfig.fallbackModels ? { fallbackModels: [...recoveryAgentConfig.fallbackModels] } : {},
1390
+ ...effectiveThinking ? { thinking: resolveEffectiveThinking(model, effectiveThinking) } : {},
1391
+ ...thinkingCeiling ? { thinkingCeiling } : {},
1392
+ ...recoveryAgentConfig.tools ? { tools: [...recoveryAgentConfig.tools] } : {},
1393
+ ...recoveryAgentConfig.allowNestedSubagents !== undefined ? { allowNestedSubagents: recoveryAgentConfig.allowNestedSubagents } : {},
1394
+ ...recoveryAgentConfig.extensions ? { extensions: [...recoveryAgentConfig.extensions] } : {},
1395
+ ...recoveryAgentConfig.subagentOnlyExtensions ? { subagentOnlyExtensions: [...recoveryAgentConfig.subagentOnlyExtensions] } : {},
1396
+ ...recoveryAgentConfig.mcpDirectTools ? { mcpDirectTools: [...recoveryAgentConfig.mcpDirectTools] } : {},
1397
+ ...recoveryAgentConfig.mutationTools ? { mutationTools: [...recoveryAgentConfig.mutationTools] } : {},
1398
+ ...recoveryAgentConfig.systemPrompt ? { systemPrompt: recoveryAgentConfig.systemPrompt } : {},
1399
+ systemPromptMode: recoveryAgentConfig.systemPromptMode,
1400
+ inheritProjectContext: recoveryAgentConfig.inheritProjectContext,
1401
+ inheritGlobalContext: recoveryAgentConfig.inheritGlobalContext,
1402
+ inheritSkills: recoveryAgentConfig.inheritSkills,
1403
+ ...resolvedSkills.length ? { skills: resolvedSkills.map((skill) => skill.name) } : {},
1404
+ ...recoveryAgentConfig.skillPath ? { skillPath: [...recoveryAgentConfig.skillPath] } : {},
1405
+ ...recoveryAgentConfig.filePath ? { agentFilePath: recoveryAgentConfig.filePath } : {},
1406
+ ...recoveryAgentConfig.completionGuard !== undefined ? { completionGuard: recoveryAgentConfig.completionGuard } : {},
1407
+ ...recoveryAgentConfig.memory ? { memory: { ...recoveryAgentConfig.memory } } : {},
1408
+ ...outputPath ? { outputPath } : {},
1409
+ outputMode,
1410
+ ...params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {},
1411
+ ...params.acceptance !== undefined ? { acceptance: params.acceptance } : {},
1412
+ ...controlConfig ? { controlConfig } : {},
1413
+ ...params.context ? { context: params.context } : {},
1414
+ ...params.intercomBridge !== undefined ? { intercomBridge: params.intercomBridge } : {},
1415
+ ...deadlineAt !== undefined ? { absoluteDeadlineAt: deadlineAt } : {},
1416
+ ...resolvedToolBudget.budget ? { initialToolBudget: resolvedToolBudget.budget } : {},
1417
+ maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, recoveryAgentConfig.maxSubagentDepth),
1418
+ ...maxOutput ? { maxOutput } : {},
1419
+ share: shareEnabled,
1420
+ ...resolvedSessionDir ? { sessionDir: resolvedSessionDir } : {},
1421
+ ...artifactsDir ? { artifactsDir } : {},
1422
+ artifactConfig,
1423
+ ...capabilityCeiling ? { capabilityCeiling } : {}
1424
+ };
1425
+ if (!externalRunner) {
1426
+ try {
1427
+ writePrivateAtomicJson(path.join(asyncDir, "recovery-descriptor.json"), recoveryDescriptor);
1428
+ } catch (error) {
1429
+ return formatAsyncStartError("single", `Failed to persist async recovery descriptor for '${id}': ${error instanceof Error ? error.message : String(error)}`);
1430
+ }
1431
+ }
710
1432
  let spawnResult = {};
1433
+ const initialStatusAt = Date.now();
1434
+ const initialCompletionOwnerId = ctx.completionOwnerId ?? currentCompletionOwnerId();
711
1435
  try {
712
1436
  spawnResult = spawnRunner({
713
1437
  id,
714
1438
  steps: [
715
1439
  {
716
1440
  parentSessionId: ctx.parentSessionId ?? ctx.currentSessionId,
1441
+ permissionRules,
1442
+ ...capabilityCeiling ? { capabilityCeiling } : {},
717
1443
  agent,
718
- task: taskWithOutputInstruction,
1444
+ task: taskText,
1445
+ ...agentConfig.runner ? { runner: agentConfig.runner } : {},
1446
+ ...params.externalJobFollowUp ? { externalJobFollowUp: params.externalJobFollowUp } : {},
1447
+ ...params.context ? { context: params.context } : {},
719
1448
  cwd: runnerCwd,
1449
+ requestedCwd: params.requestedCwd ?? runnerCwd,
720
1450
  model,
1451
+ ...contextLimit !== undefined ? { contextLimit } : {},
1452
+ ...params.fast ?? agentConfig.fast ? { fast: params.fast ?? agentConfig.fast } : {},
721
1453
  thinking: resolveEffectiveThinking(model, effectiveThinking),
722
- modelCandidates: buildModelCandidates(primaryModel, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider, { scope: ctx.modelScope }).map((candidate) => applyThinkingSuffix(candidate, effectiveThinking, params.thinkingOverride !== undefined)),
1454
+ ...thinkingCeiling ? { thinkingCeiling } : {},
1455
+ modelCandidates,
1456
+ ...params.modelOverrideFromParent ? { skipPrimaryModelVerification: true } : {},
1457
+ ...availableModels && availableModels.length > 0 ? { modelVerificationRegistry: availableModels } : {},
723
1458
  tools: agentConfig.tools,
1459
+ allowNestedSubagents: agentConfig.allowNestedSubagents,
724
1460
  extensions: agentConfig.extensions,
725
1461
  subagentOnlyExtensions: agentConfig.subagentOnlyExtensions,
726
1462
  mcpDirectTools: agentConfig.mcpDirectTools,
1463
+ ...toolPlan.mcpConfig ? { mcpConfig: toolPlan.mcpConfig } : {},
1464
+ ...toolPlan.runtimeServerNames ? { runtimeServerNames: toolPlan.runtimeServerNames } : {},
1465
+ mutationTools: agentConfig.mutationTools,
727
1466
  completionGuard: agentConfig.completionGuard,
728
1467
  systemPrompt,
729
1468
  systemPromptMode: agentConfig.systemPromptMode,
730
1469
  inheritProjectContext: agentConfig.inheritProjectContext,
1470
+ inheritGlobalContext: agentConfig.inheritGlobalContext,
731
1471
  inheritSkills: agentConfig.inheritSkills,
732
1472
  skills: resolvedSkills.map((r) => r.name),
733
1473
  outputPath,
1474
+ ...params.outputClaimPath ? { outputClaimPath: params.outputClaimPath } : {},
734
1475
  outputMode,
735
- sessionFile,
1476
+ ...!externalRunner && sessionFile ? { sessionFile } : {},
736
1477
  maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, agentConfig.maxSubagentDepth),
737
- effectiveAcceptance: resolveEffectiveAcceptance({
738
- explicit: params.acceptance,
739
- agentName: agent,
740
- task,
741
- mode: "single",
742
- async: true
743
- }),
744
- ...resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}
1478
+ waitToolEnabled: params.waitToolEnabled,
1479
+ waitToolDefaultTimeoutMs: params.waitToolDefaultTimeoutMs,
1480
+ ...params.agentContract ? { agentContract: params.agentContract } : {},
1481
+ definitionDigest: agentDefinitionDigest(agentConfig),
1482
+ launchBindingTask: task,
1483
+ launchContractDigest,
1484
+ ...extensionBindings ? { extensionBindings } : {},
1485
+ launchResolvedExtensions,
1486
+ effectiveAcceptance: resolvedAcceptance,
1487
+ ...structuredOutput ? { structuredOutput } : {},
1488
+ ...params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {},
1489
+ ...resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {},
1490
+ ...params.worktree === true ? { worktree: true } : {},
1491
+ ...lane ? { lane } : {}
745
1492
  }
746
1493
  ],
747
- resultPath: inheritedNestedRoute ? nestedResultsPath(inheritedNestedRoute.rootRunId, id) : path.join(RESULTS_DIR, `${id}.json`),
1494
+ resultPath: params.parentWorkflowRunId !== undefined && (params.revivalLease !== undefined || params.workflowAwaitAsync === true) ? workflowAwaitedAsyncResultPath(asyncDir) : inheritedNestedRoute ? nestedResultsPath(inheritedNestedRoute.rootRunId, id) : resultFilePath(DIRS.results, id),
748
1495
  cwd: runnerCwd,
749
1496
  placeholder: "{previous}",
750
1497
  maxOutput,
751
1498
  artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
752
1499
  artifactConfig,
753
1500
  share: shareEnabled,
754
- sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
1501
+ sessionDir: resolvedSessionDir,
755
1502
  asyncDir,
756
1503
  sessionId: ctx.currentSessionId,
1504
+ completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
1505
+ ...capabilityCeiling ? { capabilityCeiling } : {},
757
1506
  piPackageRoot,
758
1507
  piArgv1: process.argv[1],
759
1508
  worktreeSetupHook,
760
1509
  worktreeSetupHookTimeoutMs,
761
1510
  worktreeBaseDir,
762
1511
  controlConfig,
763
- timeoutMs: params.timeoutMs,
1512
+ timeoutMs,
764
1513
  deadlineAt,
765
- turnBudget: params.turnBudget,
1514
+ toolTimeoutMs,
766
1515
  toolBudget: params.toolBudget,
1516
+ usageBudget: params.usageBudget,
767
1517
  controlIntercomTarget,
768
1518
  childIntercomTargets: childIntercomTarget ? [childIntercomTarget(agent, 0)] : undefined,
769
1519
  resultMode: "single",
1520
+ launchContractDigest,
1521
+ launchResolvedExtensions,
1522
+ runFanoutBudget,
1523
+ ...params.parentWorkflowRunId ? { parentWorkflowRunId: params.parentWorkflowRunId } : {},
1524
+ ...params.workflowKey ? { workflowKey: params.workflowKey } : {},
1525
+ ...lane ? { lane } : {},
1526
+ ...params.revivalLease ? { revivalLease: params.revivalLease } : {},
770
1527
  nestedRoute: nestedRoute ?? inheritedNestedRoute,
771
1528
  nestedSelf: inheritedNestedRoute && nestedAddress ? {
772
1529
  parentRunId: nestedAddress.parentRunId,
@@ -774,14 +1531,37 @@ ${memoryInjection}` : memoryInjection;
774
1531
  depth: nestedAddress.depth,
775
1532
  path: nestedAddress.path
776
1533
  } : undefined
777
- }, id, runnerCwd);
1534
+ }, id, runnerCwd, {
1535
+ lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
1536
+ runId: id,
1537
+ ...ctx.currentSessionId ? { sessionId: ctx.currentSessionId } : {},
1538
+ ...initialCompletionOwnerId ? { completionOwnerId: initialCompletionOwnerId } : {},
1539
+ mode: "single",
1540
+ state: "running",
1541
+ startedAt: initialStatusAt,
1542
+ lastUpdate: initialStatusAt,
1543
+ currentStep: 0,
1544
+ chainStepCount: 1,
1545
+ ...lane ? { lane } : {},
1546
+ steps: [{ agent, status: "pending", ...lane ? { lane } : {}, ...model ? { model } : {}, ...contextLimit !== undefined ? { contextLimit } : {} }]
1547
+ }, path.join(asyncDir, "status.json"), (proof) => emitProcessTerminalEvent(ctx, proof), params.requestedCwd ?? runnerCwd);
778
1548
  } catch (error) {
1549
+ params.activeAsyncCapacity?.rollback();
779
1550
  const message = error instanceof Error ? error.message : String(error);
780
1551
  return formatAsyncStartError("single", `Failed to start async run '${id}': ${message}`);
781
1552
  }
782
1553
  if (spawnResult.error) {
1554
+ if (!spawnResult.pid || !spawnResult.runnerProcessInstanceId || spawnResult.startupDidNotProceed && spawnResult.terminationObserved)
1555
+ params.activeAsyncCapacity?.rollback();
1556
+ else
1557
+ params.activeAsyncCapacity?.markStarted(spawnResult.runnerProcessInstanceId);
783
1558
  return formatAsyncStartError("single", `Failed to start async run '${id}': ${spawnResult.error}`);
784
1559
  }
1560
+ if (!spawnResult.pid || !spawnResult.runnerProcessInstanceId) {
1561
+ params.activeAsyncCapacity?.rollback();
1562
+ return formatAsyncStartError("single", `Failed to start async run '${id}': runner identity unavailable`);
1563
+ }
1564
+ params.activeAsyncCapacity?.markStarted(spawnResult.runnerProcessInstanceId);
785
1565
  if (spawnResult.pid) {
786
1566
  if (inheritedNestedRoute && nestedAddress) {
787
1567
  const now = Date.now();
@@ -808,33 +1588,41 @@ ${memoryInjection}` : memoryInjection;
808
1588
  agent,
809
1589
  agents: [agent],
810
1590
  chainStepCount: 1,
811
- ...params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {},
812
- ...initialTurnBudget ? { turnBudget: initialTurnBudget } : {},
1591
+ ...timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {},
813
1592
  startedAt: now,
814
- lastUpdate: now
1593
+ lastUpdate: now,
1594
+ ...capabilityCeiling ? { capabilityCeiling } : {}
815
1595
  }
816
1596
  });
817
1597
  } catch (error) {
818
1598
  console.error("Failed to emit nested async start event:", error);
819
1599
  }
820
1600
  }
821
- ctx.pi.events.emit(SUBAGENT_ASYNC_STARTED_EVENT, {
1601
+ ctx.dm.events.emit(SUBAGENT_ASYNC_STARTED_EVENT, {
822
1602
  lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
823
1603
  id,
824
1604
  pid: spawnResult.pid,
825
1605
  sessionId: ctx.currentSessionId,
1606
+ completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
826
1607
  mode: "single",
827
1608
  agent,
828
- task: task?.slice(0, 50),
1609
+ task: task?.trim() ? PROMPT_REDACTED : undefined,
1610
+ goal: (params.goal ?? task).trim() ? PROMPT_REDACTED : undefined,
829
1611
  cwd: runnerCwd,
830
1612
  asyncDir,
831
- ...params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {},
832
- ...initialTurnBudget ? { turnBudget: initialTurnBudget } : {},
1613
+ ...sessionRoot ? { sessionRoot } : {},
1614
+ launchContractDigest,
1615
+ launchResolvedExtensions,
1616
+ ...params.parentWorkflowRunId ? { parentWorkflowRunId: params.parentWorkflowRunId } : {},
1617
+ ...params.workflowKey ? { workflowKey: params.workflowKey } : {},
1618
+ ...timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {},
1619
+ ...initialUsageBudget ? { usageBudget: initialUsageBudget } : {},
1620
+ ...capabilityCeiling ? { capabilityCeiling } : {},
833
1621
  nestedRoute
834
1622
  });
835
1623
  }
836
1624
  return {
837
- content: [{ type: "text", text: formatAsyncStartedMessage(`Async: ${agent} [${id}]`) }],
838
- details: { mode: "single", runId: id, results: [], asyncId: id, asyncDir, ...params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}, ...params.turnBudget ? { turnBudget: params.turnBudget } : {}, ...params.toolBudget ? { toolBudget: params.toolBudget } : {} }
1625
+ content: [{ type: "text", text: formatAsyncStartedMessage(`Async: ${agent} [${id}]`, ctx.interactive === true) }],
1626
+ details: { mode: "single", runId: id, results: [], asyncId: id, asyncDir, launchContractDigest, launchResolvedExtensions, ...capabilityCeiling ? { capabilityCeiling } : {}, ...params.context ? { context: params.context } : {}, ...timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}, ...params.toolBudget ? { toolBudget: resolvedToolBudget.budget ?? params.toolBudget } : {}, ...initialUsageBudget ? { usageBudget: initialUsageBudget } : {} }
839
1627
  };
840
1628
  }