@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,39 +1,18 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { ASYNC_DIR, RESULTS_DIR } from "../../shared/types.js";
4
- import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.js";
5
- import { deliverInterruptRequest } from "./control-channel.js";
3
+ import { DIRS } from "../../shared/types.js";
4
+ import { normalizeExtensionBindings } from "../shared/extension-bindings.js";
5
+ import { normalizeWorkflowLaneMetadata } from "../shared/lane-metadata.js";
6
+ import { validateAcceptanceInput } from "../shared/acceptance.js";
7
+ import { validateToolBudgetConfig } from "../shared/tool-budget.js";
8
+ import { intersectSubagentCapabilityCeilings, parseSubagentCapabilityCeiling } from "../shared/capability-ceiling.js";
9
+ import { validateRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.js";
6
10
  import { reconcileAsyncRun } from "./stale-run-reconciler.js";
7
- export const ASYNC_RESUME_INTERRUPT_SIGNAL = process.platform === "win32" ? "SIGBREAK" : "SIGUSR2";
8
- export function interruptLiveAsyncResumeTarget(input) {
9
- const asyncId = input.target.runId;
10
- if (!input.target.asyncDir) {
11
- return { ok: false, message: `Async run ${asyncId} is live but does not have an async directory to interrupt.` };
12
- }
13
- const status = reconcileAsyncRun(input.target.asyncDir, { resultsDir: input.resultsDir, kill: input.kill, now: input.now }).status;
14
- if (!status || status.state !== "running" || typeof status.pid !== "number") {
15
- return { ok: false, message: `Async run ${asyncId} is live but no interrupt-capable runner pid was found.` };
16
- }
17
- try {
18
- deliverInterruptRequest({
19
- asyncDir: input.target.asyncDir,
20
- pid: status.pid,
21
- kill: input.kill,
22
- signal: ASYNC_RESUME_INTERRUPT_SIGNAL,
23
- now: input.now,
24
- source: "async-resume"
25
- });
26
- const tracked = input.state?.asyncJobs.get(asyncId);
27
- if (tracked) {
28
- tracked.activityState = undefined;
29
- tracked.updatedAt = input.now?.() ?? Date.now();
30
- }
31
- return { ok: true, asyncId };
32
- } catch (error) {
33
- const message = error instanceof Error ? error.message : String(error);
34
- return { ok: false, message: `Failed to interrupt async run ${asyncId}: ${message}` };
35
- }
36
- }
11
+ import { resultFilePath, resultPayloadPathForIndexedRun } from "./result-files.js";
12
+ import { canScanAsyncRunPrefix, MIN_SAFE_ASYNC_RUN_PREFIX_LENGTH } from "./run-id-query.js";
13
+ import { parallelHandoffPath, resolveRetainedWorktreeCwd } from "../shared/parallel-handoff.js";
14
+ import { intersectThinkingCeilings, parseThinkingLevel } from "../../shared/thinking-ceiling.js";
15
+ import { assertWorkflowGraphHostSteps } from "../shared/host-step-status.js";
37
16
  function getErrorMessage(error) {
38
17
  return error instanceof Error ? error.message : String(error);
39
18
  }
@@ -62,11 +41,16 @@ function validateResultFile(value, resultPath) {
62
41
  const child = ensureObject(entry, `${resultPath} results[${index}]`);
63
42
  const agent = validateOptionalString(child, "agent", resultPath, `results[${index}].agent`);
64
43
  const sessionFile = validateOptionalString(child, "sessionFile", resultPath, `results[${index}].sessionFile`);
44
+ const sessionName = validateOptionalString(child, "sessionName", resultPath, `results[${index}].sessionName`);
65
45
  const intercomTarget = validateOptionalString(child, "intercomTarget", resultPath, `results[${index}].intercomTarget`);
46
+ const model = validateOptionalString(child, "model", resultPath, `results[${index}].model`);
47
+ const thinking = validateOptionalString(child, "thinking", resultPath, `results[${index}].thinking`);
48
+ const launchContractDigest = validateOptionalString(child, "launchContractDigest", resultPath, `results[${index}].launchContractDigest`);
49
+ const capabilityCeiling = child.capabilityCeiling === undefined ? undefined : parseSubagentCapabilityCeiling(child.capabilityCeiling, `async result file '${resultPath}' results[${index}].capabilityCeiling`);
66
50
  const success = child.success;
67
51
  if (success !== undefined && typeof success !== "boolean")
68
52
  throw new Error(`Invalid async result file '${resultPath}': results[${index}].success must be a boolean.`);
69
- return { agent, sessionFile, intercomTarget, ...typeof success === "boolean" ? { success } : {} };
53
+ return { agent, sessionName, sessionFile, intercomTarget, model, thinking, launchContractDigest, ...capabilityCeiling ? { capabilityCeiling } : {}, ...typeof success === "boolean" ? { success } : {} };
70
54
  });
71
55
  }
72
56
  const success = data.success;
@@ -79,7 +63,12 @@ function validateResultFile(value, resultPath) {
79
63
  mode: validateOptionalString(data, "mode", resultPath),
80
64
  state: validateOptionalString(data, "state", resultPath),
81
65
  cwd: validateOptionalString(data, "cwd", resultPath),
66
+ sessionId: validateOptionalString(data, "sessionId", resultPath),
82
67
  sessionFile: validateOptionalString(data, "sessionFile", resultPath),
68
+ model: validateOptionalString(data, "model", resultPath),
69
+ thinking: validateOptionalString(data, "thinking", resultPath),
70
+ launchContractDigest: validateOptionalString(data, "launchContractDigest", resultPath),
71
+ ...data.capabilityCeiling === undefined ? {} : { capabilityCeiling: parseSubagentCapabilityCeiling(data.capabilityCeiling, `async result file '${resultPath}' capabilityCeiling`) },
83
72
  ...typeof success === "boolean" ? { success } : {},
84
73
  ...results ? { results } : {}
85
74
  };
@@ -128,7 +117,10 @@ function prefixedRunIds(dir, prefix, suffix = "") {
128
117
  return fs.readdirSync(dir).filter((entry) => entry.startsWith(prefix) && (!suffix || entry.endsWith(suffix))).map((entry) => suffix ? entry.slice(0, -suffix.length) : entry).sort();
129
118
  }
130
119
  function exactResultPath(resultsDir, runId) {
131
- const resultPath = path.join(resultsDir, `${runId}.json`);
120
+ const indexed = resultPayloadPathForIndexedRun(resultsDir, runId);
121
+ if (indexed)
122
+ return indexed;
123
+ const resultPath = resultFilePath(resultsDir, runId);
132
124
  assertInsideRoot(resultsDir, resultPath, "Async result file");
133
125
  return fs.existsSync(resultPath) ? resultPath : null;
134
126
  }
@@ -136,12 +128,11 @@ export function findAsyncRunPrefixMatches(prefix, asyncDirRoot, resultsDir) {
136
128
  const requestedId = assertRunId(prefix, "id");
137
129
  if (!requestedId)
138
130
  return [];
131
+ if (!canScanAsyncRunPrefix(requestedId))
132
+ return [];
139
133
  const asyncRoot = path.resolve(asyncDirRoot);
140
134
  const resultRoot = path.resolve(resultsDir);
141
- const matchingIds = [...new Set([
142
- ...prefixedRunIds(asyncRoot, requestedId),
143
- ...prefixedRunIds(resultRoot, requestedId, ".json")
144
- ])].sort();
135
+ const matchingIds = prefixedRunIds(asyncRoot, requestedId).sort();
145
136
  return matchingIds.map((id) => {
146
137
  const asyncDir = path.join(asyncRoot, id);
147
138
  assertInsideRoot(asyncRoot, asyncDir, "Async run directory");
@@ -180,6 +171,11 @@ export function resolveAsyncRunLocation(params, asyncDirRoot, resultsDir) {
180
171
  resolvedId: requestedId
181
172
  };
182
173
  }
174
+ if (requestedId.length < MIN_SAFE_ASYNC_RUN_PREFIX_LENGTH) {
175
+ throw new Error(`Async run id prefix '${requestedId}' is too short. Provide at least ${MIN_SAFE_ASYNC_RUN_PREFIX_LENGTH} characters.`);
176
+ }
177
+ if (!canScanAsyncRunPrefix(requestedId))
178
+ return { asyncDir: null, resultPath: null, resolvedId: requestedId };
183
179
  const matching = findAsyncRunPrefixMatches(requestedId, asyncRoot, resultRoot);
184
180
  if (matching.length === 0)
185
181
  return { asyncDir: null, resultPath: null, resolvedId: requestedId };
@@ -189,7 +185,7 @@ export function resolveAsyncRunLocation(params, asyncDirRoot, resultsDir) {
189
185
  return matching[0].location;
190
186
  }
191
187
  function resultState(result) {
192
- if (result.state === "complete" || result.state === "failed" || result.state === "paused" || result.state === "running" || result.state === "queued") {
188
+ if (result.state === "complete" || result.state === "failed" || result.state === "partial" || result.state === "paused" || result.state === "stopped" || result.state === "running" || result.state === "queued") {
193
189
  return result.state;
194
190
  }
195
191
  return result.success ? "complete" : "failed";
@@ -199,25 +195,269 @@ function validateStatusForResume(status, source) {
199
195
  return;
200
196
  if (typeof status.runId !== "string")
201
197
  throw new Error(`Invalid async status '${source}': runId must be a string.`);
198
+ assertWorkflowGraphHostSteps(status.workflowGraph, source, status.runId);
202
199
  if (status.sessionId !== undefined && typeof status.sessionId !== "string")
203
200
  throw new Error(`Invalid async status '${source}': sessionId must be a string.`);
204
201
  if (status.cwd !== undefined && typeof status.cwd !== "string")
205
202
  throw new Error(`Invalid async status '${source}': cwd must be a string.`);
206
203
  if (status.sessionFile !== undefined && typeof status.sessionFile !== "string")
207
204
  throw new Error(`Invalid async status '${source}': sessionFile must be a string.`);
205
+ if (status.capabilityCeiling !== undefined)
206
+ status.capabilityCeiling = parseSubagentCapabilityCeiling(status.capabilityCeiling, `async status '${source}' capabilityCeiling`);
208
207
  if (status.steps !== undefined) {
209
208
  if (!Array.isArray(status.steps))
210
209
  throw new Error(`Invalid async status '${source}': steps must be an array.`);
211
210
  status.steps.forEach((step, index) => {
212
211
  if (!step || typeof step !== "object" || Array.isArray(step))
213
212
  throw new Error(`Invalid async status '${source}': steps[${index}] must be an object.`);
214
- if (typeof step.agent !== "string")
213
+ const stepRecord = step;
214
+ if (typeof stepRecord.agent !== "string")
215
215
  throw new Error(`Invalid async status '${source}': steps[${index}].agent must be a string.`);
216
- if (step.sessionFile !== undefined && typeof step.sessionFile !== "string")
216
+ if (stepRecord.sessionFile !== undefined && typeof stepRecord.sessionFile !== "string")
217
217
  throw new Error(`Invalid async status '${source}': steps[${index}].sessionFile must be a string.`);
218
+ if (stepRecord.sessionName !== undefined && typeof stepRecord.sessionName !== "string")
219
+ throw new Error(`Invalid async status '${source}': steps[${index}].sessionName must be a string.`);
220
+ if (stepRecord.model !== undefined && typeof stepRecord.model !== "string")
221
+ throw new Error(`Invalid async status '${source}': steps[${index}].model must be a string.`);
222
+ if (stepRecord.thinking !== undefined && typeof stepRecord.thinking !== "string")
223
+ throw new Error(`Invalid async status '${source}': steps[${index}].thinking must be a string.`);
224
+ if (stepRecord.thinkingCeiling !== undefined)
225
+ stepRecord.thinkingCeiling = parseThinkingLevel(stepRecord.thinkingCeiling, `async status '${source}' steps[${index}].thinkingCeiling`);
226
+ if (stepRecord.launchContractDigest !== undefined && typeof stepRecord.launchContractDigest !== "string")
227
+ throw new Error(`Invalid async status '${source}': steps[${index}].launchContractDigest must be a string.`);
228
+ if (stepRecord.capabilityCeiling !== undefined)
229
+ stepRecord.capabilityCeiling = parseSubagentCapabilityCeiling(stepRecord.capabilityCeiling, `async status '${source}' steps[${index}].capabilityCeiling`);
218
230
  });
219
231
  }
220
232
  }
233
+ function normalizeRecoveryAcceptance(value, descriptorPath) {
234
+ const errors = validateAcceptanceInput(value, "recoveryDescriptor.acceptance");
235
+ if (errors.length)
236
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${errors.join(" ")}`);
237
+ return value;
238
+ }
239
+ export function asyncReviveRequiresRecoveryDescriptor(target) {
240
+ if (target.recoveryDescriptor)
241
+ return false;
242
+ return !(target.mode === "workflow" && Boolean(target.sessionFile));
243
+ }
244
+ function resumeTargetMode(status, result) {
245
+ if (status?.mode)
246
+ return status.mode;
247
+ if (result?.mode === "single" || result?.mode === "parallel" || result?.mode === "chain" || result?.mode === "workflow")
248
+ return result.mode;
249
+ return;
250
+ }
251
+ export function readAsyncRecoveryDescriptor(asyncDir) {
252
+ if (!asyncDir)
253
+ return;
254
+ const descriptorPath = path.join(asyncDir, "recovery-descriptor.json");
255
+ if (!fs.existsSync(descriptorPath))
256
+ return;
257
+ let value;
258
+ try {
259
+ value = JSON.parse(fs.readFileSync(descriptorPath, "utf-8"));
260
+ } catch (error) {
261
+ throw new Error(`Failed to parse async recovery descriptor '${descriptorPath}': ${getErrorMessage(error)}`, { cause: error instanceof Error ? error : undefined });
262
+ }
263
+ if (!value || typeof value !== "object" || Array.isArray(value))
264
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': expected an object.`);
265
+ const parsed = value;
266
+ const allowedFields = new Set([
267
+ "version",
268
+ "launchContractDigest",
269
+ "sourceRunId",
270
+ "agentContract",
271
+ "agent",
272
+ "sessionFile",
273
+ "cwd",
274
+ "model",
275
+ "modelProvider",
276
+ "modelOverrideFromParent",
277
+ "fallbackModels",
278
+ "thinking",
279
+ "thinkingCeiling",
280
+ "tools",
281
+ "allowNestedSubagents",
282
+ "extensions",
283
+ "subagentOnlyExtensions",
284
+ "mcpDirectTools",
285
+ "mutationTools",
286
+ "systemPrompt",
287
+ "systemPromptMode",
288
+ "inheritProjectContext",
289
+ "inheritGlobalContext",
290
+ "inheritSkills",
291
+ "skills",
292
+ "skillPath",
293
+ "agentFilePath",
294
+ "completionGuard",
295
+ "memory",
296
+ "outputPath",
297
+ "outputMode",
298
+ "structuredOutputSchema",
299
+ "acceptance",
300
+ "sessionDir",
301
+ "artifactConfig",
302
+ "artifactsDir",
303
+ "maxOutput",
304
+ "controlConfig",
305
+ "context",
306
+ "intercomBridge",
307
+ "absoluteDeadlineAt",
308
+ "initialTurnBudget",
309
+ "initialToolBudget",
310
+ "maxSubagentDepth",
311
+ "share",
312
+ "capabilityCeiling",
313
+ "launchResolvedExtensions",
314
+ "runFanoutBudget",
315
+ "lane",
316
+ "extensionBindings"
317
+ ]);
318
+ for (const field of Object.keys(parsed)) {
319
+ if (!allowedFields.has(field))
320
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': unknown field '${field}'.`);
321
+ }
322
+ const requiredStrings = ["sourceRunId", "agent", "cwd", "systemPromptMode", "outputMode"];
323
+ for (const field of requiredStrings) {
324
+ if (typeof parsed[field] !== "string" || !parsed[field].trim())
325
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${field} must be a non-empty string.`);
326
+ }
327
+ if (parsed.version !== 1)
328
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': version must be 1.`);
329
+ try {
330
+ parsed.runFanoutBudget = validateRunFanoutBudgetDescriptor(parsed.runFanoutBudget);
331
+ } catch (error) {
332
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${error instanceof Error ? error.message : String(error)}`);
333
+ }
334
+ if (parsed.capabilityCeiling !== undefined)
335
+ parsed.capabilityCeiling = parseSubagentCapabilityCeiling(parsed.capabilityCeiling, `async recovery descriptor '${descriptorPath}' capabilityCeiling`);
336
+ if (parsed.thinkingCeiling !== undefined)
337
+ parsed.thinkingCeiling = parseThinkingLevel(parsed.thinkingCeiling, `async recovery descriptor '${descriptorPath}' thinkingCeiling`);
338
+ if (parsed.extensionBindings !== undefined)
339
+ parsed.extensionBindings = normalizeExtensionBindings(parsed.extensionBindings).value;
340
+ if (parsed.lane !== undefined)
341
+ parsed.lane = normalizeWorkflowLaneMetadata(parsed.lane, `Invalid async recovery descriptor '${descriptorPath}': lane`);
342
+ if (parsed.agentContract !== undefined) {
343
+ if (!parsed.agentContract || typeof parsed.agentContract !== "object" || Array.isArray(parsed.agentContract))
344
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': agentContract must be an object.`);
345
+ const contract = parsed.agentContract;
346
+ if (contract.version !== 1 || Object.keys(contract).some((key) => key !== "version"))
347
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': agentContract must be { version: 1 }.`);
348
+ }
349
+ if (parsed.systemPromptMode !== "append" && parsed.systemPromptMode !== "replace")
350
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': systemPromptMode is invalid.`);
351
+ if (parsed.outputMode !== "inline" && parsed.outputMode !== "file-only")
352
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': outputMode is invalid.`);
353
+ if (parsed.context !== undefined && parsed.context !== "fresh" && parsed.context !== "fork")
354
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': context is invalid.`);
355
+ if (parsed.modelOverrideFromParent !== undefined && typeof parsed.modelOverrideFromParent !== "boolean")
356
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': modelOverrideFromParent must be a boolean.`);
357
+ for (const field of ["inheritProjectContext", "inheritSkills", "share"]) {
358
+ if (typeof parsed[field] !== "boolean")
359
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${field} must be a boolean.`);
360
+ }
361
+ if (parsed.inheritGlobalContext === undefined)
362
+ parsed.inheritGlobalContext = parsed.inheritProjectContext;
363
+ else if (typeof parsed.inheritGlobalContext !== "boolean")
364
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': inheritGlobalContext must be a boolean.`);
365
+ if (parsed.allowNestedSubagents !== undefined && typeof parsed.allowNestedSubagents !== "boolean")
366
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': allowNestedSubagents must be a boolean.`);
367
+ if (!Number.isInteger(parsed.maxSubagentDepth) || parsed.maxSubagentDepth < 0)
368
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': maxSubagentDepth must be a non-negative integer.`);
369
+ for (const field of ["fallbackModels", "tools", "extensions", "subagentOnlyExtensions", "mcpDirectTools", "mutationTools", "skills", "skillPath"]) {
370
+ const item = parsed[field];
371
+ if (item !== undefined && (!Array.isArray(item) || item.some((entry) => typeof entry !== "string" || !entry.trim())))
372
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${field} must contain non-empty strings.`);
373
+ }
374
+ if (parsed.systemPrompt !== undefined && typeof parsed.systemPrompt !== "string")
375
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': systemPrompt must be a string.`);
376
+ for (const field of ["launchContractDigest", "sessionFile", "model", "modelProvider", "thinking", "agentFilePath", "outputPath", "sessionDir", "artifactsDir"]) {
377
+ if (parsed[field] !== undefined && (typeof parsed[field] !== "string" || !parsed[field].trim()))
378
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${field} must be a non-empty string.`);
379
+ }
380
+ if (parsed.completionGuard !== undefined && typeof parsed.completionGuard !== "boolean")
381
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': completionGuard must be a boolean.`);
382
+ if (parsed.structuredOutputSchema !== undefined && (!parsed.structuredOutputSchema || typeof parsed.structuredOutputSchema !== "object" || Array.isArray(parsed.structuredOutputSchema)))
383
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': structuredOutputSchema must be an object.`);
384
+ if (parsed.memory !== undefined) {
385
+ if (!parsed.memory || typeof parsed.memory !== "object" || Array.isArray(parsed.memory))
386
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': memory must be an object.`);
387
+ const memory = parsed.memory;
388
+ if (memory.scope !== "project" && memory.scope !== "user" || typeof memory.path !== "string" || !memory.path.trim())
389
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': memory is invalid.`);
390
+ }
391
+ if (parsed.absoluteDeadlineAt !== undefined && (!Number.isFinite(parsed.absoluteDeadlineAt) || parsed.absoluteDeadlineAt <= 0))
392
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': absoluteDeadlineAt must be a positive timestamp.`);
393
+ if (parsed.initialTurnBudget !== undefined) {
394
+ delete parsed.initialTurnBudget;
395
+ }
396
+ if (parsed.initialToolBudget !== undefined) {
397
+ const result = validateToolBudgetConfig(parsed.initialToolBudget, "recoveryDescriptor.initialToolBudget");
398
+ if (result.error)
399
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${result.error}`);
400
+ }
401
+ if (parsed.maxOutput !== undefined) {
402
+ if (!parsed.maxOutput || typeof parsed.maxOutput !== "object" || Array.isArray(parsed.maxOutput))
403
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': maxOutput must be an object.`);
404
+ for (const field of ["bytes", "lines"]) {
405
+ const item = parsed.maxOutput[field];
406
+ if (item !== undefined && (!Number.isInteger(item) || item < 1))
407
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': maxOutput.${field} must be a positive integer.`);
408
+ }
409
+ }
410
+ if (parsed.artifactConfig !== undefined) {
411
+ if (!parsed.artifactConfig || typeof parsed.artifactConfig !== "object" || Array.isArray(parsed.artifactConfig))
412
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': artifactConfig must be an object.`);
413
+ const artifact = parsed.artifactConfig;
414
+ for (const field of ["enabled", "includeInput", "includeOutput", "includeJsonl", "includeMetadata"]) {
415
+ if (typeof artifact[field] !== "boolean")
416
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': artifactConfig.${field} must be a boolean.`);
417
+ }
418
+ if (artifact.includeTranscript !== undefined && typeof artifact.includeTranscript !== "boolean")
419
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': artifactConfig.includeTranscript must be a boolean.`);
420
+ if (!Number.isInteger(artifact.cleanupDays) || artifact.cleanupDays < 0)
421
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': artifactConfig.cleanupDays must be a non-negative integer.`);
422
+ }
423
+ if (parsed.intercomBridge !== undefined) {
424
+ if (!parsed.intercomBridge || typeof parsed.intercomBridge !== "object" || Array.isArray(parsed.intercomBridge))
425
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': intercomBridge must be an object.`);
426
+ const bridge = parsed.intercomBridge;
427
+ for (const field of Object.keys(bridge)) {
428
+ if (field !== "mode" && field !== "instructionFile" && field !== "resultDelivery")
429
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': intercomBridge.${field} is not supported.`);
430
+ }
431
+ if (bridge.mode !== undefined && bridge.mode !== "off" && bridge.mode !== "fork-only" && bridge.mode !== "always")
432
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': intercomBridge.mode is invalid.`);
433
+ if (bridge.instructionFile !== undefined && typeof bridge.instructionFile !== "string")
434
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': intercomBridge.instructionFile must be a string.`);
435
+ if (bridge.resultDelivery !== undefined && typeof bridge.resultDelivery !== "boolean")
436
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': intercomBridge.resultDelivery must be a boolean.`);
437
+ }
438
+ if (parsed.controlConfig !== undefined) {
439
+ if (!parsed.controlConfig || typeof parsed.controlConfig !== "object" || Array.isArray(parsed.controlConfig))
440
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig must be an object.`);
441
+ const control = parsed.controlConfig;
442
+ if (typeof control.enabled !== "boolean")
443
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig.enabled must be a boolean.`);
444
+ for (const field of ["needsAttentionAfterMs", "activeNoticeAfterMs", "failedToolAttemptsBeforeAttention"]) {
445
+ if (!Number.isInteger(control[field]) || control[field] < 1)
446
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig.${field} must be a positive integer.`);
447
+ }
448
+ for (const field of ["activeNoticeAfterTurns", "activeNoticeAfterTokens"]) {
449
+ if (control[field] !== undefined && (!Number.isInteger(control[field]) || control[field] < 1))
450
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig.${field} must be a positive integer.`);
451
+ }
452
+ if (!Array.isArray(control.notifyOn) || control.notifyOn.some((item) => item !== "active_long_running" && item !== "needs_attention"))
453
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig.notifyOn is invalid.`);
454
+ if (!Array.isArray(control.notifyChannels) || control.notifyChannels.some((item) => item !== "event" && item !== "async" && item !== "intercom"))
455
+ throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig.notifyChannels is invalid.`);
456
+ }
457
+ if (parsed.acceptance !== undefined)
458
+ parsed.acceptance = normalizeRecoveryAcceptance(parsed.acceptance, descriptorPath);
459
+ return parsed;
460
+ }
221
461
  function validateResumeSessionFile(runId, sessionFile) {
222
462
  if (path.extname(sessionFile) !== ".jsonl")
223
463
  throw new Error(`Async run '${runId}' session file must be a .jsonl file: ${sessionFile}`);
@@ -226,9 +466,21 @@ function validateResumeSessionFile(runId, sessionFile) {
226
466
  throw new Error(`Async run '${runId}' session file does not exist: ${sessionFile}`);
227
467
  return resolved;
228
468
  }
469
+ function validateResumeCwd(runId, cwd) {
470
+ if (!cwd)
471
+ return;
472
+ const resolved = path.resolve(cwd);
473
+ try {
474
+ if (!fs.statSync(resolved).isDirectory())
475
+ throw new Error("path is not a directory");
476
+ } catch (error) {
477
+ throw new Error(`Async run '${runId}' required cwd does not exist: ${cwd}`, { cause: error instanceof Error ? error : undefined });
478
+ }
479
+ return resolved;
480
+ }
229
481
  export function resolveAsyncResumeTarget(params, deps = {}, options = {}) {
230
- const asyncDirRoot = deps.asyncDirRoot ?? ASYNC_DIR;
231
- const resultsDir = deps.resultsDir ?? RESULTS_DIR;
482
+ const asyncDirRoot = deps.asyncDirRoot ?? DIRS.async;
483
+ const resultsDir = deps.resultsDir ?? DIRS.results;
232
484
  const requireSessionFile = options.requireSessionFile ?? true;
233
485
  const location = resolveAsyncRunLocation(params, asyncDirRoot, resultsDir);
234
486
  if (!location.asyncDir && !location.resultPath) {
@@ -237,11 +489,20 @@ export function resolveAsyncResumeTarget(params, deps = {}, options = {}) {
237
489
  const reconciliation = location.asyncDir ? reconcileAsyncRun(location.asyncDir, { resultsDir, kill: deps.kill, now: deps.now }) : undefined;
238
490
  const status = reconciliation?.status ?? null;
239
491
  validateStatusForResume(status, location.asyncDir ? path.join(location.asyncDir, "status.json") : "status.json");
492
+ const recoveryDescriptor = readAsyncRecoveryDescriptor(location.asyncDir ?? undefined);
240
493
  const result = location.resultPath ? readResultFile(location.resultPath) : undefined;
241
494
  const runId = status?.runId ?? result?.runId ?? result?.id ?? location.resolvedId ?? (location.asyncDir ? path.basename(location.asyncDir) : "unknown");
495
+ const mode = resumeTargetMode(status, result);
496
+ if (options.sessionId && (status && status.sessionId !== options.sessionId || result && result.sessionId !== options.sessionId)) {
497
+ throw new Error(`Async run '${runId}' was not found in the active session.`);
498
+ }
499
+ if (recoveryDescriptor && recoveryDescriptor.sourceRunId !== runId)
500
+ throw new Error(`Async run '${runId}' has a recovery descriptor for a different source run.`);
242
501
  const state = status?.state ?? (result ? resultState(result) : undefined);
243
502
  if (!state)
244
503
  throw new Error(`Status file not found for async run '${runId}'.`);
504
+ if (state === "stopped")
505
+ throw new Error(`Async run '${runId}' was stopped and cannot be resumed. Start a new run instead.`);
245
506
  const statusSteps = status?.steps ?? [];
246
507
  const resultSteps = result?.results ?? [];
247
508
  const stepCount = statusSteps.length || resultSteps.length || (result?.agent ? 1 : 0);
@@ -255,16 +516,26 @@ export function resolveAsyncResumeTarget(params, deps = {}, options = {}) {
255
516
  throw new Error(`Async run '${runId}' has ${stepCount} children. Index ${requestedIndex} is out of range.`);
256
517
  const selectedStep = statusSteps[requestedIndex];
257
518
  if (selectedStep?.status === "running") {
519
+ const capabilityCeiling = intersectSubagentCapabilityCeilings(status?.capabilityCeiling, selectedStep.capabilityCeiling);
258
520
  return {
259
521
  kind: "live",
260
522
  runId,
261
523
  asyncDir: location.asyncDir ?? undefined,
262
524
  state,
525
+ ...mode ? { mode } : {},
263
526
  agent: selectedStep.agent,
527
+ ...selectedStep.sessionName ?? result?.results?.[requestedIndex]?.sessionName ? { sessionName: selectedStep.sessionName ?? result?.results?.[requestedIndex]?.sessionName } : {},
264
528
  index: requestedIndex,
265
- intercomTarget: resolveSubagentIntercomTarget(runId, selectedStep.agent, requestedIndex),
266
529
  cwd: status?.cwd ?? result?.cwd,
267
- sessionFile: selectedStep.sessionFile ?? status?.sessionFile ?? result?.sessionFile
530
+ sessionFile: selectedStep.sessionFile ?? status?.sessionFile ?? result?.sessionFile,
531
+ model: selectedStep.model,
532
+ thinking: selectedStep.thinking,
533
+ launchContractDigest: selectedStep.launchContractDigest ?? result?.results?.[requestedIndex]?.launchContractDigest ?? result?.launchContractDigest ?? recoveryDescriptor?.launchContractDigest,
534
+ ...selectedStep.runner ? { runner: selectedStep.runner } : {},
535
+ ...selectedStep.externalJob ? { externalJob: selectedStep.externalJob } : {},
536
+ ...capabilityCeiling ? { capabilityCeiling } : {},
537
+ ...selectedStep.thinkingCeiling ? { thinkingCeiling: selectedStep.thinkingCeiling } : {},
538
+ ...recoveryDescriptor ? { recoveryDescriptor } : {}
268
539
  };
269
540
  }
270
541
  if (selectedStep?.status === "pending")
@@ -277,16 +548,26 @@ export function resolveAsyncResumeTarget(params, deps = {}, options = {}) {
277
548
  if (!selected) {
278
549
  throw new Error(`Async run '${runId}' has ${running.length} running children. Provide index to choose one.`);
279
550
  }
551
+ const capabilityCeiling = intersectSubagentCapabilityCeilings(status?.capabilityCeiling, selected.step.capabilityCeiling);
280
552
  return {
281
553
  kind: "live",
282
554
  runId,
283
555
  asyncDir: location.asyncDir ?? undefined,
284
556
  state,
557
+ ...mode ? { mode } : {},
285
558
  agent: selected.step.agent,
559
+ ...selected.step.sessionName ?? result?.results?.[selected.index]?.sessionName ? { sessionName: selected.step.sessionName ?? result?.results?.[selected.index]?.sessionName } : {},
286
560
  index: selected.index,
287
- intercomTarget: resolveSubagentIntercomTarget(runId, selected.step.agent, selected.index),
288
561
  cwd: status?.cwd ?? result?.cwd,
289
- sessionFile: selected.step.sessionFile ?? status?.sessionFile ?? result?.sessionFile
562
+ sessionFile: selected.step.sessionFile ?? status?.sessionFile ?? result?.sessionFile,
563
+ model: selected.step.model,
564
+ thinking: selected.step.thinking,
565
+ launchContractDigest: selected.step.launchContractDigest ?? result?.results?.[selected.index]?.launchContractDigest ?? result?.launchContractDigest ?? recoveryDescriptor?.launchContractDigest,
566
+ ...selected.step.runner ? { runner: selected.step.runner } : {},
567
+ ...selected.step.externalJob ? { externalJob: selected.step.externalJob } : {},
568
+ ...capabilityCeiling ? { capabilityCeiling } : {},
569
+ ...selected.step.thinkingCeiling ? { thinkingCeiling: selected.step.thinkingCeiling } : {},
570
+ ...recoveryDescriptor ? { recoveryDescriptor } : {}
290
571
  };
291
572
  }
292
573
  }
@@ -301,20 +582,66 @@ export function resolveAsyncResumeTarget(params, deps = {}, options = {}) {
301
582
  const agent = statusSteps[index]?.agent ?? resultSteps[index]?.agent ?? result?.agent;
302
583
  if (!agent)
303
584
  throw new Error(`Could not determine child agent for async run '${runId}'.`);
585
+ if (recoveryDescriptor && recoveryDescriptor.agent !== agent)
586
+ throw new Error(`Async run '${runId}' has a recovery descriptor for '${recoveryDescriptor.agent}', not '${agent}'.`);
304
587
  const sessionFile = statusSteps[index]?.sessionFile ?? resultSteps[index]?.sessionFile ?? (stepCount === 1 ? status?.sessionFile ?? result?.sessionFile : undefined);
305
588
  if (!sessionFile && requireSessionFile)
306
589
  throw new Error(`Async run '${runId}' child ${index} does not have a persisted session file to resume from.`);
307
590
  const resolvedSessionFile = sessionFile ? validateResumeSessionFile(runId, sessionFile) : undefined;
591
+ const stepModel = statusSteps[index]?.model ?? resultSteps[index]?.model ?? (stepCount === 1 ? result?.model : undefined);
592
+ const stepThinking = statusSteps[index]?.thinking ?? resultSteps[index]?.thinking ?? (stepCount === 1 ? result?.thinking : undefined);
593
+ const thinkingCeiling = statusSteps[index]?.thinkingCeiling ?? (stepCount === 1 ? recoveryDescriptor?.thinkingCeiling : undefined);
594
+ const capabilityCeiling = intersectSubagentCapabilityCeilings(status?.capabilityCeiling, statusSteps[index]?.capabilityCeiling, result?.capabilityCeiling, resultSteps[index]?.capabilityCeiling);
595
+ const managedWorktreeCwd = location.asyncDir ? resolveRetainedWorktreeCwd(parallelHandoffPath(location.asyncDir), runId, index) : undefined;
596
+ const resumeCwd = validateResumeCwd(runId, managedWorktreeCwd ?? status?.cwd ?? result?.cwd ?? recoveryDescriptor?.cwd);
308
597
  return {
309
598
  kind: "revive",
310
599
  runId,
311
600
  asyncDir: location.asyncDir ?? undefined,
312
601
  state,
602
+ ...mode ? { mode } : {},
313
603
  agent,
604
+ ...statusSteps[index]?.sessionName ?? resultSteps[index]?.sessionName ? { sessionName: statusSteps[index]?.sessionName ?? resultSteps[index]?.sessionName } : {},
314
605
  index,
315
- intercomTarget: resolveSubagentIntercomTarget(runId, agent, index),
316
- cwd: status?.cwd ?? result?.cwd,
317
- ...resolvedSessionFile ? { sessionFile: resolvedSessionFile } : {}
606
+ ...resumeCwd ? { cwd: resumeCwd } : {},
607
+ ...resolvedSessionFile ? { sessionFile: resolvedSessionFile } : {},
608
+ ...stepModel ? { model: stepModel } : {},
609
+ ...stepThinking ? { thinking: stepThinking } : {},
610
+ launchContractDigest: statusSteps[index]?.launchContractDigest ?? resultSteps[index]?.launchContractDigest ?? result?.launchContractDigest ?? recoveryDescriptor?.launchContractDigest,
611
+ ...statusSteps[index]?.runner ? { runner: statusSteps[index].runner } : {},
612
+ ...statusSteps[index]?.externalJob ? { externalJob: statusSteps[index].externalJob } : {},
613
+ ...capabilityCeiling ? { capabilityCeiling } : {},
614
+ ...thinkingCeiling ? { thinkingCeiling } : {},
615
+ ...recoveryDescriptor ? { recoveryDescriptor } : {}
616
+ };
617
+ }
618
+ export function applySteeringRecoveryAgentConfig(agentConfig, descriptor) {
619
+ return {
620
+ ...agentConfig,
621
+ model: descriptor.model,
622
+ modelProvider: descriptor.modelProvider,
623
+ fallbackModels: descriptor.fallbackModels ? [...descriptor.fallbackModels] : undefined,
624
+ thinking: descriptor.thinking,
625
+ maxThinking: intersectThinkingCeilings(descriptor.thinkingCeiling, agentConfig.maxThinking),
626
+ tools: descriptor.tools ? [...descriptor.tools] : undefined,
627
+ allowNestedSubagents: descriptor.allowNestedSubagents,
628
+ extensions: descriptor.extensions ? [...descriptor.extensions] : undefined,
629
+ subagentOnlyExtensions: descriptor.subagentOnlyExtensions ? [...descriptor.subagentOnlyExtensions] : undefined,
630
+ mcpDirectTools: descriptor.mcpDirectTools ? [...descriptor.mcpDirectTools] : undefined,
631
+ mutationTools: descriptor.mutationTools ? [...descriptor.mutationTools] : undefined,
632
+ systemPrompt: descriptor.systemPrompt ?? agentConfig.systemPrompt,
633
+ systemPromptMode: descriptor.systemPromptMode,
634
+ inheritProjectContext: descriptor.inheritProjectContext,
635
+ inheritGlobalContext: descriptor.inheritGlobalContext,
636
+ inheritSkills: descriptor.inheritSkills,
637
+ skills: descriptor.skills ? [...descriptor.skills] : undefined,
638
+ skillPath: descriptor.skillPath ? [...descriptor.skillPath] : undefined,
639
+ filePath: descriptor.agentFilePath,
640
+ completionGuard: descriptor.completionGuard,
641
+ memory: descriptor.memory ? { ...descriptor.memory } : undefined,
642
+ output: descriptor.outputPath,
643
+ toolBudget: descriptor.initialToolBudget,
644
+ maxSubagentDepth: descriptor.maxSubagentDepth
318
645
  };
319
646
  }
320
647
  export function buildRevivedAsyncTask(target, message) {