@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
@@ -0,0 +1,529 @@
1
+ import * as fs from "node:fs";
2
+ import {
3
+ listBackgroundWorkWakeChannels,
4
+ snapshotBackgroundWork
5
+ } from "../../api/background-work.js";
6
+ import { formatAsyncRunList, listAsyncRuns } from "./async-status.js";
7
+ import {
8
+ DIRS,
9
+ INTERCOM_DETACH_REQUEST_EVENT,
10
+ SUBAGENT_ASYNC_COMPLETE_EVENT,
11
+ SUBAGENT_FOREGROUND_COMPLETE_EVENT,
12
+ SUBAGENT_CONTROL_EVENT,
13
+ SUBAGENT_CONTROL_INTERCOM_EVENT,
14
+ SUBAGENT_RESULT_INTERCOM_EVENT
15
+ } from "../../shared/types.js";
16
+ import { formatDuration, shortenPath } from "../../shared/formatters.js";
17
+ import { toAgentToolUsage } from "../../shared/utils.js";
18
+ import { collectWaitCompletions } from "./wait-completions.js";
19
+ import { formatResumeFirstFailedRunsNote } from "./resume-guidance.js";
20
+ import { formatTimeoutRecoveryLines } from "../shared/mutation-evidence.js";
21
+ export { WAIT_TOOL_DEFAULT_TIMEOUT_MS_ENV, WAIT_TOOL_ENABLED_ENV, resolveWaitToolConfig } from "./wait-config.js";
22
+ const ACTIVE_STATES = ["queued", "running"];
23
+ const DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
24
+ const MIN_POLL_INTERVAL_MS = 250;
25
+ const DEFAULT_POLL_INTERVAL_MS = 1000;
26
+ const WAKE_CHANNELS = [
27
+ INTERCOM_DETACH_REQUEST_EVENT,
28
+ SUBAGENT_ASYNC_COMPLETE_EVENT,
29
+ SUBAGENT_FOREGROUND_COMPLETE_EVENT,
30
+ SUBAGENT_CONTROL_EVENT,
31
+ SUBAGENT_CONTROL_INTERCOM_EVENT,
32
+ SUBAGENT_RESULT_INTERCOM_EVENT
33
+ ];
34
+ function defaultSleep(ms, signal) {
35
+ return new Promise((resolve) => {
36
+ if (signal?.aborted) {
37
+ resolve();
38
+ return;
39
+ }
40
+ const timer = setTimeout(() => {
41
+ signal?.removeEventListener("abort", onAbort);
42
+ resolve();
43
+ }, ms);
44
+ const onAbort = () => {
45
+ clearTimeout(timer);
46
+ resolve();
47
+ };
48
+ signal?.addEventListener("abort", onAbort, { once: true });
49
+ });
50
+ }
51
+ function waitForWake(ms, signal, deps) {
52
+ const sleep = deps.sleep ?? defaultSleep;
53
+ const events = deps.events;
54
+ if (!events)
55
+ return sleep(ms, signal);
56
+ const providerChannels = deps.backgroundWork?.wakeChannels() ?? listBackgroundWorkWakeChannels();
57
+ return new Promise((resolve, reject) => {
58
+ let settled = false;
59
+ const unsubs = [];
60
+ const wakeController = new AbortController;
61
+ const done = () => {
62
+ if (settled)
63
+ return;
64
+ settled = true;
65
+ wakeController.abort();
66
+ signal?.removeEventListener("abort", done);
67
+ for (const u of unsubs) {
68
+ try {
69
+ u();
70
+ } catch {}
71
+ }
72
+ resolve();
73
+ };
74
+ if (signal?.aborted) {
75
+ done();
76
+ return;
77
+ }
78
+ signal?.addEventListener("abort", done, { once: true });
79
+ try {
80
+ for (const channel of [...new Set([...WAKE_CHANNELS, ...providerChannels])]) {
81
+ unsubs.push(events.on(channel, done));
82
+ }
83
+ } catch (error) {
84
+ signal?.removeEventListener("abort", done);
85
+ for (const unsubscribe of unsubs) {
86
+ try {
87
+ unsubscribe();
88
+ } catch {}
89
+ }
90
+ reject(error);
91
+ return;
92
+ }
93
+ sleep(ms, wakeController.signal).then(done);
94
+ });
95
+ }
96
+ function matchesId(run, id) {
97
+ return run.id === id || run.id.startsWith(id);
98
+ }
99
+ function activeDetachedForegroundRuns(params, deps) {
100
+ if (!params.id || !deps.state.foregroundRuns)
101
+ return [];
102
+ const sessionId = deps.state.currentSessionId;
103
+ if (!sessionId)
104
+ return [];
105
+ return [...deps.state.foregroundRuns.values()].filter((run) => (run.runId === params.id || run.runId.startsWith(params.id)) && run.sessionId === sessionId && run.children.some((child) => child.status === "detached"));
106
+ }
107
+ function summarizeForegroundChildren(run, indices) {
108
+ const counts = new Map;
109
+ for (const child of run.children) {
110
+ if (!indices.has(child.index) || child.status === "detached")
111
+ continue;
112
+ counts.set(child.status, (counts.get(child.status) ?? 0) + 1);
113
+ }
114
+ return [...counts.entries()].map(([status, count]) => `${count} ${status}`).join(", ");
115
+ }
116
+ function foregroundChildrenNeedingAttention(run, indices) {
117
+ return run.children.filter((child) => indices.has(child.index) && child.status === "detached" && child.activityState === "needs_attention" && child.currentTool === "contact_supervisor");
118
+ }
119
+ function formatForegroundAttention(run, children, elapsedMs) {
120
+ const childList = children.map((child) => `${child.agent}${child.index !== undefined ? `#${child.index}` : ""}`).join(", ");
121
+ return result(`Waited ${formatDuration(elapsedMs)} for remembered detached foreground run "${run.runId}"; attention required. ${children.length} child run(s) need attention: ${childList}. Reply to any pending supervisor request, then call bg_wait({ id: "${run.runId}" }) again or inspect status; do not resume or launch a replacement while it remains detached.`);
122
+ }
123
+ function needsAttention(run) {
124
+ return run.activityState === "needs_attention" || run.steps.some((step) => step.activityState === "needs_attention");
125
+ }
126
+ function hasSupervisorTool(run) {
127
+ return run.currentTool === "contact_supervisor" || run.currentTool === "intercom" || run.steps.some((step) => step.currentTool === "contact_supervisor" || step.currentTool === "intercom");
128
+ }
129
+ function backgroundWorkIdentity(item) {
130
+ return `${item.provider}\x00${item.sessionId}\x00${item.id}`;
131
+ }
132
+ function backgroundWorkForSession(deps, nowMs) {
133
+ const sessionId = deps.state.currentSessionId;
134
+ if (!sessionId)
135
+ throw new Error("bg_wait requires an active session identity to scope background work safely.");
136
+ return deps.backgroundWork?.snapshot(sessionId, nowMs) ?? snapshotBackgroundWork(sessionId, nowMs);
137
+ }
138
+ function activeRunsForSession(params, deps) {
139
+ const asyncDirRoot = deps.asyncDirRoot ?? DIRS.async;
140
+ const resultsDir = deps.resultsDir ?? DIRS.results;
141
+ const runs = listAsyncRuns(asyncDirRoot, {
142
+ states: [...ACTIVE_STATES],
143
+ sessionId: deps.state.currentSessionId ?? undefined,
144
+ resultsDir,
145
+ kill: deps.kill,
146
+ now: deps.now,
147
+ includeNested: false,
148
+ ...params.id ? { runId: params.id } : {}
149
+ });
150
+ return params.id ? runs.filter((run) => matchesId(run, params.id)) : runs;
151
+ }
152
+ function attentionRunsForSession(params, deps, initialIds) {
153
+ return activeRunsForSession(params, deps).filter((run) => needsAttention(run) && initialIds.has(run.id));
154
+ }
155
+ function runsForIds(runIds, deps) {
156
+ const asyncDirRoot = deps.asyncDirRoot ?? DIRS.async;
157
+ const resultsDir = deps.resultsDir ?? DIRS.results;
158
+ return [...runIds].flatMap((runId) => listAsyncRuns(asyncDirRoot, {
159
+ sessionId: deps.state.currentSessionId ?? undefined,
160
+ resultsDir,
161
+ kill: deps.kill,
162
+ now: deps.now,
163
+ includeNested: false,
164
+ runId,
165
+ exactRunId: true
166
+ }));
167
+ }
168
+ function summarizeTerminalRuns(runs, providerFinishedCount = 0) {
169
+ if (runs.length === 0 && providerFinishedCount === 0)
170
+ return "";
171
+ const counts = { complete: 0, failed: 0, paused: 0 };
172
+ for (const run of runs) {
173
+ const count = counts[run.state];
174
+ if (count !== undefined)
175
+ counts[run.state] = count + 1;
176
+ }
177
+ const parts = [];
178
+ if (counts.complete)
179
+ parts.push(`${counts.complete} complete`);
180
+ if (counts.failed)
181
+ parts.push(`${counts.failed} failed`);
182
+ if (counts.paused)
183
+ parts.push(`${counts.paused} paused`);
184
+ if (providerFinishedCount > 0)
185
+ parts.push(`${providerFinishedCount} provider item(s) finished`);
186
+ return parts.join(", ");
187
+ }
188
+ function completionUsage(completions) {
189
+ if (!completions?.length)
190
+ return;
191
+ const usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
192
+ let projected = false;
193
+ for (const completion of completions) {
194
+ for (const child of completion.results ?? []) {
195
+ if (!child.usage || child.usage.input === 0 && child.usage.output === 0 && child.usage.cacheRead === 0 && child.usage.cacheWrite === 0 && child.usage.cost === 0 && child.usage.turns === 0)
196
+ continue;
197
+ projected = true;
198
+ usage.input += child.usage.input;
199
+ usage.output += child.usage.output;
200
+ usage.cacheRead += child.usage.cacheRead;
201
+ usage.cacheWrite += child.usage.cacheWrite;
202
+ usage.cost += child.usage.cost;
203
+ usage.turns += child.usage.turns;
204
+ }
205
+ }
206
+ return projected ? usage : undefined;
207
+ }
208
+ function result(text, isError = false, completions) {
209
+ const usage = completionUsage(completions);
210
+ return {
211
+ content: [{ type: "text", text }],
212
+ ...isError ? { isError: true } : {},
213
+ ...usage ? { usage: toAgentToolUsage(usage) } : {},
214
+ details: {
215
+ mode: "management",
216
+ results: [],
217
+ ...completions && completions.length > 0 ? { completions } : {}
218
+ }
219
+ };
220
+ }
221
+ function formatCompletionRecovery(completions) {
222
+ const lines = (completions ?? []).flatMap((completion) => (completion.results ?? []).flatMap((child) => formatTimeoutRecoveryLines(child.timeoutRecovery)));
223
+ return lines.length > 0 ? `
224
+ ${lines.join(`
225
+ `)}` : "";
226
+ }
227
+ function windowElapsedResult(text, activeRunIds, activeProviderItems = []) {
228
+ return {
229
+ content: [{ type: "text", text }],
230
+ details: {
231
+ mode: "management",
232
+ results: [],
233
+ wait: {
234
+ reason: "window_elapsed",
235
+ timedOut: true,
236
+ activeRunIds,
237
+ activeProviderItems: activeProviderItems.map(({ provider, id }) => ({ provider, id }))
238
+ }
239
+ }
240
+ };
241
+ }
242
+ function asyncWaitUpdate(runs, providerCount, elapsedMs) {
243
+ const activity = runs.flatMap((run) => {
244
+ const activeSteps = run.steps.filter((step) => step.status === "pending" || step.status === "running");
245
+ if (activeSteps.length === 0) {
246
+ return [`${run.id}: ${run.state}`];
247
+ }
248
+ return activeSteps.map((step) => {
249
+ const current = step.currentTool ?? (step.status === "pending" ? "queued" : "thinking…");
250
+ return `${step.agent}: ${current}${step.currentPath ? ` ${shortenPath(step.currentPath)}` : ""}`;
251
+ });
252
+ });
253
+ const headline = [
254
+ `Waiting ${formatDuration(elapsedMs)} for ${runs.length} async run(s) and ${providerCount} provider item(s).`,
255
+ ...activity
256
+ ].join(" · ");
257
+ return result([headline, runs.length > 0 ? formatAsyncRunList(runs) : ""].filter(Boolean).join(`
258
+ `));
259
+ }
260
+ const TRANSCRIPT_TAIL_BYTES = 128 * 1024;
261
+ const TRANSCRIPT_PREVIEW_LINES = 3;
262
+ const TRANSCRIPT_PREVIEW_WIDTH = 220;
263
+ function compactTranscriptText(value, maxWidth = TRANSCRIPT_PREVIEW_WIDTH) {
264
+ if (typeof value !== "string")
265
+ return;
266
+ const compact = value.replace(/\s+/g, " ").trim();
267
+ if (!compact)
268
+ return;
269
+ return compact.length <= maxWidth ? compact : `${compact.slice(0, Math.max(1, maxWidth - 1))}…`;
270
+ }
271
+ function readTranscriptActivity(transcriptPath) {
272
+ if (!transcriptPath)
273
+ return;
274
+ let fd;
275
+ try {
276
+ const stat = fs.statSync(transcriptPath);
277
+ if (!stat.isFile() || stat.size <= 0)
278
+ return;
279
+ const start = Math.max(0, stat.size - TRANSCRIPT_TAIL_BYTES);
280
+ const length = stat.size - start;
281
+ const buffer = Buffer.allocUnsafe(length);
282
+ fd = fs.openSync(transcriptPath, "r");
283
+ const bytesRead = fs.readSync(fd, buffer, 0, length, start);
284
+ let lines = buffer.subarray(0, bytesRead).toString("utf-8").split(/\r?\n/);
285
+ if (start > 0)
286
+ lines = lines.slice(1);
287
+ let currentTool;
288
+ let currentToolArgs;
289
+ let latestAt;
290
+ const recent = [];
291
+ for (const line of lines) {
292
+ if (!line.trim())
293
+ continue;
294
+ let record;
295
+ try {
296
+ record = JSON.parse(line);
297
+ } catch {
298
+ continue;
299
+ }
300
+ if (typeof record.ts === "number" && Number.isFinite(record.ts))
301
+ latestAt = record.ts;
302
+ if (record.recordType === "tool_start" && typeof record.toolName === "string") {
303
+ currentTool = record.toolName;
304
+ currentToolArgs = compactTranscriptText(record.argsPreview, 140);
305
+ const preview = currentToolArgs ? `${currentTool}: ${currentToolArgs}` : currentTool;
306
+ recent.push(`tool: ${preview}`);
307
+ continue;
308
+ }
309
+ if (record.recordType === "tool_end") {
310
+ currentTool = undefined;
311
+ currentToolArgs = undefined;
312
+ continue;
313
+ }
314
+ if (record.recordType === "message") {
315
+ if (record.sourceEventType === "initial_prompt" || record.role === "user")
316
+ continue;
317
+ const text = compactTranscriptText(record.text);
318
+ if (text)
319
+ recent.push(`${typeof record.role === "string" ? record.role : "message"}: ${text}`);
320
+ continue;
321
+ }
322
+ if (record.recordType === "stdout" || record.recordType === "stderr") {
323
+ const text = compactTranscriptText(record.text);
324
+ if (text)
325
+ recent.push(`${record.recordType}: ${text}`);
326
+ }
327
+ }
328
+ return { currentTool, currentToolArgs, latestAt, recent: recent.slice(-TRANSCRIPT_PREVIEW_LINES) };
329
+ } catch {
330
+ return;
331
+ } finally {
332
+ if (fd !== undefined) {
333
+ try {
334
+ fs.closeSync(fd);
335
+ } catch {}
336
+ }
337
+ }
338
+ }
339
+ function detachedForegroundWaitUpdate(run, pendingIndices, nowMs, elapsedMs) {
340
+ const lines = [`Waiting for detached foreground run "${run.runId}" · ${formatDuration(elapsedMs)}`];
341
+ for (const child of run.children) {
342
+ if (!pendingIndices.has(child.index) || child.status !== "detached")
343
+ continue;
344
+ const activity = readTranscriptActivity(child.transcriptPath);
345
+ const age = activity?.latestAt !== undefined ? ` · activity ${formatDuration(Math.max(0, nowMs - activity.latestAt))} ago` : "";
346
+ lines.push(`${child.agent} · working after supervisor handoff${age}`);
347
+ if (activity?.currentTool) {
348
+ lines.push(` current: ${activity.currentTool}${activity.currentToolArgs ? `: ${activity.currentToolArgs}` : ""}`);
349
+ }
350
+ for (const preview of activity?.recent ?? [])
351
+ lines.push(` ${preview}`);
352
+ if (!activity && child.transcriptPath)
353
+ lines.push(" live transcript has no new activity yet");
354
+ if (!child.transcriptPath)
355
+ lines.push(" live transcript unavailable; waiting for completion event");
356
+ }
357
+ return result(lines.join(`
358
+ `));
359
+ }
360
+ async function waitForDetachedForegroundRun(run, signal, deps, startedAt, now, pollIntervalMs, timeoutMs) {
361
+ const initialDetachedIndices = new Set(run.children.filter((child) => child.status === "detached").map((child) => child.index));
362
+ while (true) {
363
+ if (deps.state.currentSessionId !== run.sessionId) {
364
+ return result(`Wait stopped because the active session changed while remembered foreground run "${run.runId}" was still detached. Return to the originating session to inspect or wait for it. Reply to any pending supervisor request before resuming or launching a replacement.`, true);
365
+ }
366
+ const current = deps.state.foregroundRuns?.get(run.runId);
367
+ if (!current || current.sessionId !== run.sessionId) {
368
+ return result(`Remembered foreground run "${run.runId}" disappeared before a terminal child result was recorded. Completion cannot be confirmed; do not launch a replacement without checking the originating child session.`, true);
369
+ }
370
+ const pending = current.children.filter((child) => initialDetachedIndices.has(child.index) && child.status === "detached");
371
+ const attention = foregroundChildrenNeedingAttention(current, initialDetachedIndices);
372
+ if (attention.length > 0)
373
+ return formatForegroundAttention(current, attention, now() - startedAt);
374
+ if (pending.length === 0) {
375
+ const outcome = summarizeForegroundChildren(current, initialDetachedIndices);
376
+ return result(`Waited ${formatDuration(now() - startedAt)} for remembered detached foreground run "${run.runId}"; done. Outcome: ${outcome || "no recovered child status"}. Completion event observed; inspect with subagent({ action: "status", id: "${run.runId}" }) for recovered output.`);
377
+ }
378
+ const updateNow = now();
379
+ deps.onUpdate?.(detachedForegroundWaitUpdate(current, initialDetachedIndices, updateNow, updateNow - startedAt));
380
+ if (signal?.aborted) {
381
+ return result(`Wait aborted after ${formatDuration(now() - startedAt)}. Remembered foreground run "${run.runId}" remains detached. Reply to any pending supervisor request before resuming or launching a replacement.`, true);
382
+ }
383
+ if (now() - startedAt >= timeoutMs) {
384
+ return windowElapsedResult(`Wait window elapsed after ${formatDuration(timeoutMs)} with remembered foreground run "${run.runId}" still detached. Reply to any pending supervisor request, then call bg_wait({ id: "${run.runId}" }) again or inspect status; do not resume or launch a replacement while it remains detached.`, [run.runId]);
385
+ }
386
+ await waitForWake(pollIntervalMs, signal, deps);
387
+ }
388
+ }
389
+ export async function waitForSubagents(params, signal, deps) {
390
+ if (deps.enabled === false) {
391
+ return result('bg_wait is disabled by config.waitTool or DM_SUBAGENT_WAIT_TOOL_ENABLED; returning immediately without blocking background work. Active work keeps going, and you can inspect subagents with subagent({ action: "status" }) or rely on completion notifications.');
392
+ }
393
+ if (!deps.state.currentSessionId) {
394
+ return result("bg_wait requires an active session identity to scope background work safely.", true);
395
+ }
396
+ const now = deps.now ?? Date.now;
397
+ const pollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
398
+ const timeoutMs = params.timeoutMs !== undefined && params.timeoutMs > 0 ? params.timeoutMs : deps.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
399
+ const startedAt = now();
400
+ const waitForAll = params.id ? true : params.all === true;
401
+ if (params.nonBlocking && !params.id) {
402
+ return result("Non-blocking wait subscriptions require id so the registration can bind one exact run identity.", true);
403
+ }
404
+ if (params.nonBlocking && params.all) {
405
+ return result("nonBlocking cannot be combined with all; subscribe to one exact run id.", true);
406
+ }
407
+ let active;
408
+ let foreground;
409
+ let providerSnapshot;
410
+ try {
411
+ active = activeRunsForSession(params, deps);
412
+ foreground = activeDetachedForegroundRuns(params, deps);
413
+ providerSnapshot = params.id ? { providers: [], items: [] } : backgroundWorkForSession(deps, startedAt);
414
+ } catch (error) {
415
+ return result(error instanceof Error ? error.message : String(error), true);
416
+ }
417
+ if (params.id) {
418
+ const candidates = [
419
+ ...active.map((run) => ({ kind: "async", id: run.id, run })),
420
+ ...foreground.map((run) => ({ kind: "foreground", id: run.runId, run }))
421
+ ];
422
+ const exact = candidates.filter((candidate) => candidate.id === params.id);
423
+ const matches = exact.length > 0 ? exact : candidates;
424
+ if (matches.length > 1) {
425
+ return result(`Ambiguous subagent run id prefix "${params.id}" matched ${matches.length} active runs: ${matches.map((candidate) => candidate.id).join(", ")}. Pass a longer id.`, true);
426
+ }
427
+ const selected = matches[0];
428
+ if (selected && params.nonBlocking) {
429
+ if (!deps.subscribe) {
430
+ return result("Non-blocking wait subscriptions require a long-lived interactive subagent runtime; this runtime can only use blocking bg_wait calls.", true);
431
+ }
432
+ try {
433
+ const registration = deps.subscribe({ targetKind: selected.kind, runId: selected.id, requestedId: params.id, timeoutMs });
434
+ return result(`Armed wait subscription ${registration.token} for exact ${selected.kind} run ${selected.id}. Returning immediately; this session will be woken on completion, failure, attention, reconciliation failure, or timeout. Inspect armed subscriptions with subagent({ action: "status" }).`);
435
+ } catch (error) {
436
+ return result(error instanceof Error ? error.message : String(error), true);
437
+ }
438
+ }
439
+ if (selected?.kind === "foreground") {
440
+ return waitForDetachedForegroundRun(selected.run, signal, deps, startedAt, now, pollIntervalMs, timeoutMs);
441
+ }
442
+ active = selected?.kind === "async" ? [selected.run] : [];
443
+ }
444
+ let providerActive = providerSnapshot.items;
445
+ if (active.length === 0 && providerActive.length === 0) {
446
+ return result(params.id ? `No active run matched "${params.id}". Nothing to wait for.` : "No active async runs or registered provider work in this session. Nothing to wait for.");
447
+ }
448
+ const waitParams = params.id ? { ...params, id: active[0].id } : params;
449
+ const initialAsyncIds = new Set(active.map((run) => run.id));
450
+ const initialProviderIds = new Set(providerActive.map(backgroundWorkIdentity));
451
+ const initialProviderNames = new Set(providerActive.map((item) => item.provider));
452
+ const initialCount = initialAsyncIds.size + initialProviderIds.size;
453
+ const stopOnAttention = params.stopOnAttention ?? deps.stopOnAttention !== false;
454
+ let attention = active.filter((run) => needsAttention(run));
455
+ const isDone = () => {
456
+ if (attention.some((run) => initialAsyncIds.has(run.id) && (stopOnAttention || hasSupervisorTool(run))))
457
+ return true;
458
+ const activeAsyncIds = new Set(active.map((run) => run.id));
459
+ const activeProviderIds = new Set(providerActive.map(backgroundWorkIdentity));
460
+ if (waitForAll) {
461
+ return [...initialAsyncIds].every((id) => !activeAsyncIds.has(id)) && [...initialProviderIds].every((id) => !activeProviderIds.has(id));
462
+ }
463
+ return [...initialAsyncIds].some((id) => !activeAsyncIds.has(id)) || [...initialProviderIds].some((id) => !activeProviderIds.has(id));
464
+ };
465
+ while (!isDone()) {
466
+ const activeInitialRuns = active.filter((run) => initialAsyncIds.has(run.id));
467
+ const activeInitialProviderItems = providerActive.filter((item) => initialProviderIds.has(backgroundWorkIdentity(item)));
468
+ const stillActive = [
469
+ ...activeInitialRuns.map((run) => `${run.id} (${run.state})`),
470
+ ...activeInitialProviderItems.map((item) => `${item.provider}/${item.id}`)
471
+ ].join(", ");
472
+ deps.onUpdate?.(asyncWaitUpdate(activeInitialRuns, activeInitialProviderItems.length, now() - startedAt));
473
+ if (signal?.aborted) {
474
+ return result(`Wait aborted after ${formatDuration(now() - startedAt)}. Still active: ${stillActive}.`, true);
475
+ }
476
+ if (now() - startedAt >= timeoutMs) {
477
+ return windowElapsedResult(`Wait window elapsed after ${formatDuration(timeoutMs)} with ${activeInitialRuns.length} async run(s) and ${activeInitialProviderItems.length} provider item(s) still active: ${stillActive}. The work keeps going; call bg_wait again or inspect subagent status.`, activeInitialRuns.map((run) => run.id), activeInitialProviderItems);
478
+ }
479
+ try {
480
+ await waitForWake(pollIntervalMs, signal, deps);
481
+ active = activeRunsForSession(waitParams, deps);
482
+ attention = attentionRunsForSession(waitParams, deps, initialAsyncIds);
483
+ providerSnapshot = params.id ? providerSnapshot : backgroundWorkForSession(deps, now());
484
+ for (const provider of initialProviderNames) {
485
+ if (!providerSnapshot.providers.includes(provider)) {
486
+ return result(`Background-work provider '${provider}' disappeared while bg_wait was tracking its active work; completion cannot be confirmed.`, true);
487
+ }
488
+ }
489
+ providerActive = providerSnapshot.items;
490
+ } catch (error) {
491
+ return result(error instanceof Error ? error.message : String(error), true);
492
+ }
493
+ }
494
+ let terminalSummary;
495
+ let finishedAsyncCount;
496
+ let failedAsyncCount;
497
+ let completions;
498
+ let resumeGuidance = "";
499
+ const activeProviderIds = new Set(providerActive.map(backgroundWorkIdentity));
500
+ const providerFinishedCount = [...initialProviderIds].filter((id) => !activeProviderIds.has(id)).length;
501
+ try {
502
+ const allNow = runsForIds(initialAsyncIds, deps);
503
+ const terminal = allNow.filter((run) => !ACTIVE_STATES.includes(run.state) && initialAsyncIds.has(run.id));
504
+ finishedAsyncCount = terminal.length;
505
+ failedAsyncCount = terminal.filter((run) => run.state === "failed" || run.state === "partial").length;
506
+ terminalSummary = summarizeTerminalRuns(terminal, providerFinishedCount);
507
+ resumeGuidance = formatResumeFirstFailedRunsNote(terminal);
508
+ completions = collectWaitCompletions(terminal, deps.state, deps.resultsDir ?? DIRS.results);
509
+ } catch (error) {
510
+ return result(error instanceof Error ? error.message : String(error), true);
511
+ }
512
+ const relevantAttention = attention.filter((run) => initialAsyncIds.has(run.id));
513
+ const supervisorAttentionHint = relevantAttention.some(hasSupervisorTool) ? ' Reply to any pending supervisor request. If subagent_supervisor({ action: "pending" }) is empty, check intercom({ action: "pending" }) because an external intercom tool may own the request.' : "";
514
+ const attentionNote = relevantAttention.length > 0 ? ` ${relevantAttention.length} run(s) need attention: ${relevantAttention.map((run) => run.id).join(", ")} —${supervisorAttentionHint} inspect with subagent({ action: "status" }) then steer a top-level live async child, resume a paused/completed/failed child, or interrupt explicitly.` : "";
515
+ const stillRunning = active.filter((run) => initialAsyncIds.has(run.id)).length + providerActive.filter((item) => initialProviderIds.has(backgroundWorkIdentity(item))).length;
516
+ const elapsed = formatDuration(now() - startedAt);
517
+ const outcome = terminalSummary ? ` Outcome: ${terminalSummary}.` : "";
518
+ const recoveryNote = formatCompletionRecovery(completions);
519
+ if (waitForAll) {
520
+ const scope = params.id ? `run "${params.id}"` : initialProviderIds.size === 0 ? `${initialAsyncIds.size} async run(s)` : `${initialAsyncIds.size} async run(s) and ${initialProviderIds.size} provider item(s)`;
521
+ const status = relevantAttention.length > 0 ? "attention required" : "done";
522
+ return result(`Waited ${elapsed} for ${scope}; ${status}.${outcome}${recoveryNote}${resumeGuidance}${attentionNote} Completion/control events have been observed; inspect status if a notification is not visible yet.`, deps.failOnFailedRuns === true && failedAsyncCount > 0 || deps.failOnAttention === true && relevantAttention.length > 0, completions);
523
+ }
524
+ const finishedCount = finishedAsyncCount + providerFinishedCount;
525
+ const subject = initialProviderIds.size === 0 ? "run(s)" : "item(s)";
526
+ const remainder = stillRunning > 0 ? ` ${stillRunning} ${subject} still in flight — call bg_wait again to catch the next one.` : relevantAttention.length > 0 ? " No other work is waitable until attention is handled." : initialProviderIds.size === 0 ? " No runs remain in flight." : " No work remains in flight.";
527
+ const progress = relevantAttention.length > 0 && finishedCount === 0 ? `${relevantAttention.length} of ${initialCount} ${subject} need attention` : `${finishedCount} of ${initialCount} ${subject} finished`;
528
+ return result(`Waited ${elapsed}; ${progress}.${outcome}${recoveryNote}${resumeGuidance}${attentionNote}${remainder} Relevant completion/control events have been observed; inspect status if a notification is not visible yet.`, deps.failOnFailedRuns === true && failedAsyncCount > 0 || deps.failOnAttention === true && relevantAttention.length > 0, completions);
529
+ }
@@ -0,0 +1,106 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { readStatus } from "../../shared/utils.js";
4
+ import { writeAtomicJson } from "../../shared/atomic-json.js";
5
+ import { encodeIndexSegment } from "./index-segment.js";
6
+ export const TERMINAL_RUN_INDEX_DIR = ".terminal-runs";
7
+ const TERMINAL_RUN_INDEX_VERSION = 1;
8
+ const TIMESTAMP_WIDTH = 16;
9
+ function isTerminalState(state) {
10
+ return state === "complete" || state === "failed" || state === "partial" || state === "paused" || state === "stopped" || state === "rejected";
11
+ }
12
+ function indexRoot(asyncDirRoot) {
13
+ return path.join(asyncDirRoot, TERMINAL_RUN_INDEX_DIR);
14
+ }
15
+ function sessionIndexDir(asyncDirRoot, sessionId) {
16
+ return path.join(indexRoot(asyncDirRoot), encodeIndexSegment(sessionId));
17
+ }
18
+ function markerName(endedAt, runId) {
19
+ return `${Math.max(0, Math.floor(endedAt)).toString().padStart(TIMESTAMP_WIDTH, "0")}-${encodeIndexSegment(runId)}.json`;
20
+ }
21
+ function markerPath(asyncDir, sessionId, endedAt) {
22
+ return path.join(sessionIndexDir(path.dirname(asyncDir), sessionId), markerName(endedAt, path.basename(asyncDir)));
23
+ }
24
+ function parseEntry(value) {
25
+ if (!value || typeof value !== "object" || Array.isArray(value))
26
+ return;
27
+ const entry = value;
28
+ if (entry.version !== TERMINAL_RUN_INDEX_VERSION || typeof entry.runId !== "string" || !entry.runId || typeof entry.sessionId !== "string" || !entry.sessionId || typeof entry.endedAt !== "number" || !Number.isFinite(entry.endedAt))
29
+ return;
30
+ return { version: TERMINAL_RUN_INDEX_VERSION, runId: entry.runId, sessionId: entry.sessionId, endedAt: entry.endedAt };
31
+ }
32
+ export function updateTerminalRunIndex(asyncDir, status) {
33
+ if (!isTerminalState(status.state) || typeof status.sessionId !== "string" || !status.sessionId)
34
+ return;
35
+ const endedAt = status.endedAt ?? status.lastUpdate ?? status.startedAt;
36
+ const marker = markerPath(asyncDir, status.sessionId, endedAt);
37
+ const entry = {
38
+ version: TERMINAL_RUN_INDEX_VERSION,
39
+ runId: status.runId || path.basename(asyncDir),
40
+ sessionId: status.sessionId,
41
+ endedAt
42
+ };
43
+ writeAtomicJson(marker, entry);
44
+ }
45
+ function removeInvalidMarker(marker) {
46
+ try {
47
+ fs.rmSync(marker, { force: true });
48
+ } catch {}
49
+ }
50
+ function markerFiles(dir) {
51
+ try {
52
+ return fs.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => ({ dir, name: entry.name }));
53
+ } catch (error) {
54
+ const code = error.code;
55
+ if (code === "ENOENT" || code === "ENOTDIR")
56
+ return [];
57
+ throw error;
58
+ }
59
+ }
60
+ function sessionDirs(asyncDirRoot, sessionId) {
61
+ if (sessionId)
62
+ return [sessionIndexDir(asyncDirRoot, sessionId)];
63
+ try {
64
+ return fs.readdirSync(indexRoot(asyncDirRoot), { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path.join(indexRoot(asyncDirRoot), entry.name));
65
+ } catch (error) {
66
+ const code = error.code;
67
+ if (code === "ENOENT" || code === "ENOTDIR")
68
+ return [];
69
+ throw error;
70
+ }
71
+ }
72
+ function recentMarkerFiles(dirs, limit) {
73
+ return dirs.flatMap(markerFiles).sort((left, right) => right.name.localeCompare(left.name)).slice(0, limit).map((marker) => path.join(marker.dir, marker.name));
74
+ }
75
+ export function readRecentTerminalRunIndex(asyncDirRoot, options = {}) {
76
+ const limit = options.limit === undefined ? Number.POSITIVE_INFINITY : Math.max(0, Math.floor(options.limit));
77
+ const candidates = recentMarkerFiles(sessionDirs(asyncDirRoot, options.sessionId), limit);
78
+ const runIds = [];
79
+ const seen = new Set;
80
+ for (const marker of candidates) {
81
+ let entry;
82
+ try {
83
+ entry = parseEntry(JSON.parse(fs.readFileSync(marker, "utf-8")));
84
+ } catch (error) {
85
+ if (error.code === "ENOENT")
86
+ continue;
87
+ }
88
+ if (!entry || options.sessionId !== undefined && entry.sessionId !== options.sessionId) {
89
+ removeInvalidMarker(marker);
90
+ continue;
91
+ }
92
+ const asyncDir = path.join(asyncDirRoot, entry.runId);
93
+ const status = readStatus(asyncDir);
94
+ if (!status || !isTerminalState(status.state) || status.sessionId !== entry.sessionId || status.runId && status.runId !== entry.runId) {
95
+ removeInvalidMarker(marker);
96
+ continue;
97
+ }
98
+ if (seen.has(entry.runId)) {
99
+ removeInvalidMarker(marker);
100
+ continue;
101
+ }
102
+ seen.add(entry.runId);
103
+ runIds.push(entry.runId);
104
+ }
105
+ return runIds;
106
+ }
@@ -1,5 +1,5 @@
1
1
  export function applyForceTopLevelAsyncOverride(params, depth, forceTopLevelAsync) {
2
- if (!(depth === 0 && forceTopLevelAsync))
2
+ if (params.foregroundOnly || !(depth === 0 && forceTopLevelAsync))
3
3
  return params;
4
4
  return { ...params, async: true, clarify: false };
5
5
  }