@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
@@ -4,46 +4,251 @@ import * as os from "node:os";
4
4
  import * as path from "node:path";
5
5
  import { keyText } from "@duckmind/dm-coding-agent";
6
6
  import { Box, Container, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@duckmind/dm-tui";
7
- import { discoverAgents } from "../agents/agents.js";
7
+ import { discoverAgentSnapshot, discoverAgents } from "../agents/agents.js";
8
+ import { clearRuntimeAgentsForPi, listRuntimeAgentConfigs, mergeRuntimeAgents } from "../agents/runtime-agent-registry.js";
9
+ import { registerRuntimeAgentEventListener } from "../agents/runtime-agent-events.js";
10
+ import { ensureAccessibleDir } from "../shared/accessible-dir.js";
8
11
  import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.js";
9
12
  import { resolveCurrentSessionId } from "../shared/session-identity.js";
13
+ import { getAgentDir } from "../shared/utils.js";
14
+ import { isStaleExtensionContextError, withCachedUiContext } from "../shared/extension-context.js";
15
+ import { currentCompletionOwnerId } from "../shared/completion-owner.js";
10
16
  import { cleanupOldChainDirs } from "../shared/settings.js";
11
- import { clearLegacyResultAnimationTimer, renderWidget, renderSubagentResult } from "../tui/render.js";
12
- import { SubagentParams, WaitParams } from "./schemas.js";
17
+ import { clearLegacyResultAnimationTimer, renderSubagentResult, renderSubagentSummary } from "../tui/render.js";
18
+ import { openSubagentFleet } from "../tui/fleet.js";
19
+ import { SubagentFleetStatus, resolveFleetViewPlacement } from "../tui/fleet-status.js";
20
+ import { createSubagentParamsSchema } from "./schemas.js";
13
21
  import { createSubagentExecutor } from "../runs/foreground/subagent-executor.js";
14
22
  import { createAsyncJobTracker } from "../runs/background/async-job-tracker.js";
23
+ import { getActiveAsyncCapacitySnapshot, resolveAbandonedSlotReleaseAfterMs, resolveMaxActiveAsyncRunsPerSession } from "../runs/background/active-async-capacity.js";
24
+ import { cleanupResultIndexes, missionObserverResultCandidateFiles } from "../runs/background/result-files.js";
25
+ import { ASYNC_RETENTION_DELAY_MS, cleanupAsyncRetention } from "../runs/background/async-retention.js";
15
26
  import { createResultWatcher } from "../runs/background/result-watcher.js";
27
+ import { createResultDeliveryOwnership } from "../runs/background/result-delivery-ownership.js";
16
28
  import { createScheduledRunManager } from "../runs/background/scheduled-runs.js";
17
29
  import { registerSlashCommands } from "../slash/slash-commands.js";
18
30
  import { registerPromptTemplateDelegationBridge } from "../slash/prompt-template-bridge.js";
31
+ import { registerMainWatchdog } from "../watchdog/register-main.js";
19
32
  import { registerSlashSubagentBridge } from "../slash/slash-bridge.js";
20
33
  import { createNativeSupervisorChannel } from "../intercom/native-supervisor-channel.js";
34
+ import { registerHerdrStatusBridge } from "../integrations/herdr-status.js";
35
+ import { listHerdrProjectPaneRoots, restoreHerdrProjectPaneSnapshots } from "../inspectors/herdr/project-panes.js";
21
36
  import { registerSubagentRpcBridge } from "./rpc.js";
22
37
  import { clearSlashSnapshots, getSlashRenderableSnapshot, resolveSlashMessageDetails, restoreSlashFinalSnapshots } from "../slash/slash-live-state.js";
23
- import { waitForSubagents } from "../runs/background/wait.js";
24
- import registerSubagentNotify from "../runs/background/notify.js";
38
+ import { resolveWaitToolConfig } from "../runs/background/subagent-wait.js";
39
+ import { registerWaitTool } from "../runs/background/wait-tool.js";
40
+ import { createWaitSubscriptionManager } from "../runs/background/wait-subscriptions.js";
41
+ import { drainOutstandingWork } from "../runs/background/auto-drain.js";
42
+ import registerSubagentNotify, { parseSubagentNotifyContent } from "../runs/background/notify.js";
43
+ import { formatSteeringNotice, handleSubagentSteeringNotice, SUBAGENT_STEERING_MESSAGE_TYPE } from "./steering-notices.js";
25
44
  import { SUBAGENT_CHILD_ENV, SUBAGENT_PARENT_SESSION_ENV } from "../runs/shared/dm-args.js";
45
+ import { resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.js";
26
46
  import { formatDuration, shortenPath } from "../shared/formatters.js";
27
- import { loadConfig } from "./config.js";
28
- import { buildSubagentToolDescription } from "./tool-description.js";
47
+ import { applyModelExclusionsConfig, loadConfig, resolveAsyncByDefault, resolveScheduledStoreRoot } from "./config.js";
48
+ import { buildSubagentToolDescription, buildSubagentToolPromptMetadata } from "./tool-description.js";
49
+ import { formatWorkflowPreflightSummary, normalizeWorkflowPreflight } from "../workflows/workflow-preflight.js";
50
+ import { finalizeToolResult } from "./tool-result.js";
51
+ import { collectGoalContinuationNotices } from "../missions/goal-driver.js";
52
+ import { restoreForegroundRunHistory } from "../runs/foreground/foreground-history.js";
53
+ import { resolveMissionStoreLocation } from "../missions/store.js";
54
+ import { listRetainedChildren } from "../runs/background/retained-children.js";
29
55
  import {
30
- ASYNC_DIR,
56
+ DIRS,
31
57
  DEFAULT_ARTIFACT_CONFIG,
32
- RESULTS_DIR,
33
58
  SLASH_RESULT_TYPE,
34
59
  SLASH_TEXT_RESULT_TYPE,
35
60
  SUBAGENT_ASYNC_COMPLETE_EVENT,
36
61
  SUBAGENT_ASYNC_STARTED_EVENT,
62
+ SUBAGENT_PROCESS_TERMINAL_EVENT,
37
63
  SUBAGENT_CONTROL_EVENT,
38
- WIDGET_KEY
64
+ SUBAGENT_STEERING_NOTICE_EVENT,
65
+ WIDGET_KEY,
66
+ resolveMaxSubagentSpawnsPerSession
39
67
  } from "../shared/types.js";
40
68
  import {
41
- clearPendingForegroundControlNotices,
42
69
  formatSubagentControlNotice,
43
70
  handleSubagentControlNotice,
44
71
  SUBAGENT_CONTROL_MESSAGE_TYPE
45
72
  } from "./control-notices.js";
46
- export { loadConfig } from "./config.js";
73
+ export { loadConfig, resolveAsyncByDefault } from "./config.js";
74
+ const SLOW_RELOAD_PHASE_MS = 250;
75
+ const RUNTIME_REGISTRY_STORE_KEY = "__piSubagentRuntimeRegistry";
76
+ function getRuntimeRegistry() {
77
+ const globalStore = globalThis;
78
+ const existing = globalStore[RUNTIME_REGISTRY_STORE_KEY];
79
+ if (existing?.bySessionManager instanceof WeakMap && existing.activeEntries instanceof Set) {
80
+ if (!(existing.visibleControlNoticesBySessionManager instanceof WeakMap)) {
81
+ existing.visibleControlNoticesBySessionManager = new WeakMap;
82
+ }
83
+ return existing;
84
+ }
85
+ const registry = {
86
+ bySessionManager: new WeakMap,
87
+ visibleControlNoticesBySessionManager: new WeakMap,
88
+ activeEntries: new Set
89
+ };
90
+ globalStore[RUNTIME_REGISTRY_STORE_KEY] = registry;
91
+ return registry;
92
+ }
93
+ function logSlowPhase(label, startedAt) {
94
+ const elapsed = Date.now() - startedAt;
95
+ if (elapsed >= SLOW_RELOAD_PHASE_MS)
96
+ console.error(`Subagent reload phase '${label}' took ${elapsed}ms.`);
97
+ }
98
+ function workflowLaneKeys(script) {
99
+ const keys = [];
100
+ const seen = new Set;
101
+ const add = (key) => {
102
+ if (!seen.has(key)) {
103
+ seen.add(key);
104
+ keys.push(key);
105
+ }
106
+ };
107
+ const isIdentifier = (char) => char !== undefined && /[\w$]/.test(char);
108
+ const skipTrivia = (start) => {
109
+ let index = start;
110
+ while (index < script.length) {
111
+ if (/\s/.test(script[index]))
112
+ index += 1;
113
+ else if (script.startsWith("//", index)) {
114
+ const end = script.indexOf(`
115
+ `, index + 2);
116
+ index = end === -1 ? script.length : end + 1;
117
+ } else if (script.startsWith("/*", index)) {
118
+ const end = script.indexOf("*/", index + 2);
119
+ index = end === -1 ? script.length : end + 2;
120
+ } else
121
+ break;
122
+ }
123
+ return index;
124
+ };
125
+ const readLiteral = (start) => {
126
+ const quote = script[start];
127
+ if (quote !== "'" && quote !== '"' && quote !== "`")
128
+ return;
129
+ let index = start + 1;
130
+ let dynamicTemplate = false;
131
+ while (index < script.length) {
132
+ if (script[index] === "\\") {
133
+ index += 2;
134
+ continue;
135
+ }
136
+ if (quote === "`" && script.startsWith("${", index))
137
+ dynamicTemplate = true;
138
+ if (script[index] === quote)
139
+ return { key: dynamicTemplate ? undefined : script.slice(start + 1, index), end: index + 1 };
140
+ if (quote !== "`" && /[\r\n]/.test(script[index]))
141
+ return { end: index + 1 };
142
+ index += 1;
143
+ }
144
+ return { end: script.length };
145
+ };
146
+ const collectRunsAllKeys = (start) => {
147
+ let index = skipTrivia(start);
148
+ if (script[index] !== "(")
149
+ return start;
150
+ index = skipTrivia(index + 1);
151
+ if (script[index] !== "[")
152
+ return start;
153
+ let arrayDepth = 1;
154
+ let objectDepth = 0;
155
+ let directChildObject = false;
156
+ let expectingElement = true;
157
+ for (index += 1;index < script.length; index += 1) {
158
+ index = skipTrivia(index);
159
+ const literal = readLiteral(index);
160
+ if (literal) {
161
+ index = literal.end - 1;
162
+ continue;
163
+ }
164
+ if (script[index] === "[") {
165
+ arrayDepth += 1;
166
+ expectingElement = false;
167
+ continue;
168
+ }
169
+ if (script[index] === "]") {
170
+ arrayDepth -= 1;
171
+ if (arrayDepth === 0)
172
+ return index + 1;
173
+ continue;
174
+ }
175
+ if (script[index] === "{") {
176
+ objectDepth += 1;
177
+ if (objectDepth === 1)
178
+ directChildObject = arrayDepth === 1 && expectingElement;
179
+ expectingElement = false;
180
+ continue;
181
+ }
182
+ if (script[index] === "}") {
183
+ objectDepth -= 1;
184
+ if (objectDepth === 0)
185
+ directChildObject = false;
186
+ continue;
187
+ }
188
+ if (script[index] === "," && arrayDepth === 1 && objectDepth === 0) {
189
+ expectingElement = true;
190
+ continue;
191
+ }
192
+ if (directChildObject && objectDepth === 1 && !isIdentifier(script[index - 1]) && script.startsWith("key", index) && !isIdentifier(script[index + 3])) {
193
+ const colon = skipTrivia(index + 3);
194
+ const key = script[colon] === ":" ? readLiteral(skipTrivia(colon + 1)) : undefined;
195
+ if (key) {
196
+ const next = skipTrivia(key.end);
197
+ if (key.key !== undefined && (script[next] === "," || script[next] === "}"))
198
+ add(key.key);
199
+ index = key.end - 1;
200
+ }
201
+ }
202
+ }
203
+ return index;
204
+ };
205
+ for (let index = 0;index < script.length; ) {
206
+ index = skipTrivia(index);
207
+ const literal = readLiteral(index);
208
+ if (literal) {
209
+ index = literal.end;
210
+ continue;
211
+ }
212
+ if (!isIdentifier(script[index - 1]) && script.startsWith("runs.run", index) && !isIdentifier(script[index + 8])) {
213
+ const open = skipTrivia(index + 8);
214
+ const key = script[open] === "(" ? readLiteral(skipTrivia(open + 1)) : undefined;
215
+ if (key) {
216
+ const next = skipTrivia(key.end);
217
+ if (key.key !== undefined && (script[next] === "," || script[next] === ")"))
218
+ add(key.key);
219
+ index = key.end;
220
+ continue;
221
+ }
222
+ }
223
+ if (!isIdentifier(script[index - 1]) && script.startsWith("runs.all", index) && !isIdentifier(script[index + 8])) {
224
+ index = collectRunsAllKeys(index + 8);
225
+ continue;
226
+ }
227
+ index += 1;
228
+ }
229
+ return keys;
230
+ }
231
+ function formatWorkflowPreflightCall(input) {
232
+ if (input === undefined)
233
+ return "";
234
+ try {
235
+ return formatWorkflowPreflightSummary(normalizeWorkflowPreflight(input));
236
+ } catch (error) {
237
+ return `preflight · rejected: ${error instanceof Error ? error.message : String(error)}`;
238
+ }
239
+ }
240
+ function formatWorkflowManifest(script, async, clarify, preflightInput) {
241
+ if (clarify === true)
242
+ return "workflow script · rejected: clarify UI unsupported";
243
+ const keys = workflowLaneKeys(script);
244
+ const mode = async === false ? "foreground" : "background";
245
+ const preflight = formatWorkflowPreflightCall(preflightInput);
246
+ if (keys.length === 0)
247
+ return `workflow script · ${mode}${preflight ? ` · ${preflight}` : ""}`;
248
+ const visibleKeys = keys.slice(0, 4).join(", ");
249
+ const remainder = keys.length > 4 ? `, +${keys.length - 4}` : "";
250
+ return `workflow · ${mode} · ${keys.length} lane${keys.length === 1 ? "" : "s"}: ${visibleKeys}${remainder}${preflight ? ` · ${preflight}` : ""}`;
251
+ }
47
252
  function getSubagentSessionRoot(parentSessionFile) {
48
253
  if (parentSessionFile) {
49
254
  const baseName = path.basename(parentSessionFile, ".jsonl");
@@ -55,100 +260,33 @@ function getSubagentSessionRoot(parentSessionFile) {
55
260
  function expandTilde(p) {
56
261
  return p.startsWith("~/") ? path.join(os.homedir(), p.slice(2)) : p;
57
262
  }
58
- function ensureAccessibleDir(dirPath) {
59
- fs.mkdirSync(dirPath, { recursive: true });
60
- try {
61
- fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
62
- } catch {
63
- try {
64
- fs.rmSync(dirPath, { recursive: true, force: true });
65
- } catch {}
66
- fs.mkdirSync(dirPath, { recursive: true });
67
- fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
68
- }
69
- }
70
263
  function isSlashResultRunning(result) {
71
264
  return result.details?.progress?.some((entry) => entry.status === "running") || result.details?.results.some((entry) => entry.progress?.status === "running") || false;
72
265
  }
73
- function subagentResultIsRunning(result) {
74
- return result.details?.progress?.some((entry) => entry.status === "running") || result.details?.results.some((entry) => entry.progress?.status === "running") || false;
75
- }
76
- function ensureSubagentResultAnimation(context) {
77
- const state = context.state;
78
- if (state.subagentResultAnimationTimer)
79
- return;
80
- if (typeof context.invalidate !== "function")
81
- return;
82
- if (state.frame === undefined)
83
- state.frame = 0;
84
- state.subagentResultAnimationTimer = setInterval(() => {
85
- state.frame = ((state.frame ?? 0) + 1) % 10;
86
- try {
87
- context.invalidate();
88
- } catch {}
89
- }, 80);
90
- }
91
266
  function isSlashResultError(result) {
92
267
  return result.details?.results.some((entry) => entry.exitCode !== 0 && entry.progress?.status !== "running") || false;
93
268
  }
94
- function isStaleExtensionContextError(error) {
95
- return error instanceof Error && error.message.includes("Extension context no longer active");
96
- }
97
- function rebuildSlashResultContainer(container, result, options, theme) {
269
+ function rebuildSlashResultContainer(container, result, options, theme, rendererConfig, foregroundDetachShortcut) {
98
270
  container.clear();
99
271
  container.addChild(new Spacer(1));
100
272
  const boxTheme = isSlashResultRunning(result) ? "toolPendingBg" : isSlashResultError(result) ? "toolErrorBg" : "toolSuccessBg";
101
273
  const box = new Box(1, 1, (text) => theme.bg(boxTheme, text));
102
- box.addChild(renderSubagentResult(result, options, theme));
274
+ box.addChild(renderSubagentResult(result, options, theme, undefined, rendererConfig, foregroundDetachShortcut));
103
275
  container.addChild(box);
104
276
  }
105
- function createSlashResultComponent(details, options, theme) {
277
+ function createSlashResultComponent(details, options, theme, rendererConfig, foregroundDetachShortcut) {
106
278
  const container = new Container;
107
279
  let lastVersion = -1;
108
280
  container.render = (width) => {
109
281
  const snapshot = getSlashRenderableSnapshot(details);
110
282
  if (snapshot.version !== lastVersion || isSlashResultRunning(snapshot.result)) {
111
283
  lastVersion = snapshot.version;
112
- rebuildSlashResultContainer(container, snapshot.result, options, theme);
284
+ rebuildSlashResultContainer(container, snapshot.result, options, theme, rendererConfig, foregroundDetachShortcut);
113
285
  }
114
286
  return Container.prototype.render.call(container, width);
115
287
  };
116
288
  return container;
117
289
  }
118
- function parseSubagentNotifyContent(content) {
119
- const lines = content.split(`
120
- `);
121
- const header = lines[0] ?? "";
122
- const match = header.match(/^Background task (completed|failed|paused): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
123
- if (!match)
124
- return;
125
- const body = lines.slice(2);
126
- let sessionIndex = -1;
127
- for (let i = body.length - 1;i >= 1; i--) {
128
- if (body[i - 1]?.trim() === "" && /^(Session|Session file|Session share error):\s+/.test(body[i])) {
129
- sessionIndex = i;
130
- break;
131
- }
132
- }
133
- const sessionLine = sessionIndex >= 0 ? body[sessionIndex] : undefined;
134
- const resultLines = sessionIndex >= 0 ? body.slice(0, sessionIndex) : body;
135
- const resultPreview = resultLines.join(`
136
- `).trim() || "(no output)";
137
- let sessionLabel;
138
- let sessionValue;
139
- if (sessionLine) {
140
- const separator = sessionLine.indexOf(":");
141
- sessionLabel = sessionLine.slice(0, separator).toLowerCase();
142
- sessionValue = sessionLine.slice(separator + 1).trim();
143
- }
144
- return {
145
- agent: match[2],
146
- status: match[1],
147
- ...match[3] ? { taskInfo: match[3] } : {},
148
- resultPreview,
149
- ...sessionLabel && sessionValue ? { sessionLabel, sessionValue } : {}
150
- };
151
- }
152
290
 
153
291
  class SubagentControlNoticeComponent {
154
292
  details;
@@ -177,39 +315,90 @@ class SubagentControlNoticeComponent {
177
315
  return lines;
178
316
  }
179
317
  }
180
- export default function registerSubagentExtension(pi) {
318
+ export function projectActiveHerdrRuns(state) {
319
+ const active = (status) => status === "queued" || status === "running";
320
+ const foregroundChildrenByWorkflow = new Map;
321
+ for (const control of state.foregroundControls.values()) {
322
+ if (!control.parentWorkflowRunId)
323
+ continue;
324
+ const children = control.activeChildren?.size ? [...control.activeChildren.values()].map((child) => ({
325
+ agent: child.agent,
326
+ needsAttention: child.currentActivityState === "needs_attention"
327
+ })) : control.currentAgent ? [{ agent: control.currentAgent, needsAttention: control.currentActivityState === "needs_attention" }] : [];
328
+ if (children.length === 0)
329
+ continue;
330
+ const existing = foregroundChildrenByWorkflow.get(control.parentWorkflowRunId) ?? [];
331
+ existing.push(...children);
332
+ foregroundChildrenByWorkflow.set(control.parentWorkflowRunId, existing);
333
+ }
334
+ return [...state.asyncJobs.values()].filter((job) => active(job.status)).map((job) => {
335
+ const children = job.mode === "workflow" ? foregroundChildrenByWorkflow.get(job.asyncId) : undefined;
336
+ const currentStep = job.steps?.find((step) => step.status === "running") ?? (job.currentStep !== undefined ? job.steps?.[job.currentStep] : undefined) ?? job.steps?.find((step) => step.status === "pending");
337
+ return {
338
+ id: job.asyncId,
339
+ agents: children?.length ? children.map((child) => child.agent) : job.agents,
340
+ ...currentStep?.label ? { taskLabel: currentStep.label } : {},
341
+ needsAttention: job.activityState === "needs_attention" || children?.some((child) => child.needsAttention)
342
+ };
343
+ });
344
+ }
345
+ export default function registerSubagentExtension(dm) {
181
346
  if (process.env[SUBAGENT_CHILD_ENV] === "1") {
182
347
  return;
183
348
  }
184
- const globalStore = globalThis;
185
- const runtimeCleanupStoreKey = "__piSubagentRuntimeCleanup";
186
- const previousRuntimeCleanup = globalStore[runtimeCleanupStoreKey];
187
- if (typeof previousRuntimeCleanup === "function") {
188
- try {
189
- previousRuntimeCleanup();
190
- } catch {}
191
- }
192
- ensureAccessibleDir(RESULTS_DIR);
193
- ensureAccessibleDir(ASYNC_DIR);
349
+ const runtimeRegistry = getRuntimeRegistry();
350
+ DIRS.results = ensureAccessibleDir(DIRS.results);
351
+ DIRS.async = ensureAccessibleDir(DIRS.async);
194
352
  cleanupOldChainDirs();
195
353
  const config = loadConfig();
196
- const asyncByDefault = config.asyncByDefault === true;
354
+ applyModelExclusionsConfig(config);
355
+ const waitToolConfig = resolveWaitToolConfig(config.waitTool);
356
+ const asyncByDefault = resolveAsyncByDefault(config);
357
+ const fleetViewEnabled = config.fleetView !== false;
358
+ const fleetViewPlacement = resolveFleetViewPlacement(config.fleetViewPlacement);
359
+ const asyncWidgetEnabled = config.asyncWidget !== false;
360
+ const summaryInlineToolDisplay = config.inlineToolDisplay === "summary";
197
361
  const tempArtifactsDir = getArtifactsDir(null);
198
- cleanupAllArtifactDirs(DEFAULT_ARTIFACT_CONFIG.cleanupDays);
362
+ const artifactCleanupDays = config.artifactConfig?.cleanupDays ?? DEFAULT_ARTIFACT_CONFIG.cleanupDays;
363
+ cleanupAllArtifactDirs(artifactCleanupDays);
364
+ const resultIndexCleanupTimer = setTimeout(() => {
365
+ try {
366
+ cleanupResultIndexes(DIRS.results);
367
+ } catch (error) {
368
+ console.error("Failed to clean stale subagent result indexes:", error);
369
+ }
370
+ }, 30000);
371
+ resultIndexCleanupTimer.unref?.();
199
372
  const state = {
200
373
  baseCwd: "",
201
374
  currentSessionId: null,
375
+ statusProjectionSessionId: null,
376
+ completionOwnerId: currentCompletionOwnerId(),
377
+ artifactDirPreference: config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir,
378
+ ...config.authorityPolicy ? { authorityPolicy: config.authorityPolicy } : {},
379
+ ...config.missions ? { missionStoreConfig: config.missions } : {},
380
+ parentSessionFile: null,
381
+ trustedSessionRoots: [],
202
382
  subagentInProgress: false,
203
- subagentSpawns: { sessionId: null, count: 0 },
383
+ subagentSpawns: {
384
+ sessionId: null,
385
+ count: 0,
386
+ configuredLimit: resolveMaxSubagentSpawnsPerSession(config.maxSubagentSpawnsPerSession) ?? null,
387
+ granted: 0,
388
+ grantHistory: []
389
+ },
390
+ activeAsyncCapacity: { used: 0, limit: resolveMaxActiveAsyncRunsPerSession(config.maxActiveAsyncRunsPerSession) ?? 0 },
391
+ herdrProjectPanes: new Map,
204
392
  asyncJobs: new Map,
393
+ fleetJobs: new Map,
205
394
  foregroundRuns: new Map,
206
395
  foregroundControls: new Map,
207
396
  lastForegroundControlId: null,
208
- pendingForegroundControlNotices: new Map,
209
397
  cleanupTimers: new Map,
210
398
  lastUiContext: null,
211
399
  poller: null,
212
400
  completionSeen: new Map,
401
+ widgetsSuspended: false,
213
402
  watcher: null,
214
403
  watcherRestartTimer: null,
215
404
  resultFileCoalescer: {
@@ -217,60 +406,144 @@ export default function registerSubagentExtension(pi) {
217
406
  clear: () => {}
218
407
  }
219
408
  };
220
- const supervisorChannel = createNativeSupervisorChannel(pi, state);
221
- const { startResultWatcher, primeExistingResults, stopResultWatcher } = createResultWatcher(pi, state, RESULTS_DIR, 10 * 60 * 1000);
222
- startResultWatcher();
223
- primeExistingResults();
224
- const runtimeCleanup = () => {
225
- stopResultWatcher();
226
- scheduledRunManager.stop();
227
- supervisorChannel.dispose();
228
- clearPendingForegroundControlNotices(state);
229
- if (state.poller) {
230
- clearInterval(state.poller);
231
- state.poller = null;
232
- }
409
+ const withLastUiContext = (run) => {
410
+ const cached = state.lastUiContext;
411
+ return withCachedUiContext(cached, () => {
412
+ if (state.lastUiContext === cached)
413
+ state.lastUiContext = null;
414
+ }, run);
233
415
  };
234
- globalStore[runtimeCleanupStoreKey] = runtimeCleanup;
235
- const { ensurePoller, handleStarted, handleComplete, resetJobs, restoreActiveJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR);
236
- let executorExecute;
416
+ const supervisorChannel = createNativeSupervisorChannel(dm, state);
417
+ const waitSubscriptionManager = createWaitSubscriptionManager(dm, state);
418
+ const mainWatchdog = registerMainWatchdog(dm);
419
+ const resultDeliveryOwnership = createResultDeliveryOwnership(state);
420
+ const completionNotifier = registerSubagentNotify(dm, state, { batchConfig: config.completionBatch, ownership: resultDeliveryOwnership });
421
+ const fleetStatus = fleetViewEnabled ? new SubagentFleetStatus(state, async (itemKey) => {
422
+ const ctx = withLastUiContext((current) => current);
423
+ if (!ctx)
424
+ return;
425
+ try {
426
+ await openSubagentFleet(ctx, state, { initialKey: itemKey, asyncDirRoot: DIRS.async, resultsDir: DIRS.results, fleetKeybindings: config.fleetKeybindings });
427
+ } catch (error) {
428
+ if (isStaleExtensionContextError(error)) {
429
+ if (state.lastUiContext === ctx)
430
+ state.lastUiContext = null;
431
+ return;
432
+ }
433
+ throw error;
434
+ }
435
+ }, { placement: fleetViewPlacement }) : undefined;
436
+ let executorScheduled;
437
+ let goalTurnId = 0;
438
+ let parentSessionEnvValue = null;
439
+ const scheduledStoreRoot = config.scheduledRuns?.storeRoot === undefined ? undefined : resolveScheduledStoreRoot(config.scheduledRuns.storeRoot);
237
440
  const scheduledRunManager = createScheduledRunManager({
238
441
  config,
442
+ storeRoot: scheduledStoreRoot,
239
443
  launch: (params, ctx, signal) => {
240
- if (!executorExecute) {
444
+ if (!executorScheduled) {
241
445
  return Promise.resolve({
242
446
  content: [{ type: "text", text: "Scheduled subagent launch is unavailable (executor not ready)." }],
243
447
  isError: true,
244
448
  details: { mode: "management", results: [] }
245
449
  });
246
450
  }
247
- return executorExecute(randomUUID(), params, signal, undefined, ctx);
248
- }
451
+ return executorScheduled(randomUUID(), params, signal, ctx);
452
+ },
453
+ resolveCapabilityCeiling: (sessionId) => resolveCurrentSubagentCapabilityCeiling(sessionId)
454
+ });
455
+ let refreshResultDelivery = () => {};
456
+ const hasResultDeliveryDemand = () => {
457
+ if ([...state.asyncJobs.values()].some((job) => job.status === "queued" || job.status === "running"))
458
+ return true;
459
+ if (state.foregroundControls.size > 0)
460
+ return true;
461
+ if (scheduledRunManager.observedCompletionRunIds().size > 0)
462
+ return true;
463
+ return missionObserverResultCandidateFiles(DIRS.results).length > 0;
464
+ };
465
+ const discoverAgentsForRuntime = (cwd, scope, preferredModelProvider) => {
466
+ if (listRuntimeAgentConfigs(dm).length === 0)
467
+ return discoverAgents(cwd, scope, preferredModelProvider);
468
+ const snapshot = discoverAgentSnapshot(cwd, scope, preferredModelProvider, { includeChains: false });
469
+ const discovered = snapshot.effective;
470
+ const all = snapshot.all;
471
+ const configuredAgents = [
472
+ ...all.builtin,
473
+ ...all.package,
474
+ ...all.user,
475
+ ...all.project
476
+ ];
477
+ const merged = mergeRuntimeAgents(dm, discovered, configuredAgents);
478
+ if (discovered.maxThinking === undefined)
479
+ return merged;
480
+ return {
481
+ ...merged,
482
+ agents: merged.agents.map((agent) => agent.maxThinking === discovered.maxThinking ? agent : { ...agent, maxThinking: discovered.maxThinking })
483
+ };
484
+ };
485
+ const { ensurePoller, refreshWidget, handleStarted, handleComplete, resetJobs, restoreActiveJobs, dispose: disposeAsyncJobTracker } = createAsyncJobTracker(dm, state, DIRS.async, {
486
+ widgetEnabled: asyncWidgetEnabled,
487
+ onJobTerminal: () => refreshResultDelivery()
249
488
  });
489
+ const resultWatcher = createResultWatcher(dm, state, DIRS.results, 10 * 60 * 1000, {
490
+ notifier: completionNotifier,
491
+ ownership: resultDeliveryOwnership,
492
+ observeCompletion: (result) => scheduledRunManager.handleAsyncCompletion(result),
493
+ observedCompletionRunIds: () => scheduledRunManager.observedCompletionRunIds(),
494
+ hasDeliveryDemand: hasResultDeliveryDemand,
495
+ deliverIntercomResults: config.intercomBridge?.resultDelivery === true,
496
+ resultScanLogging: config.resultScanLogging
497
+ });
498
+ const { startResultWatcher, transitionResultDelivery, primeExistingResults, stopResultWatcher } = resultWatcher;
499
+ refreshResultDelivery = resultWatcher.refreshResultDelivery;
500
+ const asyncRetentionAbort = new AbortController;
501
+ const asyncRetentionTimer = setTimeout(async () => {
502
+ try {
503
+ await cleanupAsyncRetention({
504
+ asyncDirRoot: DIRS.async,
505
+ resultsDir: DIRS.results,
506
+ signal: asyncRetentionAbort.signal,
507
+ protectedRunIds: new Set([
508
+ ...state.asyncJobs.keys(),
509
+ ...state.workflowControllers?.keys() ?? [],
510
+ ...scheduledRunManager.referencedAsyncRunIds()
511
+ ])
512
+ });
513
+ } catch (error) {
514
+ console.error("Failed to clean retained async subagent state:", error);
515
+ }
516
+ }, ASYNC_RETENTION_DELAY_MS);
517
+ asyncRetentionTimer.unref?.();
250
518
  const executor = createSubagentExecutor({
251
- pi,
519
+ dm,
252
520
  state,
253
521
  config,
254
522
  asyncByDefault,
523
+ waitToolEnabled: waitToolConfig.enabled,
524
+ waitToolDefaultTimeoutMs: waitToolConfig.defaultTimeoutMs,
255
525
  handleScheduledRunAction: (params, ctx) => scheduledRunManager.handleToolCall(params, ctx),
526
+ watchdog: mainWatchdog,
256
527
  tempArtifactsDir,
257
528
  getSubagentSessionRoot,
258
529
  expandTilde,
259
- discoverAgents
530
+ discoverAgents: discoverAgentsForRuntime,
531
+ activateSupervisorTransport: () => supervisorChannel.activateTransport(),
532
+ refreshResultDelivery: () => refreshResultDelivery()
260
533
  });
261
- executorExecute = executor.execute;
262
- pi.registerMessageRenderer(SLASH_RESULT_TYPE, (message, options, theme) => {
534
+ executorScheduled = executor.executeScheduled;
535
+ dm.registerMessageRenderer(SLASH_RESULT_TYPE, (message, options, theme) => {
263
536
  const details = resolveSlashMessageDetails(message.details);
264
537
  if (!details)
265
538
  return;
266
- return createSlashResultComponent(details, options, theme);
539
+ return createSlashResultComponent(details, options, theme, config.mainWindowRenderer, config.foregroundDetachShortcut);
267
540
  });
268
- pi.registerMessageRenderer(SLASH_TEXT_RESULT_TYPE, (message, _options, _theme) => {
541
+ dm.registerMessageRenderer(SLASH_TEXT_RESULT_TYPE, (message, _options, _theme) => {
269
542
  const content = typeof message.content === "string" ? message.content : message.content.filter((entry) => entry.type === "text").map((entry) => entry.text).join(`
270
543
  `);
271
544
  return new Text(content, 0, 0);
272
545
  });
273
- pi.registerMessageRenderer("subagent-notify", (message, options, theme) => {
546
+ dm.registerMessageRenderer("subagent-notify", (message, options, theme) => {
274
547
  const content = typeof message.content === "string" ? message.content : "";
275
548
  const details = message.details ?? parseSubagentNotifyContent(content);
276
549
  if (!details)
@@ -297,6 +570,18 @@ export default function registerSubagentExtension(pi) {
297
570
  const expandKey = keyText("app.tools.expand");
298
571
  text += `
299
572
  ${theme.fg("dim", `${expandKey} full notification`)}`;
573
+ }
574
+ if (details.workflowRunId) {
575
+ text += `
576
+ ${theme.fg("muted", `workflow: ${details.workflowRunId}`)}`;
577
+ }
578
+ if (details.childRuns?.length) {
579
+ text += `
580
+ ${theme.fg("muted", `children: ${details.childRuns.map((child) => `${child.workflowKey ?? child.agent ?? "child"}=${child.runId}`).join(", ")}`)}`;
581
+ }
582
+ if (details.reconciledFromDetachedChild) {
583
+ text += `
584
+ ${theme.fg("muted", `reconciled child: ${details.reconciledFromDetachedChild}`)}`;
300
585
  }
301
586
  if (details.sessionLabel && details.sessionValue) {
302
587
  text += `
@@ -304,7 +589,13 @@ export default function registerSubagentExtension(pi) {
304
589
  }
305
590
  return new Text(text, 0, 0);
306
591
  });
307
- pi.registerMessageRenderer(SUBAGENT_CONTROL_MESSAGE_TYPE, (message, _options, theme) => {
592
+ dm.registerMessageRenderer(SUBAGENT_STEERING_MESSAGE_TYPE, (message, _options, theme) => {
593
+ const details = message.details;
594
+ if (!details)
595
+ return;
596
+ return new Text(theme.fg(details.state === "recovered" ? "warning" : "error", formatSteeringNotice(details)), 0, 0);
597
+ });
598
+ dm.registerMessageRenderer(SUBAGENT_CONTROL_MESSAGE_TYPE, (message, _options, theme) => {
308
599
  const details = message.details;
309
600
  if (!details?.event)
310
601
  return;
@@ -314,144 +605,148 @@ export default function registerSubagentExtension(pi) {
314
605
  const executeSubagentCollapsed = (id, params, signal, onUpdate, ctx) => {
315
606
  if (ctx.hasUI)
316
607
  ctx.ui.setToolsExpanded(false);
317
- return executor.execute(id, params, signal, onUpdate, ctx);
608
+ return executor.executePublic(id, params, signal, onUpdate, ctx);
318
609
  };
319
610
  const slashBridge = registerSlashSubagentBridge({
320
- events: pi.events,
611
+ events: dm.events,
321
612
  getContext: () => state.lastUiContext,
322
613
  execute: (id, params, signal, onUpdate, ctx) => executeSubagentCollapsed(id, params, signal, onUpdate, ctx)
323
614
  });
324
615
  const promptTemplateBridge = registerPromptTemplateDelegationBridge({
325
- events: pi.events,
616
+ events: dm.events,
326
617
  getContext: () => state.lastUiContext,
327
- execute: async (requestId, request, signal, ctx, onUpdate) => {
328
- if (request.tasks && request.tasks.length > 0) {
329
- return executeSubagentCollapsed(requestId, {
330
- tasks: request.tasks,
331
- context: request.context,
332
- cwd: request.cwd,
333
- worktree: request.worktree,
334
- async: false,
335
- clarify: false
336
- }, signal, onUpdate, ctx);
337
- }
338
- return executeSubagentCollapsed(requestId, {
339
- agent: request.agent,
340
- task: request.task,
341
- context: request.context,
342
- cwd: request.cwd,
343
- model: request.model,
344
- async: false,
345
- clarify: false
346
- }, signal, onUpdate, ctx);
618
+ execute: (requestId, params, signal, ctx, onUpdate) => executeSubagentCollapsed(requestId, params, signal, onUpdate, ctx),
619
+ executeStructured: (requestId, params, signal, ctx, onUpdate) => {
620
+ if (ctx.hasUI)
621
+ ctx.ui.setToolsExpanded(false);
622
+ return executor.executeDelegated(requestId, params, signal, onUpdate, ctx);
347
623
  }
348
624
  });
349
625
  const rpcBridge = registerSubagentRpcBridge({
350
- events: pi.events,
626
+ events: dm.events,
351
627
  getContext: () => state.lastUiContext,
352
- execute: (id, params, signal, onUpdate, ctx) => executor.execute(id, params, signal, onUpdate, ctx)
628
+ execute: (id, params, signal, onUpdate, ctx) => executor.executePublic(id, params, signal, onUpdate, ctx),
629
+ state
353
630
  });
354
- function effectiveParallelTaskCount(tasks) {
355
- if (!tasks || tasks.length === 0)
356
- return 0;
357
- return tasks.reduce((total, task) => {
358
- const count = typeof task.count === "number" && Number.isInteger(task.count) && task.count >= 1 ? task.count : 1;
359
- return total + count;
360
- }, 0);
361
- }
631
+ const parameters = createSubagentParamsSchema();
362
632
  const tool = {
363
633
  name: "subagent",
364
634
  label: "Subagent",
365
635
  description: buildSubagentToolDescription(config),
366
- parameters: SubagentParams,
367
- execute(id, params, signal, onUpdate, ctx) {
368
- return executeSubagentCollapsed(id, params, signal, onUpdate, ctx);
636
+ ...buildSubagentToolPromptMetadata(config),
637
+ parameters,
638
+ async execute(id, params, signal, onUpdate, ctx) {
639
+ return finalizeToolResult(await executeSubagentCollapsed(id, params, signal ?? new AbortController().signal, onUpdate, ctx));
369
640
  },
370
641
  renderCall(args, theme) {
642
+ const gap = " ".repeat(config.mainWindowRenderer?.horizontalSpacing ?? 1);
643
+ const title = theme.fg("toolTitle", theme.bold("subagent"));
371
644
  if (args.action) {
372
- const target = args.agent || args.chainName || "";
373
- return new Text(`${theme.fg("toolTitle", theme.bold("subagent "))}${args.action}${target ? ` ${theme.fg("accent", target)}` : ""}`, 0, 0);
645
+ const target = args.agent || "";
646
+ return new Text(`${title}${gap}${args.action}${target ? `${gap}${theme.fg("accent", target)}` : ""}`, 0, 0);
374
647
  }
375
- const isParallel = (args.tasks?.length ?? 0) > 0;
376
- const parallelCount = effectiveParallelTaskCount(args.tasks);
377
- const asyncLabel = args.async === true && args.clarify !== true ? theme.fg("warning", " [async]") : "";
378
- if (args.chain?.length)
379
- return new Text(`${theme.fg("toolTitle", theme.bold("subagent "))}chain (${args.chain.length})${asyncLabel}`, 0, 0);
380
- if (isParallel)
381
- return new Text(`${theme.fg("toolTitle", theme.bold("subagent "))}parallel (${parallelCount})${asyncLabel}`, 0, 0);
382
- return new Text(`${theme.fg("toolTitle", theme.bold("subagent "))}${theme.fg("accent", args.agent || "?")}${asyncLabel}`, 0, 0);
648
+ if (args.workflowScript)
649
+ return new Text(`${title}${gap}${formatWorkflowManifest(args.workflowScript, args.async, false, args.preflight)}`, 0, 0);
650
+ if (args.workflowScriptPath)
651
+ return new Text(`${title}${gap}${theme.fg("accent", args.workflowScriptPath)}${args.async === true ? `${gap}${theme.fg("warning", "[async]")}` : ""}${args.preflight !== undefined ? `${gap}${theme.fg("dim", formatWorkflowPreflightCall(args.preflight))}` : ""}`, 0, 0);
652
+ const asyncLabel = args.async === true ? `${gap}${theme.fg("warning", "[async]")}` : "";
653
+ return new Text(`${title}${gap}${theme.fg("accent", args.agent || "?")}${asyncLabel}`, 0, 0);
383
654
  },
384
655
  renderResult(result, options, theme, context) {
385
- if (subagentResultIsRunning(result)) {
386
- ensureSubagentResultAnimation(context);
387
- } else {
388
- clearLegacyResultAnimationTimer(context);
389
- }
390
- const frame = context.state?.frame ?? 0;
391
- return renderSubagentResult(result, options, theme, frame);
656
+ clearLegacyResultAnimationTimer(context);
657
+ const renderedResult = { ...result, isError: context.isError };
658
+ return summaryInlineToolDisplay ? renderSubagentSummary(renderedResult, options, theme) : renderSubagentResult(renderedResult, options, theme, undefined, config.mainWindowRenderer, config.foregroundDetachShortcut);
392
659
  }
393
660
  };
394
- pi.registerTool(tool);
395
- const waitTool = {
396
- name: "wait",
397
- label: "Wait",
398
- description: `Block until background (async) subagent runs started in this session finish, then return.
399
-
400
- Use this after launching async subagents when you have no independent work left and must not end your turn — for example inside a skill that has to run to completion, or any non-interactive run (\`dm --print ...\`) where the whole task is a single turn and ending it would abandon the still-running children.
401
-
402
- • { } — return as soon as the FIRST active run finishes (default). Ideal for a rolling fleet: launch N, wait, spawn a replacement for the one that finished, wait again — keeping N in flight.
403
- • { all: true } — block until EVERY active run in this session is finished.
404
- • { id: "..." } — wait for one specific run (id or prefix) to finish.
405
- • { timeoutMs: 600000 } — stop waiting after N ms (the runs keep going regardless; default 30 min)
406
-
407
- wait also returns when a run needs attention (a child that went idle or blocked for a decision), not only on completion — so a stuck child never stalls the loop; the summary names the run(s) to inspect/nudge/resume/interrupt. It wakes the instant a completion or control event arrives (subscribed to DM's event bus, with a poll fallback that reconciles crashed runners), keeps the turn alive for normal notification delivery, and resolves early if the turn is aborted.`,
408
- parameters: WaitParams,
409
- execute(_id, params, signal, _onUpdate, _ctx) {
410
- return waitForSubagents(params, signal, { state, events: pi.events });
661
+ dm.registerTool(tool);
662
+ registerWaitTool(dm, state, waitToolConfig.enabled, waitSubscriptionManager, waitToolConfig.defaultTimeoutMs);
663
+ dm.on("agent_end", async (_event, ctx) => {
664
+ if (!ctx.hasUI)
665
+ await drainOutstandingWork({ state, events: dm.events });
666
+ const ownerSessionId = state.currentSessionId;
667
+ if (!ownerSessionId)
668
+ return;
669
+ goalTurnId += 1;
670
+ try {
671
+ const location = resolveMissionStoreLocation({ projectRoot: state.baseCwd, ...config.missions ? { config: config.missions } : {} });
672
+ const retainedChildren = listRetainedChildren(DIRS.async, ownerSessionId);
673
+ for (const notice of collectGoalContinuationNotices({ location, ownerSessionId, retainedChildren, turnId: goalTurnId })) {
674
+ handleSubagentControlNotice({
675
+ dm,
676
+ state,
677
+ visibleControlNotices: new Set,
678
+ details: { source: "goal", event: notice.event, noticeText: notice.message }
679
+ });
680
+ }
681
+ } catch (error) {
682
+ console.error("Failed to evaluate goal missions:", error);
411
683
  }
412
- };
413
- pi.registerTool(waitTool);
414
- registerSlashCommands(pi, state);
415
- const eventUnsubscribeStoreKey = "__piSubagentEventUnsubscribes";
416
- const controlNoticeSeenStoreKey = "__piSubagentVisibleControlNotices";
417
- const previousEventUnsubscribes = globalStore[eventUnsubscribeStoreKey];
418
- if (Array.isArray(previousEventUnsubscribes)) {
419
- for (const unsubscribe of previousEventUnsubscribes) {
420
- if (typeof unsubscribe !== "function")
421
- continue;
422
- try {
423
- unsubscribe();
424
- } catch {}
684
+ });
685
+ const disposeSlashCommands = registerSlashCommands(dm, state, {
686
+ fleetKeybindings: config.fleetKeybindings,
687
+ foregroundDetachShortcut: config.foregroundDetachShortcut
688
+ });
689
+ let visibleControlNotices = new Set;
690
+ const activeHerdrRuns = () => projectActiveHerdrRuns(state);
691
+ const herdrStatusBridge = registerHerdrStatusBridge({
692
+ events: dm.events,
693
+ getRuns: activeHerdrRuns,
694
+ getProjectPaneCount: () => [...state.herdrProjectPanes?.values() ?? []].filter((pane) => pane.state === "open").length,
695
+ async runHerdr(args) {
696
+ await dm.exec(process.env.HERDR_BIN || "herdr", [...args], { timeout: 5000 });
425
697
  }
426
- }
427
- registerSubagentNotify(pi, state, { batchConfig: config.completionBatch });
428
- const existingVisibleControlNotices = globalStore[controlNoticeSeenStoreKey];
429
- const visibleControlNotices = existingVisibleControlNotices instanceof Set ? existingVisibleControlNotices : new Set;
430
- globalStore[controlNoticeSeenStoreKey] = visibleControlNotices;
698
+ });
431
699
  const controlEventHandler = (payload) => {
432
700
  handleSubagentControlNotice({
433
- pi,
701
+ dm,
434
702
  state,
435
703
  visibleControlNotices,
436
704
  details: payload
437
705
  });
438
706
  };
707
+ const steeringNoticeHandler = (payload) => {
708
+ handleSubagentSteeringNotice({ dm, state, details: payload });
709
+ };
710
+ const asyncStartedHandler = (payload) => {
711
+ handleStarted(payload);
712
+ supervisorChannel.activateTransport();
713
+ refreshResultDelivery();
714
+ fleetStatus?.refresh();
715
+ };
716
+ const asyncCompleteHandler = (payload) => {
717
+ handleComplete(payload);
718
+ refreshResultDelivery();
719
+ refreshActiveAsyncCapacity();
720
+ scheduledRunManager.handleAsyncCompletion(payload);
721
+ fleetStatus?.refresh();
722
+ };
439
723
  const eventUnsubscribes = [
440
- pi.events.on(SUBAGENT_ASYNC_STARTED_EVENT, handleStarted),
441
- pi.events.on(SUBAGENT_ASYNC_COMPLETE_EVENT, handleComplete),
442
- pi.events.on(SUBAGENT_CONTROL_EVENT, controlEventHandler),
724
+ registerRuntimeAgentEventListener(dm),
725
+ dm.events.on(SUBAGENT_ASYNC_STARTED_EVENT, asyncStartedHandler),
726
+ dm.events.on(SUBAGENT_ASYNC_COMPLETE_EVENT, asyncCompleteHandler),
727
+ dm.events.on(SUBAGENT_PROCESS_TERMINAL_EVENT, () => {
728
+ refreshActiveAsyncCapacity();
729
+ fleetStatus?.refresh();
730
+ }),
731
+ dm.events.on(SUBAGENT_CONTROL_EVENT, controlEventHandler),
732
+ dm.events.on(SUBAGENT_STEERING_NOTICE_EVENT, steeringNoticeHandler),
733
+ herdrStatusBridge.dispose,
443
734
  rpcBridge.dispose
444
735
  ];
445
- globalStore[eventUnsubscribeStoreKey] = eventUnsubscribes;
446
- pi.on("tool_result", (event, ctx) => {
736
+ dm.on("tool_result", (event, ctx) => {
447
737
  if (event.toolName !== "subagent")
448
738
  return;
449
739
  if (!ctx.hasUI)
450
740
  return;
451
741
  state.lastUiContext = ctx;
742
+ const activeJobCount = state.asyncJobs.size;
743
+ restoreActiveJobs(ctx);
744
+ if (state.asyncJobs.size > activeJobCount)
745
+ herdrStatusBridge.syncRuns();
746
+ fleetStatus?.setContext(ctx);
747
+ fleetStatus?.refresh();
452
748
  if (state.asyncJobs.size > 0) {
453
- renderWidget(ctx, Array.from(state.asyncJobs.values()));
454
- ctx.ui.requestRender?.();
749
+ refreshWidget(ctx);
455
750
  ensurePoller();
456
751
  }
457
752
  });
@@ -459,71 +754,222 @@ wait also returns when a run needs attention (a child that went idle or blocked
459
754
  try {
460
755
  const sessionFile = ctx.sessionManager.getSessionFile();
461
756
  if (sessionFile) {
462
- cleanupOldArtifacts(getArtifactsDir(sessionFile), DEFAULT_ARTIFACT_CONFIG.cleanupDays);
757
+ cleanupOldArtifacts(getArtifactsDir(sessionFile), artifactCleanupDays);
463
758
  }
464
759
  } catch {}
465
760
  };
466
- const resetSessionState = (ctx) => {
761
+ const suspendWidgetsForCompaction = () => {
762
+ if (state.widgetsSuspended)
763
+ return;
764
+ state.widgetsSuspended = true;
765
+ withLastUiContext((ctx) => ctx.ui.setWidget(WIDGET_KEY, undefined));
766
+ fleetStatus?.refresh();
767
+ };
768
+ const resumeWidgetsAfterCompaction = () => {
769
+ if (!state.widgetsSuspended)
770
+ return;
771
+ state.widgetsSuspended = false;
772
+ withLastUiContext((ctx) => refreshWidget(ctx));
773
+ fleetStatus?.refresh();
774
+ };
775
+ const refreshActiveAsyncCapacity = () => {
776
+ if (!state.currentSessionId) {
777
+ state.activeAsyncCapacity = { used: 0, limit: resolveMaxActiveAsyncRunsPerSession(config.maxActiveAsyncRunsPerSession) ?? 0 };
778
+ return;
779
+ }
780
+ state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(state.currentSessionId, resolveMaxActiveAsyncRunsPerSession(config.maxActiveAsyncRunsPerSession), { liveWorkflowRunIds: new Set(state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(config.capacity?.abandonedSlotReleaseAfterMs) });
781
+ };
782
+ const resetSessionState = (ctx, recovering, previousSessionFile) => {
783
+ state.widgetsSuspended = false;
467
784
  state.baseCwd = ctx.cwd;
785
+ goalTurnId = 0;
786
+ const previousRuntimeSessionId = state.currentSessionId;
787
+ resultDeliveryOwnership.claimPredecessor(previousSessionFile, previousRuntimeSessionId);
468
788
  state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
469
- state.subagentSpawns = { sessionId: state.currentSessionId, count: 0 };
789
+ transitionResultDelivery();
790
+ state.parentSessionFile = ctx.sessionManager.getSessionFile();
791
+ state.trustedSessionFileRoot = state.parentSessionFile ? path.join(getAgentDir(), "sessions") : undefined;
792
+ state.trustedSessionRoots = [...new Set([
793
+ ...config.defaultSessionDir ? [path.resolve(expandTilde(config.defaultSessionDir))] : [],
794
+ ...state.parentSessionFile ? [getSubagentSessionRoot(state.parentSessionFile)] : []
795
+ ])];
796
+ state.subagentSpawns = {
797
+ sessionId: state.currentSessionId,
798
+ count: 0,
799
+ configuredLimit: resolveMaxSubagentSpawnsPerSession(config.maxSubagentSpawnsPerSession) ?? null,
800
+ granted: 0,
801
+ grantHistory: []
802
+ };
803
+ const projectPaneOwnerRoot = path.resolve(ctx.cwd);
804
+ restoreHerdrProjectPaneSnapshots(state, [...new Set([...state.herdrProjectPanes?.keys() ?? [], ...listHerdrProjectPaneRoots(projectPaneOwnerRoot), projectPaneOwnerRoot])]);
470
805
  if (!process.env[SUBAGENT_CHILD_ENV]) {
471
806
  const sessionId = ctx.sessionManager.getSessionId();
472
807
  if (sessionId) {
473
808
  process.env[SUBAGENT_PARENT_SESSION_ENV] = sessionId;
809
+ parentSessionEnvValue = sessionId;
474
810
  }
475
811
  }
476
812
  state.lastUiContext = ctx;
813
+ let phaseStartedAt = Date.now();
814
+ refreshActiveAsyncCapacity();
815
+ logSlowPhase("active-capacity", phaseStartedAt);
816
+ phaseStartedAt = Date.now();
477
817
  cleanupSessionArtifacts(ctx);
478
- clearPendingForegroundControlNotices(state);
818
+ logSlowPhase("session-artifact-cleanup", phaseStartedAt);
819
+ state.foregroundControls.clear();
820
+ state.lastForegroundControlId = null;
821
+ phaseStartedAt = Date.now();
479
822
  resetJobs(ctx);
823
+ logSlowPhase("reset-jobs", phaseStartedAt);
824
+ phaseStartedAt = Date.now();
825
+ restoreForegroundRunHistory(state, { resultsDir: DIRS.results });
826
+ logSlowPhase("foreground-history", phaseStartedAt);
827
+ phaseStartedAt = Date.now();
480
828
  restoreActiveJobs(ctx);
829
+ logSlowPhase("active-job-restore", phaseStartedAt);
830
+ phaseStartedAt = Date.now();
481
831
  scheduledRunManager.bindSession(ctx);
832
+ logSlowPhase("scheduled-runs", phaseStartedAt);
833
+ phaseStartedAt = Date.now();
482
834
  restoreSlashFinalSnapshots(ctx.sessionManager.getEntries());
483
- primeExistingResults();
835
+ logSlowPhase("slash-snapshots", phaseStartedAt);
836
+ phaseStartedAt = Date.now();
837
+ waitSubscriptionManager.restore();
838
+ logSlowPhase("wait-subscriptions", phaseStartedAt);
839
+ phaseStartedAt = Date.now();
840
+ startResultWatcher();
841
+ logSlowPhase("result-watcher-start", phaseStartedAt);
842
+ phaseStartedAt = Date.now();
843
+ primeExistingResults({ triggerTurn: !recovering });
844
+ logSlowPhase("result-prime", phaseStartedAt);
845
+ fleetStatus?.setContext(ctx);
484
846
  };
485
- pi.on("session_start", (_event, ctx) => {
486
- resetSessionState(ctx);
487
- rpcBridge.emitReady(ctx);
488
- supervisorChannel.start();
489
- });
490
- pi.on("session_shutdown", () => {
491
- delete process.env[SUBAGENT_PARENT_SESSION_ENV];
492
- for (const unsubscribe of eventUnsubscribes) {
847
+ let runtimeCleaned = false;
848
+ const runtimeEntry = {
849
+ sessionManager: null,
850
+ visibleControlNotices,
851
+ cleanup() {
852
+ if (runtimeCleaned)
853
+ return;
854
+ runtimeCleaned = true;
855
+ const shuttingDownParentSession = parentSessionEnvValue;
856
+ for (const controller of state.workflowControllers?.values() ?? []) {
857
+ if (!controller.signal.aborted)
858
+ controller.abort(new Error("Workflow stopped because the extension session was replaced or reloaded."));
859
+ }
860
+ state.workflowControllers?.clear();
861
+ state.workflowChildStops?.clear();
862
+ clearRuntimeAgentsForPi(dm);
863
+ clearTimeout(resultIndexCleanupTimer);
864
+ clearTimeout(asyncRetentionTimer);
865
+ asyncRetentionAbort.abort();
866
+ stopResultWatcher();
867
+ resultDeliveryOwnership.clear();
868
+ completionNotifier.dispose();
869
+ mainWatchdog.dispose();
870
+ scheduledRunManager.stop();
871
+ supervisorChannel.dispose();
872
+ waitSubscriptionManager.dispose();
873
+ fleetStatus?.dispose();
874
+ disposeAsyncJobTracker();
875
+ for (const timer of state.cleanupTimers.values())
876
+ clearTimeout(timer);
877
+ state.cleanupTimers.clear();
878
+ state.asyncJobs.clear();
879
+ for (const unsubscribe of eventUnsubscribes) {
880
+ try {
881
+ unsubscribe();
882
+ } catch {}
883
+ }
884
+ disposeSlashCommands.dispose();
885
+ slashBridge.cancelAll();
886
+ slashBridge.dispose();
887
+ promptTemplateBridge.cancelAll();
888
+ promptTemplateBridge.dispose();
889
+ state.widgetsSuspended = false;
890
+ state.currentSessionId = null;
891
+ state.statusProjectionSessionId = null;
892
+ state.parentSessionFile = null;
893
+ parentSessionEnvValue = null;
894
+ if (shuttingDownParentSession && process.env[SUBAGENT_PARENT_SESSION_ENV] === shuttingDownParentSession) {
895
+ delete process.env[SUBAGENT_PARENT_SESSION_ENV];
896
+ }
897
+ if (runtimeEntry.sessionManager && runtimeRegistry.bySessionManager.get(runtimeEntry.sessionManager) === runtimeEntry) {
898
+ runtimeRegistry.bySessionManager.delete(runtimeEntry.sessionManager);
899
+ }
900
+ runtimeRegistry.activeEntries.delete(runtimeEntry);
901
+ if (runtimeRegistry.activeEntries.size === 0)
902
+ clearSlashSnapshots();
493
903
  try {
494
- unsubscribe();
495
- } catch {}
904
+ if (state.lastUiContext?.hasUI)
905
+ state.lastUiContext.ui.setWidget(WIDGET_KEY, undefined);
906
+ } catch (error) {
907
+ if (!isStaleExtensionContextError(error))
908
+ throw error;
909
+ }
910
+ state.lastUiContext = null;
496
911
  }
497
- if (globalStore[eventUnsubscribeStoreKey] === eventUnsubscribes) {
498
- delete globalStore[eventUnsubscribeStoreKey];
499
- }
500
- stopResultWatcher();
501
- scheduledRunManager.stop();
502
- if (state.poller)
503
- clearInterval(state.poller);
504
- state.poller = null;
505
- clearPendingForegroundControlNotices(state);
506
- for (const timer of state.cleanupTimers.values()) {
507
- clearTimeout(timer);
508
- }
509
- state.cleanupTimers.clear();
510
- state.asyncJobs.clear();
511
- clearSlashSnapshots();
512
- slashBridge.cancelAll();
513
- slashBridge.dispose();
514
- promptTemplateBridge.cancelAll();
515
- promptTemplateBridge.dispose();
516
- supervisorChannel.dispose();
517
- if (globalStore[runtimeCleanupStoreKey] === runtimeCleanup) {
518
- delete globalStore[runtimeCleanupStoreKey];
912
+ };
913
+ const installRuntime = (ctx) => {
914
+ if (runtimeCleaned) {
915
+ throw new Error("Cannot restart a cleaned dm-subagents extension runtime; register a new extension instance.");
519
916
  }
520
- try {
521
- if (state.lastUiContext?.hasUI) {
522
- state.lastUiContext.ui.setWidget(WIDGET_KEY, undefined);
523
- }
524
- } catch (error) {
525
- if (!isStaleExtensionContextError(error))
526
- throw error;
917
+ const sessionManager = ctx.sessionManager;
918
+ const previousRuntime = runtimeRegistry.bySessionManager.get(sessionManager);
919
+ const existingVisibleControlNotices = runtimeRegistry.visibleControlNoticesBySessionManager.get(sessionManager);
920
+ if (existingVisibleControlNotices) {
921
+ visibleControlNotices = existingVisibleControlNotices;
922
+ } else {
923
+ runtimeRegistry.visibleControlNoticesBySessionManager.set(sessionManager, visibleControlNotices);
527
924
  }
925
+ runtimeEntry.visibleControlNotices = visibleControlNotices;
926
+ if (runtimeEntry.sessionManager && runtimeEntry.sessionManager !== sessionManager && runtimeRegistry.bySessionManager.get(runtimeEntry.sessionManager) === runtimeEntry) {
927
+ runtimeRegistry.bySessionManager.delete(runtimeEntry.sessionManager);
928
+ }
929
+ runtimeEntry.sessionManager = sessionManager;
930
+ runtimeRegistry.bySessionManager.set(sessionManager, runtimeEntry);
931
+ runtimeRegistry.activeEntries.add(runtimeEntry);
932
+ if (previousRuntime && previousRuntime !== runtimeEntry) {
933
+ try {
934
+ previousRuntime.cleanup();
935
+ } catch {}
936
+ }
937
+ };
938
+ dm.on("agent_start", () => {
939
+ resumeWidgetsAfterCompaction();
940
+ herdrStatusBridge.agentStarted();
941
+ });
942
+ dm.on("agent_end", () => {
943
+ resumeWidgetsAfterCompaction();
944
+ });
945
+ dm.on("session_before_compact", (event) => {
946
+ if (event.reason !== "manual")
947
+ suspendWidgetsForCompaction();
948
+ });
949
+ dm.on("session_compact", () => {
950
+ const hasActiveAsyncWork = [...state.asyncJobs.values()].some((job) => job.status === "queued" || job.status === "running");
951
+ if (!hasActiveAsyncWork || !withLastUiContext(() => true))
952
+ return;
953
+ dm.sendMessage({
954
+ customType: "subagent-compaction-resume",
955
+ content: "Compaction is complete. Resume the parent task now; background subagent results will arrive separately when ready.",
956
+ display: false
957
+ }, { triggerTurn: true });
958
+ });
959
+ dm.on("session_start", (event, ctx) => {
960
+ installRuntime(ctx);
961
+ const recovering = event.reason === "startup" || event.reason === "reload" || event.reason === "resume";
962
+ resetSessionState(ctx, recovering, event.previousSessionFile);
963
+ herdrStatusBridge.sessionStarted({
964
+ hasUI: ctx.hasUI === true,
965
+ runs: activeHerdrRuns()
966
+ });
967
+ rpcBridge.emitReady(ctx);
968
+ supervisorChannel.start();
969
+ supervisorChannel.activateTransport();
970
+ });
971
+ dm.on("session_shutdown", async () => {
972
+ runtimeEntry.cleanup();
973
+ await herdrStatusBridge.flush();
528
974
  });
529
975
  }