@duckmind/dm-windows-x64 0.61.4 → 0.61.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +211 -67
  3. package/extensions/dm-subagents/agents/claude-code-writer.md +15 -0
  4. package/extensions/dm-subagents/agents/claude-code.md +15 -0
  5. package/extensions/dm-subagents/agents/codex-exec-writer.md +15 -0
  6. package/extensions/dm-subagents/agents/codex-exec.md +15 -0
  7. package/extensions/dm-subagents/agents/cursor-agent-writer.md +14 -0
  8. package/extensions/dm-subagents/agents/cursor-agent.md +14 -0
  9. package/extensions/dm-subagents/agents/delegate.md +3 -2
  10. package/extensions/dm-subagents/agents/oracle.md +10 -5
  11. package/extensions/dm-subagents/agents/researcher.md +2 -2
  12. package/extensions/dm-subagents/agents/reviewer.md +17 -7
  13. package/extensions/dm-subagents/agents/scout.md +5 -5
  14. package/extensions/dm-subagents/agents/worker.md +6 -2
  15. package/extensions/dm-subagents/async-retention-discovery-worker.mjs +167 -0
  16. package/extensions/dm-subagents/index.js +4 -0
  17. package/extensions/dm-subagents/inspector-runner.mjs +10 -0
  18. package/extensions/dm-subagents/install.mjs +3 -2
  19. package/extensions/dm-subagents/package.json +2 -2
  20. package/extensions/dm-subagents/prompts/council.md +60 -0
  21. package/extensions/dm-subagents/prompts/parallel-review.md +5 -1
  22. package/extensions/dm-subagents/prompts/review-loop.md +13 -7
  23. package/extensions/dm-subagents/skills/council-mode/SKILL.md +59 -0
  24. package/extensions/dm-subagents/skills/council-mode/references/pass-contracts.md +150 -0
  25. package/extensions/dm-subagents/skills/dm-subagents/SKILL.md +96 -911
  26. package/extensions/dm-subagents/skills/dm-subagents/references/constraints-and-recipes.md +70 -0
  27. package/extensions/dm-subagents/skills/dm-subagents/references/execution-controls.md +539 -0
  28. package/extensions/dm-subagents/skills/dm-subagents/references/management-authoring-rpc.md +161 -0
  29. package/extensions/dm-subagents/skills/dm-subagents/references/multi-lane-orchestration.md +51 -0
  30. package/extensions/dm-subagents/skills/dm-subagents/references/prompting-and-roles.md +295 -0
  31. package/extensions/dm-subagents/skills/dm-subagents/references/review-and-validation.md +73 -0
  32. package/extensions/dm-subagents/src/agents/agent-management.js +716 -484
  33. package/extensions/dm-subagents/src/agents/agent-refinements.js +563 -0
  34. package/extensions/dm-subagents/src/agents/agent-serializer.js +70 -5
  35. package/extensions/dm-subagents/src/agents/agents.js +1447 -299
  36. package/extensions/dm-subagents/src/agents/builtin-names.js +15 -0
  37. package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -7
  38. package/extensions/dm-subagents/src/agents/frontmatter.js +64 -14
  39. package/extensions/dm-subagents/src/agents/identity.js +1 -1
  40. package/extensions/dm-subagents/src/agents/proactive-skills.js +14 -11
  41. package/extensions/dm-subagents/src/agents/runtime-agent-events.js +49 -0
  42. package/extensions/dm-subagents/src/agents/runtime-agent-registry.js +412 -0
  43. package/extensions/dm-subagents/src/agents/skills.js +52 -35
  44. package/extensions/dm-subagents/src/api/agents.js +6 -0
  45. package/extensions/dm-subagents/src/api/background-work.js +151 -0
  46. package/extensions/dm-subagents/src/api/capability-ceiling.js +12 -0
  47. package/extensions/dm-subagents/src/api/control-channel.js +3 -0
  48. package/extensions/dm-subagents/src/api/delegation.js +5 -0
  49. package/extensions/dm-subagents/src/api/dm-args.js +3 -0
  50. package/extensions/dm-subagents/src/api/external-job-provider.js +137 -0
  51. package/extensions/dm-subagents/src/api/external-runs.js +233 -0
  52. package/extensions/dm-subagents/src/api/intercom-bridge.js +3 -0
  53. package/extensions/dm-subagents/src/api/preflight.js +322 -0
  54. package/extensions/dm-subagents/src/api/project-panes.js +11 -0
  55. package/extensions/dm-subagents/src/api/shared-types.js +4 -0
  56. package/extensions/dm-subagents/src/extension/config.js +175 -0
  57. package/extensions/dm-subagents/src/extension/control-notices.js +4 -43
  58. package/extensions/dm-subagents/src/extension/doctor.js +71 -17
  59. package/extensions/dm-subagents/src/extension/fanout-child.js +49 -32
  60. package/extensions/dm-subagents/src/extension/index.js +711 -265
  61. package/extensions/dm-subagents/src/extension/public-execution.js +114 -0
  62. package/extensions/dm-subagents/src/extension/rpc.js +427 -22
  63. package/extensions/dm-subagents/src/extension/schemas.js +158 -65
  64. package/extensions/dm-subagents/src/extension/steering-notices.js +23 -0
  65. package/extensions/dm-subagents/src/extension/subagent-guide.js +31 -0
  66. package/extensions/dm-subagents/src/extension/tool-description.js +92 -74
  67. package/extensions/dm-subagents/src/extension/tool-result.js +7 -0
  68. package/extensions/dm-subagents/src/inspectors/herdr/actions.js +218 -0
  69. package/extensions/dm-subagents/src/inspectors/herdr/client.js +123 -0
  70. package/extensions/dm-subagents/src/inspectors/herdr/focus.js +47 -0
  71. package/extensions/dm-subagents/src/inspectors/herdr/inspector-runner.js +160 -0
  72. package/extensions/dm-subagents/src/inspectors/herdr/project-panes.js +618 -0
  73. package/extensions/dm-subagents/src/inspectors/herdr/session-roots-codec.js +21 -0
  74. package/extensions/dm-subagents/src/inspectors/herdr/shell-command.js +15 -0
  75. package/extensions/dm-subagents/src/integrations/herdr-status.js +377 -0
  76. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +17 -13
  77. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +371 -79
  78. package/extensions/dm-subagents/src/intercom/result-intercom.js +47 -7
  79. package/extensions/dm-subagents/src/missions/actions.js +394 -0
  80. package/extensions/dm-subagents/src/missions/goal-driver.js +149 -0
  81. package/extensions/dm-subagents/src/missions/lifecycle.js +331 -0
  82. package/extensions/dm-subagents/src/missions/store.js +548 -0
  83. package/extensions/dm-subagents/src/missions/types.js +9 -0
  84. package/extensions/dm-subagents/src/missions/workflow-state.js +245 -0
  85. package/extensions/dm-subagents/src/policy/authority.js +37 -0
  86. package/extensions/dm-subagents/src/profiles/profiles.js +36 -18
  87. package/extensions/dm-subagents/src/runs/background/active-async-capacity.js +427 -0
  88. package/extensions/dm-subagents/src/runs/background/active-run-index.js +122 -0
  89. package/extensions/dm-subagents/src/runs/background/async-execution.js +925 -137
  90. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +515 -148
  91. package/extensions/dm-subagents/src/runs/background/async-resume.js +378 -51
  92. package/extensions/dm-subagents/src/runs/background/async-retention.js +828 -0
  93. package/extensions/dm-subagents/src/runs/background/async-status-snapshot.js +31 -0
  94. package/extensions/dm-subagents/src/runs/background/async-status.js +259 -22
  95. package/extensions/dm-subagents/src/runs/background/auto-drain.js +46 -0
  96. package/extensions/dm-subagents/src/runs/background/chain-append.js +50 -15
  97. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +67 -12
  98. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +5 -1
  99. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +3 -11
  100. package/extensions/dm-subagents/src/runs/background/completion-replay.js +245 -0
  101. package/extensions/dm-subagents/src/runs/background/control-channel.js +423 -36
  102. package/extensions/dm-subagents/src/runs/background/fleet-view.js +132 -59
  103. package/extensions/dm-subagents/src/runs/background/index-segment.js +38 -0
  104. package/extensions/dm-subagents/src/runs/background/inspect-rpc.js +373 -0
  105. package/extensions/dm-subagents/src/runs/background/notify.js +357 -57
  106. package/extensions/dm-subagents/src/runs/background/owned-process-tree.js +86 -0
  107. package/extensions/dm-subagents/src/runs/background/process-terminal.js +269 -0
  108. package/extensions/dm-subagents/src/runs/background/result-delivery-ownership.js +34 -0
  109. package/extensions/dm-subagents/src/runs/background/result-files.js +469 -0
  110. package/extensions/dm-subagents/src/runs/background/result-watcher.js +540 -75
  111. package/extensions/dm-subagents/src/runs/background/resume-guidance.js +44 -0
  112. package/extensions/dm-subagents/src/runs/background/retained-children.js +119 -0
  113. package/extensions/dm-subagents/src/runs/background/run-id-query.js +5 -0
  114. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +93 -9
  115. package/extensions/dm-subagents/src/runs/background/run-status.js +303 -32
  116. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +784 -376
  117. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +75 -34
  118. package/extensions/dm-subagents/src/runs/background/steering.js +221 -0
  119. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +3106 -844
  120. package/extensions/dm-subagents/src/runs/background/subagent-wait.js +529 -0
  121. package/extensions/dm-subagents/src/runs/background/terminal-run-index.js +106 -0
  122. package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -1
  123. package/extensions/dm-subagents/src/runs/background/wait-completions.js +155 -0
  124. package/extensions/dm-subagents/src/runs/background/wait-config.js +46 -0
  125. package/extensions/dm-subagents/src/runs/background/wait-subscriptions.js +278 -0
  126. package/extensions/dm-subagents/src/runs/background/wait-tool.js +47 -0
  127. package/extensions/dm-subagents/src/runs/foreground/async-dismiss-action.js +81 -0
  128. package/extensions/dm-subagents/src/runs/foreground/async-steering-action.js +245 -0
  129. package/extensions/dm-subagents/src/runs/foreground/async-stop-action.js +74 -0
  130. package/extensions/dm-subagents/src/runs/foreground/execution.js +1401 -342
  131. package/extensions/dm-subagents/src/runs/foreground/foreground-control.js +133 -0
  132. package/extensions/dm-subagents/src/runs/foreground/foreground-history.js +148 -0
  133. package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +139 -0
  134. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +4278 -1441
  135. package/extensions/dm-subagents/src/runs/foreground/workflow-detach-reconcile.js +278 -0
  136. package/extensions/dm-subagents/src/runs/foreground/workflow-foreground-steering.js +155 -0
  137. package/extensions/dm-subagents/src/runs/shared/abort-recovery.js +97 -0
  138. package/extensions/dm-subagents/src/runs/shared/acceptance.js +597 -148
  139. package/extensions/dm-subagents/src/runs/shared/agent-contract.js +35 -0
  140. package/extensions/dm-subagents/src/runs/shared/async-status-projection.js +472 -0
  141. package/extensions/dm-subagents/src/runs/shared/background-process-options.js +6 -0
  142. package/extensions/dm-subagents/src/runs/shared/capability-ceiling.js +175 -0
  143. package/extensions/dm-subagents/src/runs/shared/child-identity.js +32 -0
  144. package/extensions/dm-subagents/src/runs/shared/child-launch-plan.js +65 -0
  145. package/extensions/dm-subagents/src/runs/shared/child-protocol.js +447 -0
  146. package/extensions/dm-subagents/src/runs/shared/claude-code-adapter.js +120 -0
  147. package/extensions/dm-subagents/src/runs/shared/codex-exec-adapter.js +129 -0
  148. package/extensions/dm-subagents/src/runs/shared/completion-evidence.js +40 -0
  149. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +140 -83
  150. package/extensions/dm-subagents/src/runs/shared/context-mode.js +38 -0
  151. package/extensions/dm-subagents/src/runs/shared/cursor-agent-adapter.js +101 -0
  152. package/extensions/dm-subagents/src/runs/shared/dm-args.js +445 -72
  153. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +27 -16
  154. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +19 -6
  155. package/extensions/dm-subagents/src/runs/shared/extension-bindings.js +81 -0
  156. package/extensions/dm-subagents/src/runs/shared/external-cli-contract.js +134 -0
  157. package/extensions/dm-subagents/src/runs/shared/external-cli-preflight.js +98 -0
  158. package/extensions/dm-subagents/src/runs/shared/external-cli-runner.js +419 -0
  159. package/extensions/dm-subagents/src/runs/shared/external-job-bridge.js +404 -0
  160. package/extensions/dm-subagents/src/runs/shared/external-job-runner.js +334 -0
  161. package/extensions/dm-subagents/src/runs/shared/fast-mode-extension.js +8 -0
  162. package/extensions/dm-subagents/src/runs/shared/host-step-status.js +228 -0
  163. package/extensions/dm-subagents/src/runs/shared/lane-metadata.js +104 -0
  164. package/extensions/dm-subagents/src/runs/shared/launch-cwd.js +17 -0
  165. package/extensions/dm-subagents/src/runs/shared/llm-intent-arbiter.js +190 -0
  166. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +48 -3
  167. package/extensions/dm-subagents/src/runs/shared/mcp-config-sources.js +387 -0
  168. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +212 -137
  169. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-grant.js +131 -0
  170. package/extensions/dm-subagents/src/runs/shared/model-exclusions.js +207 -0
  171. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +225 -55
  172. package/extensions/dm-subagents/src/runs/shared/model-scope.js +85 -28
  173. package/extensions/dm-subagents/src/runs/shared/mutation-evidence.js +182 -0
  174. package/extensions/dm-subagents/src/runs/shared/nested-events.js +264 -102
  175. package/extensions/dm-subagents/src/runs/shared/nested-render.js +15 -5
  176. package/extensions/dm-subagents/src/runs/shared/orca-progress-tabs.js +505 -0
  177. package/extensions/dm-subagents/src/runs/shared/parallel-handoff.js +653 -0
  178. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +29 -12
  179. package/extensions/dm-subagents/src/runs/shared/permissions.js +108 -0
  180. package/extensions/dm-subagents/src/runs/shared/process-signal.js +13 -0
  181. package/extensions/dm-subagents/src/runs/shared/run-fanout-budget.js +257 -0
  182. package/extensions/dm-subagents/src/runs/shared/run-history.js +133 -13
  183. package/extensions/dm-subagents/src/runs/shared/runtime-acknowledged-extensions.js +62 -0
  184. package/extensions/dm-subagents/src/runs/shared/session-lease.js +225 -0
  185. package/extensions/dm-subagents/src/runs/shared/single-output.js +129 -27
  186. package/extensions/dm-subagents/src/runs/shared/spawn-budget.js +95 -0
  187. package/extensions/dm-subagents/src/runs/shared/structured-output.js +129 -10
  188. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +66 -11
  189. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +548 -70
  190. package/extensions/dm-subagents/src/runs/shared/subagent-startup-retry.js +50 -0
  191. package/extensions/dm-subagents/src/runs/shared/task-intent.js +130 -0
  192. package/extensions/dm-subagents/src/runs/shared/tool-availability.js +59 -0
  193. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +7 -5
  194. package/extensions/dm-subagents/src/runs/shared/tool-timeout.js +64 -0
  195. package/extensions/dm-subagents/src/runs/shared/usage-budget.js +74 -0
  196. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +22 -0
  197. package/extensions/dm-subagents/src/runs/shared/worktree-cleanup-plan.js +721 -0
  198. package/extensions/dm-subagents/src/runs/shared/worktree.js +168 -19
  199. package/extensions/dm-subagents/src/shared/accessible-dir.js +35 -0
  200. package/extensions/dm-subagents/src/shared/agent-stream-options.js +3 -0
  201. package/extensions/dm-subagents/src/shared/artifacts.js +170 -11
  202. package/extensions/dm-subagents/src/shared/atomic-json.js +36 -38
  203. package/extensions/dm-subagents/src/shared/capacity-resilient-json.js +77 -0
  204. package/extensions/dm-subagents/src/shared/child-session-name.js +15 -0
  205. package/extensions/dm-subagents/src/shared/child-transcript.js +57 -2
  206. package/extensions/dm-subagents/src/shared/completion-owner.js +7 -0
  207. package/extensions/dm-subagents/src/shared/display-text.js +142 -0
  208. package/extensions/dm-subagents/src/shared/extension-context.js +17 -0
  209. package/extensions/dm-subagents/src/shared/file-coalescer.js +9 -0
  210. package/extensions/dm-subagents/src/shared/file-system-retry.js +56 -0
  211. package/extensions/dm-subagents/src/shared/fork-context.js +96 -33
  212. package/extensions/dm-subagents/src/shared/formatters.js +21 -7
  213. package/extensions/dm-subagents/src/shared/launch-contract.js +94 -0
  214. package/extensions/dm-subagents/src/shared/model-info.js +10 -5
  215. package/extensions/dm-subagents/src/shared/node-executable.js +19 -0
  216. package/extensions/dm-subagents/src/shared/prompt-resources.js +10 -0
  217. package/extensions/dm-subagents/src/shared/pruned-fork.js +427 -0
  218. package/extensions/dm-subagents/src/shared/session-file-trust.js +19 -0
  219. package/extensions/dm-subagents/src/shared/session-tokens.js +14 -3
  220. package/extensions/dm-subagents/src/shared/settings.js +39 -47
  221. package/extensions/dm-subagents/src/shared/shortcuts.js +16 -0
  222. package/extensions/dm-subagents/src/shared/status-format.js +9 -2
  223. package/extensions/dm-subagents/src/shared/thinking-ceiling.js +41 -0
  224. package/extensions/dm-subagents/src/shared/types.js +47 -7
  225. package/extensions/dm-subagents/src/shared/utf8.js +12 -0
  226. package/extensions/dm-subagents/src/shared/utils.js +151 -131
  227. package/extensions/dm-subagents/src/shared/watch-strategy.js +3 -0
  228. package/extensions/dm-subagents/src/shared/workflow-child-permit.js +84 -0
  229. package/extensions/dm-subagents/src/slash/delegation-adapters.js +274 -0
  230. package/extensions/dm-subagents/src/slash/delegation-json.js +113 -0
  231. package/extensions/dm-subagents/src/slash/delegation-request.js +152 -0
  232. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +294 -243
  233. package/extensions/dm-subagents/src/slash/prompt-workflows.js +35 -73
  234. package/extensions/dm-subagents/src/slash/selector.js +101 -0
  235. package/extensions/dm-subagents/src/slash/slash-bridge.js +17 -1
  236. package/extensions/dm-subagents/src/slash/slash-commands.js +722 -732
  237. package/extensions/dm-subagents/src/slash/slash-live-state.js +37 -19
  238. package/extensions/dm-subagents/src/slash/subagents-admin.js +410 -0
  239. package/extensions/dm-subagents/src/tui/fleet-status.js +824 -0
  240. package/extensions/dm-subagents/src/tui/fleet-transcript.js +479 -0
  241. package/extensions/dm-subagents/src/tui/fleet.js +1326 -0
  242. package/extensions/dm-subagents/src/tui/render-helpers.js +22 -0
  243. package/extensions/dm-subagents/src/tui/render.js +1511 -261
  244. package/extensions/dm-subagents/src/watchdog/change-signature.js +220 -0
  245. package/extensions/dm-subagents/src/watchdog/child-status.js +151 -0
  246. package/extensions/dm-subagents/src/watchdog/emission-guard.js +90 -0
  247. package/extensions/dm-subagents/src/watchdog/lsp-diagnostics.js +484 -0
  248. package/extensions/dm-subagents/src/watchdog/model-selection.js +154 -0
  249. package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +138 -0
  250. package/extensions/dm-subagents/src/watchdog/register-child.js +112 -0
  251. package/extensions/dm-subagents/src/watchdog/register-main.js +419 -0
  252. package/extensions/dm-subagents/src/watchdog/render.js +54 -0
  253. package/extensions/dm-subagents/src/watchdog/review.js +251 -0
  254. package/extensions/dm-subagents/src/watchdog/runtime.js +803 -0
  255. package/extensions/dm-subagents/src/watchdog/scope.js +56 -0
  256. package/extensions/dm-subagents/src/watchdog/settings.js +515 -0
  257. package/extensions/dm-subagents/src/watchdog/tool-actions.js +151 -0
  258. package/extensions/dm-subagents/src/watchdog/turn-delta.js +169 -0
  259. package/extensions/dm-subagents/src/watchdog/types.js +29 -0
  260. package/extensions/dm-subagents/src/watchdog/warning-format.js +58 -0
  261. package/extensions/dm-subagents/src/workflows/chat-progress.js +116 -0
  262. package/extensions/dm-subagents/src/workflows/host-command.js +227 -0
  263. package/extensions/dm-subagents/src/workflows/scripted-workflow.js +2011 -0
  264. package/extensions/dm-subagents/src/workflows/workflow-child-summary.js +116 -0
  265. package/extensions/dm-subagents/src/workflows/workflow-preflight.js +243 -0
  266. package/extensions/dm-subagents/src/workflows/workflow-receipt.js +387 -0
  267. package/extensions/dm-subagents/src/workflows/workflow-settlement.js +189 -0
  268. package/package.json +4 -3
  269. package/extensions/dm-fff/package.json +0 -21
  270. package/extensions/dm-fff/src/index.js +0 -691
  271. package/extensions/dm-fff/src/query.js +0 -60
  272. package/extensions/dm-subagents/agents/context-builder.md +0 -46
  273. package/extensions/dm-subagents/agents/planner.md +0 -55
  274. package/extensions/dm-subagents/prompts/parallel-context-build.md +0 -55
  275. package/extensions/dm-subagents/prompts/parallel-handoff-plan.md +0 -61
  276. package/extensions/dm-subagents/src/runs/background/wait.js +0 -206
  277. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +0 -1013
  278. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +0 -981
  279. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +0 -50
@@ -1,25 +1,219 @@
1
- import { buildCompletionKey, getGlobalSeenMap, markSeenWithTtl } from "./completion-dedupe.js";
1
+ import { buildCompletionKey, markSeenWithTtl } from "./completion-dedupe.js";
2
2
  import {
3
3
  createCompletionBatcher,
4
4
  resolveCompletionBatchConfig
5
5
  } from "./completion-batcher.js";
6
- import { SUBAGENT_ASYNC_COMPLETE_EVENT } from "../../shared/types.js";
6
+ import { SUBAGENT_ASYNC_COMPLETE_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT } from "../../shared/types.js";
7
+ import { safeTerminalText } from "../../shared/display-text.js";
8
+ import { resolveSubagentResultStatus } from "../../intercom/result-intercom.js";
9
+ import { isUnexplainedProcessSignal } from "../shared/process-signal.js";
10
+ const CHILD_OUTPUT_PREVIEW_MAX_BYTES = 4 * 1024;
11
+ const CHILD_OUTPUT_PREVIEW_COUNT = 8;
12
+ const PREVIEW_TRUNCATION_MARKER = "...[preview truncated]";
13
+ function truncateUtf8Head(value, maxBytes) {
14
+ const bytes = Buffer.from(value, "utf8");
15
+ if (bytes.length <= maxBytes)
16
+ return { text: value, truncated: false };
17
+ const markerBytes = Buffer.byteLength(PREVIEW_TRUNCATION_MARKER, "utf8");
18
+ const contentBytes = Math.max(0, maxBytes - markerBytes);
19
+ let end = contentBytes;
20
+ while (end > 0 && (bytes[end] & 192) === 128)
21
+ end -= 1;
22
+ return {
23
+ text: `${bytes.subarray(0, end).toString("utf8")}${PREVIEW_TRUNCATION_MARKER}`,
24
+ truncated: true
25
+ };
26
+ }
27
+ function boundedSafeText(value, maxBytes = 1024) {
28
+ return truncateUtf8Head(safeTerminalText(value).replace(/\n/g, "\\n"), maxBytes).text;
29
+ }
30
+ function outputPathFromReference(value) {
31
+ if (typeof value === "string" && value.trim())
32
+ return value.trim();
33
+ if (value && typeof value === "object" && typeof value.path === "string" && value.path.trim())
34
+ return value.path.trim();
35
+ return;
36
+ }
37
+ function childSavedOutputPath(child) {
38
+ return outputPathFromReference(child.outputReference);
39
+ }
40
+ function childStatus(child, workflowState) {
41
+ const knownStatus = child.status === "complete" ? "completed" : child.status === "completed" || child.status === "failed" || child.status === "paused" || child.status === "stopped" || child.status === "detached" ? child.status : undefined;
42
+ if (knownStatus)
43
+ return knownStatus;
44
+ return resolveSubagentResultStatus({
45
+ success: child.success,
46
+ state: child.state ?? (child.success === undefined ? workflowState : undefined),
47
+ interrupted: child.interrupted,
48
+ detached: child.detached,
49
+ processSignal: child.processSignal,
50
+ timedOut: child.timedOut,
51
+ stopped: child.stopped,
52
+ turnBudgetExceeded: child.turnBudgetExceeded,
53
+ exitCode: typeof child.exitCode === "number" ? child.exitCode : undefined
54
+ });
55
+ }
56
+ function structuredOutputText(value) {
57
+ if (value === undefined)
58
+ return;
59
+ try {
60
+ const serialized = JSON.stringify(value, null, 2);
61
+ return typeof serialized === "string" ? serialized : undefined;
62
+ } catch {
63
+ return;
64
+ }
65
+ }
66
+ function childInlinePreview(child) {
67
+ const output = typeof child.output === "string" ? child.output : "";
68
+ const outputReference = childSavedOutputPath(child);
69
+ const referenceOnly = Boolean(outputReference && output.trim().startsWith("Output saved to:"));
70
+ const raw = child.outputState === "absent" || referenceOnly ? structuredOutputText(child.structuredOutput) : output || structuredOutputText(child.structuredOutput);
71
+ if (!raw?.trim()) {
72
+ return { unavailableReason: referenceOnly ? "saved output is file-only" : "no safe inline output" };
73
+ }
74
+ const safe = safeTerminalText(raw);
75
+ if (!safe.trim())
76
+ return { unavailableReason: "no safe inline output" };
77
+ const bounded = truncateUtf8Head(safe, CHILD_OUTPUT_PREVIEW_MAX_BYTES);
78
+ return { preview: bounded.text, ...bounded.truncated ? { truncated: true } : {} };
79
+ }
80
+ function formatChildOutputBlock(children) {
81
+ if (!children?.length)
82
+ return;
83
+ const lines = ["Child outputs:"];
84
+ for (let index = 0;index < children.length; index++) {
85
+ const child = children[index];
86
+ const key = child.workflowKey ? boundedSafeText(child.workflowKey) : "unavailable";
87
+ const runId = child.runId ? boundedSafeText(child.runId) : "unavailable";
88
+ const status = boundedSafeText(child.status) || "unavailable";
89
+ lines.push(`- key=${key} run=${runId} status=${status}`);
90
+ lines.push(` Saved output: ${child.savedOutputPath ? boundedSafeText(child.savedOutputPath) : "unavailable"}`);
91
+ if (index >= CHILD_OUTPUT_PREVIEW_COUNT) {
92
+ lines.push(" Preview: unavailable (notice preview budget exceeded)");
93
+ continue;
94
+ }
95
+ if (!child.preview) {
96
+ lines.push(` Preview: unavailable (${child.previewUnavailableReason ?? "no safe inline output"})`);
97
+ continue;
98
+ }
99
+ lines.push(" Preview:");
100
+ for (const line of child.preview.split(`
101
+ `))
102
+ lines.push(` | ${line}`);
103
+ }
104
+ if (children.length > CHILD_OUTPUT_PREVIEW_COUNT) {
105
+ lines.push(`- ${children.length - CHILD_OUTPUT_PREVIEW_COUNT} additional child preview(s) omitted by notice budget; child run metadata is retained below.`);
106
+ }
107
+ return lines.join(`
108
+ `);
109
+ }
110
+ function formatResultPreview(details) {
111
+ const summary = details.resultPreview.trim() ? details.resultPreview : "(no output)";
112
+ const childOutputs = formatChildOutputBlock(details.childOutputs);
113
+ return childOutputs ? `${summary}
114
+
115
+ ${childOutputs}` : summary;
116
+ }
7
117
  function formatSessionLine(details) {
8
118
  if (!details.sessionValue)
9
119
  return;
10
120
  return details.sessionLabel ? `${details.sessionLabel}: ${details.sessionValue}` : details.sessionValue;
11
121
  }
122
+ function formatChildRun(child) {
123
+ const label = child.workflowKey ?? child.agent;
124
+ const status = child.status ? ` (${child.status})` : "";
125
+ return `${label ? `${label}=` : ""}${child.runId}${status}`;
126
+ }
127
+ function formatCorrelationLines(details) {
128
+ return [
129
+ details.workflowRunId ? `Workflow run: ${details.workflowRunId}` : undefined,
130
+ details.childRuns?.length ? `Child runs: ${details.childRuns.map(formatChildRun).join(", ")}` : undefined,
131
+ details.reconciledFromDetachedChild ? `Reconciled detached child: ${details.reconciledFromDetachedChild}` : undefined
132
+ ].filter((line) => line !== undefined);
133
+ }
12
134
  export function formatSingleCompletion(details) {
13
135
  const sessionLine = formatSessionLine(details);
136
+ const correlationLines = formatCorrelationLines(details);
137
+ const taskKind = details.source === "foreground" ? "Detached foreground task" : "Background task";
138
+ const scheduleLine = details.scheduleOrigin ? `Scheduled run from **${details.scheduleOrigin.name ?? details.scheduleOrigin.id}** (schedule ${details.scheduleOrigin.id}).` : undefined;
14
139
  return [
15
- `Background task ${details.status}: **${details.agent}**${details.taskInfo ?? ""}`,
140
+ `${taskKind} ${details.status}: **${details.agent}**${details.taskInfo ?? ""}`,
16
141
  "",
17
- details.resultPreview.trim() ? details.resultPreview : "(no output)",
142
+ scheduleLine,
143
+ scheduleLine ? "" : undefined,
144
+ formatResultPreview(details),
145
+ details.handoffPath ? "" : undefined,
146
+ details.handoffPath ? `Parallel handoff: ${details.handoffPath}` : undefined,
147
+ correlationLines.length && !details.handoffPath ? "" : undefined,
148
+ ...correlationLines,
18
149
  sessionLine ? "" : undefined,
19
150
  sessionLine
20
151
  ].filter((line) => line !== undefined).join(`
21
152
  `);
22
153
  }
154
+ export function parseSubagentNotifyContent(content) {
155
+ const lines = content.split(`
156
+ `);
157
+ const match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused|stopped): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
158
+ if (!match)
159
+ return;
160
+ let body = lines.slice(2);
161
+ const scheduleMatch = (body[0] ?? "").match(/^Scheduled run from \*\*(.+?)\*\* \(schedule (.+?)\)\.$/);
162
+ let parsedScheduleOrigin;
163
+ if (scheduleMatch) {
164
+ const label = scheduleMatch[1];
165
+ const id = scheduleMatch[2];
166
+ parsedScheduleOrigin = { id, ...label === id ? {} : { name: label } };
167
+ body = body.slice(body[1]?.trim() === "" ? 2 : 1);
168
+ }
169
+ let sessionIndex = -1;
170
+ for (let i = body.length - 1;i >= 1; i--) {
171
+ if (body[i - 1]?.trim() === "" && /^(Session|Session file|Session share error):\s+/.test(body[i])) {
172
+ sessionIndex = i;
173
+ break;
174
+ }
175
+ }
176
+ const sessionLine = sessionIndex >= 0 ? body[sessionIndex] : undefined;
177
+ const handoffIndex = body.findIndex((line) => line.startsWith("Parallel handoff: "));
178
+ const workflowRunIndex = body.findIndex((line) => line.startsWith("Workflow run: "));
179
+ const childRunsIndex = body.findIndex((line) => line.startsWith("Child runs: "));
180
+ const reconciledIndex = body.findIndex((line) => line.startsWith("Reconciled detached child: "));
181
+ const metadataIndexes = [sessionIndex, handoffIndex, workflowRunIndex, childRunsIndex, reconciledIndex].filter((index) => index >= 0);
182
+ const firstMetadataIndex = metadataIndexes.length ? Math.min(...metadataIndexes) : body.length;
183
+ const resultEnd = firstMetadataIndex > 0 && body[firstMetadataIndex - 1]?.trim() === "" ? firstMetadataIndex - 1 : firstMetadataIndex;
184
+ const resultPreview = body.slice(0, resultEnd).join(`
185
+ `).trim() || "(no output)";
186
+ const handoffPath = handoffIndex >= 0 ? body[handoffIndex].slice("Parallel handoff: ".length).trim() : undefined;
187
+ const workflowRunId = workflowRunIndex >= 0 ? body[workflowRunIndex].slice("Workflow run: ".length).trim() : undefined;
188
+ const childRuns = childRunsIndex >= 0 ? body[childRunsIndex].slice("Child runs: ".length).split(", ").map((part) => {
189
+ const trimmed = part.trim();
190
+ const statusMatch = trimmed.match(/^(.*?)(?: \(([^)]*)\))?$/);
191
+ const raw = statusMatch?.[1] ?? trimmed;
192
+ const separator = raw.indexOf("=");
193
+ return separator >= 0 ? { workflowKey: raw.slice(0, separator), runId: raw.slice(separator + 1), ...statusMatch?.[2] ? { status: statusMatch[2] } : {} } : { runId: raw, ...statusMatch?.[2] ? { status: statusMatch[2] } : {} };
194
+ }).filter((child) => child.runId) : undefined;
195
+ const reconciledFromDetachedChild = reconciledIndex >= 0 ? body[reconciledIndex].slice("Reconciled detached child: ".length).trim() : undefined;
196
+ let sessionLabel;
197
+ let sessionValue;
198
+ if (sessionLine) {
199
+ const separator = sessionLine.indexOf(":");
200
+ sessionLabel = sessionLine.slice(0, separator).toLowerCase();
201
+ sessionValue = sessionLine.slice(separator + 1).trim();
202
+ }
203
+ return {
204
+ agent: match[3],
205
+ status: match[2],
206
+ ...match[1] === "Detached foreground task" ? { source: "foreground" } : {},
207
+ ...match[4] ? { taskInfo: match[4] } : {},
208
+ ...parsedScheduleOrigin ? { scheduleOrigin: parsedScheduleOrigin } : {},
209
+ resultPreview,
210
+ ...handoffPath ? { handoffPath } : {},
211
+ ...workflowRunId ? { workflowRunId } : {},
212
+ ...childRuns?.length ? { childRuns } : {},
213
+ ...reconciledFromDetachedChild ? { reconciledFromDetachedChild } : {},
214
+ ...sessionLabel && sessionValue ? { sessionLabel, sessionValue } : {}
215
+ };
216
+ }
23
217
  export function formatGroupedCompletion(details) {
24
218
  const header = `Background tasks completed (${details.length}): ${details.map((d) => `**${d.agent}**${d.taskInfo ?? ""}`).join(", ")}`;
25
219
  const blocks = [header, ""];
@@ -28,8 +222,11 @@ export function formatGroupedCompletion(details) {
28
222
  if (!detail)
29
223
  continue;
30
224
  const sessionLine = formatSessionLine(detail);
31
- blocks.push(`${index + 1}. ${detail.agent}${detail.taskInfo ?? ""}`);
32
- blocks.push(detail.resultPreview.trim() ? detail.resultPreview : "(no output)");
225
+ blocks.push(`${index + 1}. ${detail.agent}${detail.taskInfo ?? ""}${detail.scheduleOrigin ? ` — scheduled run from ${detail.scheduleOrigin.name ?? detail.scheduleOrigin.id} (schedule ${detail.scheduleOrigin.id})` : ""}`);
226
+ blocks.push(formatResultPreview(detail));
227
+ if (detail.handoffPath)
228
+ blocks.push(`Parallel handoff: ${detail.handoffPath}`);
229
+ blocks.push(...formatCorrelationLines(detail));
33
230
  if (sessionLine)
34
231
  blocks.push(sessionLine);
35
232
  blocks.push("");
@@ -37,15 +234,22 @@ export function formatGroupedCompletion(details) {
37
234
  return blocks.join(`
38
235
  `).trimEnd();
39
236
  }
40
- function sendCompletion(pi, details) {
41
- if (details.length === 0)
42
- return;
237
+ function sendCompletion(dm, items) {
238
+ if (items.length === 0)
239
+ return true;
240
+ const details = items.map((item) => item.details);
43
241
  const content = details.length === 1 ? formatSingleCompletion(details[0]) : formatGroupedCompletion(details);
44
- pi.sendMessage({
45
- customType: "subagent-notify",
46
- content,
47
- display: true
48
- }, { triggerTurn: true });
242
+ const display = details.some((detail) => detail.source === "foreground" || detail.status !== "completed" || detail.scheduleOrigin !== undefined);
243
+ try {
244
+ dm.sendMessage({
245
+ customType: "subagent-notify",
246
+ content,
247
+ display
248
+ }, { triggerTurn: items.some((item) => item.triggerTurn) });
249
+ return true;
250
+ } catch {
251
+ return false;
252
+ }
49
253
  }
50
254
  function completionBatchKey(result) {
51
255
  const sessionId = typeof result.sessionId === "string" ? result.sessionId.trim() : "";
@@ -57,73 +261,169 @@ function completionBatchKey(result) {
57
261
  export function buildCompletionDetails(result) {
58
262
  const agent = result.agent ?? "unknown";
59
263
  const summary = typeof result.summary === "string" ? result.summary : "";
60
- const paused = !result.success && (result.exitCode === 0 || result.state === "paused" || summary.startsWith("Paused after interrupt."));
61
- const status = paused ? "paused" : result.success ? "completed" : "failed";
264
+ const stopped = result.stopped === true || result.state === "stopped" || result.success !== true && result.exitCode !== 0 && isUnexplainedProcessSignal(result) || result.results?.some((child) => child.stopped === true || child.status === "stopped" || child.success !== true && child.exitCode !== 0 && isUnexplainedProcessSignal(child)) === true;
265
+ const paused = !stopped && !result.success && (result.exitCode === 0 || result.state === "paused" || result.interrupted === true || summary.startsWith("Paused after interrupt."));
266
+ const status = stopped ? "stopped" : paused ? "paused" : result.success ? "completed" : "failed";
62
267
  const taskInfo = result.taskIndex !== undefined && result.totalTasks !== undefined ? ` (${result.taskIndex + 1}/${result.totalTasks})` : undefined;
268
+ const parallelHandoff = result.parallelHandoff && typeof result.parallelHandoff === "object" ? result.parallelHandoff : undefined;
269
+ const handoffPath = typeof parallelHandoff?.path === "string" ? parallelHandoff.path : undefined;
270
+ const rawRunId = typeof result.runId === "string" ? result.runId : typeof result.id === "string" ? result.id : undefined;
271
+ const workflowRunId = (result.mode === "workflow" || agent === "workflow") && rawRunId ? rawRunId : undefined;
272
+ const childRuns = result.results?.flatMap((child) => {
273
+ const runId = typeof child.runId === "string" && child.runId.trim() ? child.runId.trim() : undefined;
274
+ const workflowKey = typeof child.workflowKey === "string" && child.workflowKey.trim() ? child.workflowKey.trim() : undefined;
275
+ if (!runId && (!workflowRunId || !workflowKey))
276
+ return [];
277
+ return [{
278
+ runId: runId ?? "unavailable",
279
+ ...workflowKey ? { workflowKey } : {},
280
+ ...typeof child.agent === "string" ? { agent: child.agent } : {},
281
+ status: childStatus(child, workflowRunId ? result.state : undefined)
282
+ }];
283
+ }) ?? [];
284
+ const childOutputs = workflowRunId && result.results?.length ? result.results.map((child) => {
285
+ const inline = childInlinePreview(child);
286
+ const workflowKey = typeof child.workflowKey === "string" && child.workflowKey.trim() ? child.workflowKey.trim() : undefined;
287
+ const runId = typeof child.runId === "string" && child.runId.trim() ? child.runId.trim() : undefined;
288
+ const savedOutputPath = childSavedOutputPath(child);
289
+ return {
290
+ ...workflowKey ? { workflowKey } : {},
291
+ ...runId ? { runId } : {},
292
+ ...typeof child.agent === "string" ? { agent: child.agent } : {},
293
+ status: childStatus(child, result.state),
294
+ ...savedOutputPath ? { savedOutputPath } : {},
295
+ ...inline.preview ? { preview: inline.preview } : { preview: "" },
296
+ ...inline.truncated ? { previewTruncated: true } : {},
297
+ ...inline.unavailableReason ? { previewUnavailableReason: inline.unavailableReason } : {}
298
+ };
299
+ }) : undefined;
300
+ const reconciledFromDetachedChild = typeof result.reconciledFromDetachedChild === "string" ? result.reconciledFromDetachedChild : undefined;
63
301
  const session = result.shareUrl ? { label: "Session", value: result.shareUrl } : result.shareError ? { label: "Session share error", value: result.shareError } : result.sessionFile ? { label: "Session file", value: result.sessionFile } : undefined;
302
+ const rawOrigin = result.scheduleOrigin;
303
+ const scheduleOrigin = rawOrigin && typeof rawOrigin.id === "string" ? { id: rawOrigin.id, ...typeof rawOrigin.name === "string" ? { name: rawOrigin.name } : {} } : undefined;
64
304
  return {
65
305
  agent,
66
306
  status,
307
+ ...scheduleOrigin ? { scheduleOrigin } : {},
308
+ ...result.source ? { source: result.source } : {},
67
309
  ...taskInfo ? { taskInfo } : {},
68
310
  resultPreview: summary,
69
311
  ...typeof result.durationMs === "number" ? { durationMs: result.durationMs } : {},
312
+ ...handoffPath ? { handoffPath } : {},
313
+ ...workflowRunId ? { workflowRunId } : {},
314
+ ...childRuns.length ? { childRuns } : {},
315
+ ...childOutputs?.length ? { childOutputs } : {},
316
+ ...reconciledFromDetachedChild ? { reconciledFromDetachedChild } : {},
70
317
  ...session ? { sessionLabel: session.label, sessionValue: session.value } : {}
71
318
  };
72
319
  }
73
- export default function registerSubagentNotify(pi, state, options = {}) {
74
- const unsubscribeStoreKey = "__pi_subagents_notify_unsubscribe__";
75
- const batcherStoreKey = "__pi_subagents_notify_batcher__";
76
- const globalStore = globalThis;
77
- const previousUnsubscribe = globalStore[unsubscribeStoreKey];
78
- if (typeof previousUnsubscribe === "function") {
79
- try {
80
- previousUnsubscribe();
81
- } catch {}
82
- }
83
- const previousBatcher = globalStore[batcherStoreKey];
84
- if (previousBatcher && typeof previousBatcher.dispose === "function") {
85
- try {
86
- previousBatcher.dispose();
87
- } catch {}
88
- }
89
- const seen = getGlobalSeenMap("__pi_subagents_notify_seen__");
320
+ export default function registerSubagentNotify(dm, state, options = {}) {
321
+ const seen = new Map;
322
+ const pending = new Map;
90
323
  const ttlMs = 10 * 60 * 1000;
91
- const nowFn = options.now ?? Date.now;
324
+ const now = options.now ?? Date.now;
92
325
  const batchConfig = resolveCompletionBatchConfig(options.batchConfig);
93
326
  const batchers = new Map;
94
- globalStore[batcherStoreKey] = {
95
- dispose() {
96
- for (const batcher of batchers.values())
97
- batcher.dispose();
98
- batchers.clear();
327
+ let disposed = false;
328
+ const ownsResult = options.ownership?.owns ?? ((sessionId, completionOwnerId) => sessionId === state.currentSessionId && typeof completionOwnerId === "string" && completionOwnerId === state.completionOwnerId);
329
+ const settle = (items, accepted) => {
330
+ for (const item of items) {
331
+ pending.delete(item.key);
332
+ if (accepted)
333
+ markSeenWithTtl(seen, item.key, now(), ttlMs);
334
+ item.resolve(accepted);
99
335
  }
100
336
  };
101
- const handleComplete = (data) => {
102
- const result = data;
103
- if (typeof result.sessionId !== "string" || result.sessionId !== state.currentSessionId)
104
- return;
105
- const now = nowFn();
106
- const key = buildCompletionKey(result, "notify");
107
- if (markSeenWithTtl(seen, key, now, ttlMs))
108
- return;
109
- const details = buildCompletionDetails(result);
110
- const batchKey = completionBatchKey(result);
111
- let batcher = batchers.get(batchKey);
337
+ const emit = (items) => {
338
+ const accepted = [];
339
+ const rejected = [];
340
+ for (const item of items) {
341
+ const owned = item.details.source === "foreground" ? item.sessionId === state.currentSessionId : ownsResult(item.sessionId, item.completionOwnerId);
342
+ (owned ? accepted : rejected).push(item);
343
+ }
344
+ settle(rejected, false);
345
+ settle(accepted, sendCompletion(dm, accepted));
346
+ };
347
+ const getBatcher = (result) => {
348
+ const key = completionBatchKey(result);
349
+ let batcher = batchers.get(key);
112
350
  if (!batcher) {
113
351
  batcher = createCompletionBatcher({
114
352
  config: batchConfig,
115
- emit: (items) => sendCompletion(pi, items),
353
+ emit,
116
354
  ...options.timers ? { timers: options.timers } : {},
117
- now: nowFn
355
+ now
118
356
  });
119
- batchers.set(batchKey, batcher);
357
+ batchers.set(key, batcher);
358
+ }
359
+ return batcher;
360
+ };
361
+ const deliver = (result) => {
362
+ if (disposed || typeof result.sessionId !== "string")
363
+ return Promise.resolve(false);
364
+ if (result.source === "foreground") {
365
+ if (result.sessionId !== state.currentSessionId)
366
+ return Promise.resolve(false);
367
+ } else if (!ownsResult(result.sessionId, result.completionOwnerId))
368
+ return Promise.resolve(false);
369
+ if (result.intercomDelivered === true)
370
+ return Promise.resolve(true);
371
+ const key = buildCompletionKey(result, "notify");
372
+ const seenAt = seen.get(key);
373
+ if (seenAt !== undefined && now() - seenAt <= ttlMs)
374
+ return Promise.resolve(true);
375
+ if (seenAt !== undefined)
376
+ seen.delete(key);
377
+ const inFlight = pending.get(key);
378
+ if (inFlight)
379
+ return inFlight;
380
+ const details = buildCompletionDetails(result);
381
+ let resolve;
382
+ const completion = new Promise((settleCompletion) => {
383
+ resolve = settleCompletion;
384
+ });
385
+ pending.set(key, completion);
386
+ const item = {
387
+ key,
388
+ details,
389
+ sessionId: result.sessionId,
390
+ completionOwnerId: result.completionOwnerId,
391
+ triggerTurn: result.triggerTurn !== false,
392
+ resolve
393
+ };
394
+ if (details.source === "foreground") {
395
+ emit([item]);
396
+ return completion;
120
397
  }
398
+ const batcher = getBatcher(result);
121
399
  if (details.status !== "completed") {
122
400
  batcher.flush();
123
- sendCompletion(pi, [details]);
124
- return;
401
+ emit([item]);
402
+ return completion;
403
+ }
404
+ batcher.push(item);
405
+ return completion;
406
+ };
407
+ const unsubscribeAsync = dm.events.on(SUBAGENT_ASYNC_COMPLETE_EVENT, (data) => {
408
+ deliver(data);
409
+ });
410
+ const unsubscribeForeground = dm.events.on(SUBAGENT_FOREGROUND_COMPLETE_EVENT, (data) => {
411
+ deliver(data);
412
+ });
413
+ return {
414
+ deliver,
415
+ dispose() {
416
+ if (disposed)
417
+ return;
418
+ disposed = true;
419
+ for (const batcher of batchers.values())
420
+ settle(batcher.dispose(), false);
421
+ batchers.clear();
422
+ for (const unsubscribe of [unsubscribeAsync, unsubscribeForeground]) {
423
+ try {
424
+ unsubscribe?.();
425
+ } catch {}
426
+ }
125
427
  }
126
- batcher.push(details);
127
428
  };
128
- globalStore[unsubscribeStoreKey] = pi.events.on(SUBAGENT_ASYNC_COMPLETE_EVENT, handleComplete);
129
429
  }
@@ -0,0 +1,86 @@
1
+ import { spawnSync } from "node:child_process";
2
+ const DEFAULT_TERM_GRACE_MS = 3000;
3
+ const DEFAULT_KILL_VERIFY_MS = 1000;
4
+ const VERIFY_INTERVAL_MS = 25;
5
+ function diagnostic(error) {
6
+ return error instanceof Error ? error.message : String(error);
7
+ }
8
+ function signalProcess(id, signal) {
9
+ try {
10
+ process.kill(id, signal);
11
+ return "sent";
12
+ } catch (error) {
13
+ if (error.code === "ESRCH")
14
+ return "absent";
15
+ return { diagnostic: diagnostic(error) };
16
+ }
17
+ }
18
+ function activeProcessGroupMembers(processGroupId) {
19
+ const result = spawnSync("ps", ["-axo", "pid=,pgid=,stat="], { encoding: "utf-8" });
20
+ if (result.error || result.status !== 0) {
21
+ return { diagnostic: result.error ? diagnostic(result.error) : result.stderr.trim() || `ps exited with ${result.status}` };
22
+ }
23
+ const members = [];
24
+ for (const line of result.stdout.split(`
25
+ `)) {
26
+ const match = /^\s*(\d+)\s+(\d+)\s+(\S+)/.exec(line);
27
+ if (!match || Number(match[2]) !== processGroupId || match[3].startsWith("Z"))
28
+ continue;
29
+ members.push(Number(match[1]));
30
+ }
31
+ return members;
32
+ }
33
+ async function waitUntilGroupTerminal(processGroupId, timeoutMs) {
34
+ const deadline = Date.now() + timeoutMs;
35
+ while (true) {
36
+ const members = activeProcessGroupMembers(processGroupId);
37
+ if (Array.isArray(members) && members.length === 0)
38
+ return false;
39
+ const remaining = deadline - Date.now();
40
+ if (remaining <= 0) {
41
+ if (!Array.isArray(members))
42
+ return { state: "enumeration-failed", diagnostic: members.diagnostic };
43
+ return { state: "still-active", diagnostic: `Process group ${processGroupId} still has active members: ${members.join(", ")}.` };
44
+ }
45
+ await new Promise((resolve) => setTimeout(resolve, Math.min(VERIFY_INTERVAL_MS, remaining)));
46
+ }
47
+ }
48
+ function observed(processGroupId) {
49
+ return { state: "observed", mechanism: "posix-process-group", processGroupId, verifiedAt: Date.now() };
50
+ }
51
+ export function createOwnedProcessTreeController(pid, options = {}) {
52
+ let termination;
53
+ const posixGroupOwned = process.platform !== "win32";
54
+ const target = posixGroupOwned ? -pid : pid;
55
+ const terminate = () => {
56
+ if (termination)
57
+ return termination;
58
+ termination = (async () => {
59
+ if (!posixGroupOwned) {
60
+ signalProcess(target, "SIGTERM");
61
+ return { state: "unknown", reason: "unsupported-platform" };
62
+ }
63
+ const term = signalProcess(target, "SIGTERM");
64
+ if (term !== "sent" && term !== "absent") {
65
+ return { state: "unknown", reason: "signal-failed", diagnostic: term.diagnostic };
66
+ }
67
+ const termExit = await waitUntilGroupTerminal(pid, options.termGraceMs ?? DEFAULT_TERM_GRACE_MS);
68
+ if (termExit === false)
69
+ return observed(pid);
70
+ const kill = signalProcess(target, "SIGKILL");
71
+ if (kill !== "sent" && kill !== "absent") {
72
+ const members = activeProcessGroupMembers(pid);
73
+ if (!Array.isArray(members) || members.length > 0) {
74
+ return { state: "unknown", reason: "signal-failed", diagnostic: kill.diagnostic };
75
+ }
76
+ }
77
+ const killExit = await waitUntilGroupTerminal(pid, options.killVerifyMs ?? DEFAULT_KILL_VERIFY_MS);
78
+ if (killExit !== false) {
79
+ return { state: "unknown", reason: "verification-failed", diagnostic: killExit.diagnostic };
80
+ }
81
+ return observed(pid);
82
+ })();
83
+ return termination;
84
+ };
85
+ return { terminate, finishAfterWriterClose: terminate };
86
+ }