@duckmind/dm-darwin-x64 0.43.2 → 0.43.8

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 (256) hide show
  1. package/dm +0 -0
  2. package/extensions/.dm-extensions.json +242 -49
  3. package/extensions/dm-9router-ext/package.json +2 -19
  4. package/extensions/dm-9router-ext/src/index.js +5 -0
  5. package/extensions/dm-ask-user/index.js +10 -0
  6. package/extensions/dm-ask-user/package.json +1 -21
  7. package/extensions/dm-cli-anything/package.json +0 -6
  8. package/extensions/dm-context/package.json +2 -12
  9. package/extensions/dm-context/src/context.js +1 -0
  10. package/extensions/dm-context/src/index.js +7 -0
  11. package/extensions/dm-context/src/utils.js +1 -0
  12. package/extensions/dm-cua/package.json +0 -7
  13. package/extensions/dm-fff/package.json +1 -28
  14. package/extensions/dm-fff/src/index.js +15 -0
  15. package/extensions/dm-fff/src/query.js +1 -0
  16. package/extensions/dm-goal/package.json +2 -31
  17. package/extensions/dm-goal/src/goal.js +43 -0
  18. package/extensions/dm-grill-me/index.js +174 -0
  19. package/extensions/dm-grill-me/package.json +1 -26
  20. package/extensions/dm-image2/package.json +0 -7
  21. package/extensions/dm-subagents/package.json +1 -32
  22. package/extensions/dm-subagents/src/agents/agent-management.js +37 -0
  23. package/extensions/dm-subagents/src/agents/agent-memory.js +6 -0
  24. package/extensions/dm-subagents/src/agents/agent-scope.js +1 -0
  25. package/extensions/dm-subagents/src/agents/agent-selection.js +1 -0
  26. package/extensions/dm-subagents/src/agents/agent-serializer.js +7 -0
  27. package/extensions/dm-subagents/src/agents/agents.js +11 -0
  28. package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -0
  29. package/extensions/dm-subagents/src/agents/frontmatter.js +6 -0
  30. package/extensions/dm-subagents/src/agents/identity.js +1 -0
  31. package/extensions/dm-subagents/src/agents/proactive-skills.js +1 -0
  32. package/extensions/dm-subagents/src/agents/skills.js +6 -0
  33. package/extensions/dm-subagents/src/extension/config.js +2 -0
  34. package/extensions/dm-subagents/src/extension/control-notices.js +4 -0
  35. package/extensions/dm-subagents/src/extension/doctor.js +16 -0
  36. package/extensions/dm-subagents/src/extension/fanout-child.js +231 -0
  37. package/extensions/dm-subagents/src/extension/index.js +367 -0
  38. package/extensions/dm-subagents/src/extension/rpc.js +8 -0
  39. package/extensions/dm-subagents/src/extension/schemas.js +1 -0
  40. package/extensions/dm-subagents/src/extension/{tool-description.ts → tool-description.js} +6 -126
  41. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +13 -0
  42. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +5 -0
  43. package/extensions/dm-subagents/src/intercom/result-intercom.js +3 -0
  44. package/extensions/dm-subagents/src/profiles/profiles.js +3 -0
  45. package/extensions/dm-subagents/src/runs/background/async-execution.js +46 -0
  46. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +14 -0
  47. package/extensions/dm-subagents/src/runs/background/async-resume.js +8 -0
  48. package/extensions/dm-subagents/src/runs/background/async-status.js +12 -0
  49. package/extensions/dm-subagents/src/runs/background/chain-append.js +2 -0
  50. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +1 -0
  51. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +1 -0
  52. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +1 -0
  53. package/extensions/dm-subagents/src/runs/background/control-channel.js +1 -0
  54. package/extensions/dm-subagents/src/runs/background/fleet-view.js +17 -0
  55. package/extensions/dm-subagents/src/runs/background/notify.js +3 -0
  56. package/extensions/dm-subagents/src/runs/background/parallel-groups.js +1 -0
  57. package/extensions/dm-subagents/src/runs/background/result-watcher.js +8 -0
  58. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +4 -0
  59. package/extensions/dm-subagents/src/runs/background/run-status.js +23 -0
  60. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +4 -0
  61. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +11 -0
  62. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +78 -0
  63. package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -0
  64. package/extensions/dm-subagents/src/runs/background/wait.js +11 -0
  65. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +12 -0
  66. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +102 -0
  67. package/extensions/dm-subagents/src/runs/foreground/execution.js +51 -0
  68. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +228 -0
  69. package/extensions/dm-subagents/src/runs/shared/acceptance.js +3 -0
  70. package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +1 -0
  71. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +3 -0
  72. package/extensions/dm-subagents/src/runs/shared/dm-args.js +1 -0
  73. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +1 -0
  74. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +1 -0
  75. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +1 -0
  76. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +1 -0
  77. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +1 -0
  78. package/extensions/dm-subagents/src/runs/shared/model-scope.js +1 -0
  79. package/extensions/dm-subagents/src/runs/shared/nested-events.js +8 -0
  80. package/extensions/dm-subagents/src/runs/shared/nested-path.js +1 -0
  81. package/extensions/dm-subagents/src/runs/shared/nested-render.js +1 -0
  82. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +5 -0
  83. package/extensions/dm-subagents/src/runs/shared/run-history.js +5 -0
  84. package/extensions/dm-subagents/src/runs/shared/single-output.js +14 -0
  85. package/extensions/dm-subagents/src/runs/shared/structured-output.js +1 -0
  86. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +5 -0
  87. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +22 -0
  88. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +1 -0
  89. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +7 -0
  90. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +1 -0
  91. package/extensions/dm-subagents/src/runs/shared/worktree.js +3 -0
  92. package/extensions/dm-subagents/src/shared/artifacts.js +2 -0
  93. package/extensions/dm-subagents/src/shared/atomic-json.js +1 -0
  94. package/extensions/dm-subagents/src/shared/child-transcript.js +5 -0
  95. package/extensions/dm-subagents/src/shared/file-coalescer.js +1 -0
  96. package/extensions/dm-subagents/src/shared/fork-context.js +6 -0
  97. package/extensions/dm-subagents/src/shared/formatters.js +9 -0
  98. package/extensions/dm-subagents/src/shared/jsonl-writer.js +2 -0
  99. package/extensions/dm-subagents/src/shared/model-info.js +1 -0
  100. package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +1 -0
  101. package/extensions/dm-subagents/src/shared/session-identity.js +1 -0
  102. package/extensions/dm-subagents/src/shared/session-tokens.js +2 -0
  103. package/extensions/dm-subagents/src/shared/settings.js +23 -0
  104. package/extensions/dm-subagents/src/shared/status-format.js +1 -0
  105. package/extensions/dm-subagents/src/shared/types.js +8 -0
  106. package/extensions/dm-subagents/src/shared/utils.js +2 -0
  107. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +1 -0
  108. package/extensions/dm-subagents/src/slash/prompt-workflows.js +7 -0
  109. package/extensions/dm-subagents/src/slash/slash-bridge.js +1 -0
  110. package/extensions/dm-subagents/src/slash/slash-commands.js +38 -0
  111. package/extensions/dm-subagents/src/slash/slash-live-state.js +6 -0
  112. package/extensions/dm-subagents/src/tui/render-helpers.js +1 -0
  113. package/extensions/dm-subagents/src/tui/render.js +4 -0
  114. package/extensions/dm-tasks/package.json +1 -30
  115. package/extensions/dm-tasks/src/auto-clear.js +1 -0
  116. package/extensions/dm-tasks/src/index.js +212 -0
  117. package/extensions/dm-tasks/src/process-tracker.js +1 -0
  118. package/extensions/dm-tasks/src/task-store.js +1 -0
  119. package/extensions/dm-tasks/src/tasks-config.js +1 -0
  120. package/extensions/dm-tasks/src/types.js +0 -0
  121. package/extensions/dm-tasks/src/ui/settings-menu.js +1 -0
  122. package/extensions/dm-tasks/src/ui/task-widget.js +1 -0
  123. package/extensions/dm-usage/index.js +9 -0
  124. package/extensions/dm-usage/package.json +1 -18
  125. package/extensions/greedysearch-dm/bin/search.mjs +135 -135
  126. package/extensions/greedysearch-dm/index.js +20 -0
  127. package/extensions/greedysearch-dm/package.json +1 -22
  128. package/extensions/greedysearch-dm/src/formatters/results.js +8 -0
  129. package/extensions/greedysearch-dm/src/formatters/sources.js +1 -0
  130. package/extensions/greedysearch-dm/src/formatters/synthesis.js +1 -0
  131. package/extensions/greedysearch-dm/src/search/engines.mjs +9 -9
  132. package/extensions/greedysearch-dm/src/search/paths.mjs +1 -0
  133. package/extensions/greedysearch-dm/src/search/research.mjs +100 -100
  134. package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +10 -10
  135. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +20 -0
  136. package/extensions/greedysearch-dm/src/tools/shared.js +9 -0
  137. package/extensions/greedysearch-dm/src/types.js +0 -0
  138. package/extensions/greedysearch-dm/src/utils/helpers.js +1 -0
  139. package/package.json +1 -1
  140. package/extensions/dm-9router-ext/src/index.ts +0 -541
  141. package/extensions/dm-ask-user/index.ts +0 -857
  142. package/extensions/dm-context/src/context.ts +0 -158
  143. package/extensions/dm-context/src/index.ts +0 -439
  144. package/extensions/dm-context/src/utils.ts +0 -6
  145. package/extensions/dm-fff/src/index.ts +0 -1023
  146. package/extensions/dm-fff/src/query.ts +0 -87
  147. package/extensions/dm-goal/src/goal.ts +0 -1073
  148. package/extensions/dm-grill-me/index.ts +0 -1085
  149. package/extensions/dm-subagents/src/agents/agent-management.ts +0 -1052
  150. package/extensions/dm-subagents/src/agents/agent-memory.ts +0 -254
  151. package/extensions/dm-subagents/src/agents/agent-scope.ts +0 -6
  152. package/extensions/dm-subagents/src/agents/agent-selection.ts +0 -25
  153. package/extensions/dm-subagents/src/agents/agent-serializer.ts +0 -121
  154. package/extensions/dm-subagents/src/agents/agents.ts +0 -1553
  155. package/extensions/dm-subagents/src/agents/chain-serializer.ts +0 -277
  156. package/extensions/dm-subagents/src/agents/frontmatter.ts +0 -93
  157. package/extensions/dm-subagents/src/agents/identity.ts +0 -30
  158. package/extensions/dm-subagents/src/agents/proactive-skills.ts +0 -191
  159. package/extensions/dm-subagents/src/agents/skills.ts +0 -729
  160. package/extensions/dm-subagents/src/extension/config.ts +0 -39
  161. package/extensions/dm-subagents/src/extension/control-notices.ts +0 -92
  162. package/extensions/dm-subagents/src/extension/doctor.ts +0 -214
  163. package/extensions/dm-subagents/src/extension/fanout-child.ts +0 -172
  164. package/extensions/dm-subagents/src/extension/index.ts +0 -656
  165. package/extensions/dm-subagents/src/extension/rpc.ts +0 -369
  166. package/extensions/dm-subagents/src/extension/schemas.ts +0 -309
  167. package/extensions/dm-subagents/src/intercom/intercom-bridge.ts +0 -180
  168. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.ts +0 -519
  169. package/extensions/dm-subagents/src/intercom/result-intercom.ts +0 -377
  170. package/extensions/dm-subagents/src/profiles/profiles.ts +0 -637
  171. package/extensions/dm-subagents/src/runs/background/async-execution.ts +0 -1065
  172. package/extensions/dm-subagents/src/runs/background/async-job-tracker.ts +0 -441
  173. package/extensions/dm-subagents/src/runs/background/async-resume.ts +0 -391
  174. package/extensions/dm-subagents/src/runs/background/async-status.ts +0 -395
  175. package/extensions/dm-subagents/src/runs/background/chain-append.ts +0 -282
  176. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.ts +0 -191
  177. package/extensions/dm-subagents/src/runs/background/completion-batcher.ts +0 -166
  178. package/extensions/dm-subagents/src/runs/background/completion-dedupe.ts +0 -63
  179. package/extensions/dm-subagents/src/runs/background/control-channel.ts +0 -332
  180. package/extensions/dm-subagents/src/runs/background/fleet-view.ts +0 -515
  181. package/extensions/dm-subagents/src/runs/background/notify.ts +0 -225
  182. package/extensions/dm-subagents/src/runs/background/parallel-groups.ts +0 -45
  183. package/extensions/dm-subagents/src/runs/background/result-watcher.ts +0 -315
  184. package/extensions/dm-subagents/src/runs/background/run-id-resolver.ts +0 -84
  185. package/extensions/dm-subagents/src/runs/background/run-status.ts +0 -434
  186. package/extensions/dm-subagents/src/runs/background/scheduled-runs.ts +0 -514
  187. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.ts +0 -368
  188. package/extensions/dm-subagents/src/runs/background/subagent-runner.ts +0 -3171
  189. package/extensions/dm-subagents/src/runs/background/top-level-async.ts +0 -13
  190. package/extensions/dm-subagents/src/runs/background/wait.ts +0 -353
  191. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.ts +0 -1333
  192. package/extensions/dm-subagents/src/runs/foreground/chain-execution.ts +0 -1313
  193. package/extensions/dm-subagents/src/runs/foreground/execution.ts +0 -1239
  194. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.ts +0 -3613
  195. package/extensions/dm-subagents/src/runs/shared/acceptance.ts +0 -879
  196. package/extensions/dm-subagents/src/runs/shared/chain-outputs.ts +0 -116
  197. package/extensions/dm-subagents/src/runs/shared/completion-guard.ts +0 -147
  198. package/extensions/dm-subagents/src/runs/shared/dm-args.ts +0 -271
  199. package/extensions/dm-subagents/src/runs/shared/dm-spawn.ts +0 -147
  200. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.ts +0 -295
  201. package/extensions/dm-subagents/src/runs/shared/long-running-guard.ts +0 -175
  202. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +0 -365
  203. package/extensions/dm-subagents/src/runs/shared/model-fallback.ts +0 -292
  204. package/extensions/dm-subagents/src/runs/shared/model-scope.ts +0 -128
  205. package/extensions/dm-subagents/src/runs/shared/nested-events.ts +0 -908
  206. package/extensions/dm-subagents/src/runs/shared/nested-path.ts +0 -52
  207. package/extensions/dm-subagents/src/runs/shared/nested-render.ts +0 -115
  208. package/extensions/dm-subagents/src/runs/shared/parallel-utils.ts +0 -198
  209. package/extensions/dm-subagents/src/runs/shared/run-history.ts +0 -60
  210. package/extensions/dm-subagents/src/runs/shared/single-output.ts +0 -180
  211. package/extensions/dm-subagents/src/runs/shared/structured-output.ts +0 -77
  212. package/extensions/dm-subagents/src/runs/shared/subagent-control.ts +0 -223
  213. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.ts +0 -342
  214. package/extensions/dm-subagents/src/runs/shared/tool-budget.ts +0 -74
  215. package/extensions/dm-subagents/src/runs/shared/turn-budget.ts +0 -52
  216. package/extensions/dm-subagents/src/runs/shared/workflow-graph.ts +0 -206
  217. package/extensions/dm-subagents/src/runs/shared/worktree.ts +0 -600
  218. package/extensions/dm-subagents/src/shared/artifacts.ts +0 -113
  219. package/extensions/dm-subagents/src/shared/atomic-json.ts +0 -86
  220. package/extensions/dm-subagents/src/shared/child-transcript.ts +0 -212
  221. package/extensions/dm-subagents/src/shared/file-coalescer.ts +0 -40
  222. package/extensions/dm-subagents/src/shared/fork-context.ts +0 -194
  223. package/extensions/dm-subagents/src/shared/formatters.ts +0 -133
  224. package/extensions/dm-subagents/src/shared/jsonl-writer.ts +0 -81
  225. package/extensions/dm-subagents/src/shared/model-info.ts +0 -78
  226. package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
  227. package/extensions/dm-subagents/src/shared/session-identity.ts +0 -10
  228. package/extensions/dm-subagents/src/shared/session-tokens.ts +0 -44
  229. package/extensions/dm-subagents/src/shared/settings.ts +0 -450
  230. package/extensions/dm-subagents/src/shared/status-format.ts +0 -49
  231. package/extensions/dm-subagents/src/shared/types.ts +0 -1257
  232. package/extensions/dm-subagents/src/shared/utils.ts +0 -554
  233. package/extensions/dm-subagents/src/slash/prompt-template-bridge.ts +0 -420
  234. package/extensions/dm-subagents/src/slash/prompt-workflows.ts +0 -330
  235. package/extensions/dm-subagents/src/slash/slash-bridge.ts +0 -176
  236. package/extensions/dm-subagents/src/slash/slash-commands.ts +0 -1296
  237. package/extensions/dm-subagents/src/slash/slash-live-state.ts +0 -292
  238. package/extensions/dm-subagents/src/tui/render-helpers.ts +0 -80
  239. package/extensions/dm-subagents/src/tui/render.ts +0 -1748
  240. package/extensions/dm-tasks/src/auto-clear.ts +0 -91
  241. package/extensions/dm-tasks/src/index.ts +0 -1145
  242. package/extensions/dm-tasks/src/process-tracker.ts +0 -140
  243. package/extensions/dm-tasks/src/task-store.ts +0 -305
  244. package/extensions/dm-tasks/src/tasks-config.ts +0 -23
  245. package/extensions/dm-tasks/src/types.ts +0 -40
  246. package/extensions/dm-tasks/src/ui/settings-menu.ts +0 -100
  247. package/extensions/dm-tasks/src/ui/task-widget.ts +0 -265
  248. package/extensions/dm-usage/index.ts +0 -1718
  249. package/extensions/greedysearch-dm/index.ts +0 -177
  250. package/extensions/greedysearch-dm/src/formatters/results.ts +0 -163
  251. package/extensions/greedysearch-dm/src/formatters/sources.ts +0 -116
  252. package/extensions/greedysearch-dm/src/formatters/synthesis.ts +0 -87
  253. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.ts +0 -370
  254. package/extensions/greedysearch-dm/src/tools/shared.ts +0 -187
  255. package/extensions/greedysearch-dm/src/types.ts +0 -110
  256. package/extensions/greedysearch-dm/src/utils/helpers.ts +0 -40
@@ -1,656 +0,0 @@
1
- /**
2
- * Subagent Tool
3
- *
4
- * Full-featured subagent with sync and async modes.
5
- * - Sync (default): Streams output, renders markdown, tracks usage
6
- * - Async: Background execution, emits events when done
7
- *
8
- * Modes: single (agent + task), parallel (tasks[]), chain (chain[] with {previous})
9
- * Toggle: async parameter (default: false, configurable via config.json)
10
- *
11
- * Config file: ~/.dm/agent/extensions/subagent/config.json
12
- * { "asyncByDefault": true, "forceTopLevelAsync": true, "maxSubagentDepth": 1, "intercomBridge": { "mode": "always", "instructionFile": "./intercom-bridge.md" }, "worktreeSetupHook": "./scripts/setup-worktree.mjs" }
13
- */
14
-
15
- import { randomUUID } from "node:crypto";
16
- import * as fs from "node:fs";
17
- import * as os from "node:os";
18
- import * as path from "node:path";
19
- import type { AgentToolResult } from "@duckmind/dm-agent-core";
20
- import { keyText, type ExtensionAPI, type ExtensionContext, type ToolDefinition } from "@duckmind/dm-coding-agent";
21
- import { Box, Container, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component } from "@duckmind/dm-tui";
22
- import { discoverAgents } from "../agents/agents.ts";
23
- import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.ts";
24
- import { resolveCurrentSessionId } from "../shared/session-identity.ts";
25
- import { cleanupOldChainDirs } from "../shared/settings.ts";
26
- import { clearLegacyResultAnimationTimer, renderWidget, renderSubagentResult } from "../tui/render.ts";
27
- import { SubagentParams, WaitParams } from "./schemas.ts";
28
- import { createSubagentExecutor, type SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
29
- import { createAsyncJobTracker } from "../runs/background/async-job-tracker.ts";
30
- import { createResultWatcher } from "../runs/background/result-watcher.ts";
31
- import { createScheduledRunManager } from "../runs/background/scheduled-runs.ts";
32
- import { registerSlashCommands } from "../slash/slash-commands.ts";
33
- import { registerPromptTemplateDelegationBridge } from "../slash/prompt-template-bridge.ts";
34
- import { registerSlashSubagentBridge } from "../slash/slash-bridge.ts";
35
- import { createNativeSupervisorChannel } from "../intercom/native-supervisor-channel.ts";
36
- import { registerSubagentRpcBridge } from "./rpc.ts";
37
- import { clearSlashSnapshots, getSlashRenderableSnapshot, resolveSlashMessageDetails, restoreSlashFinalSnapshots, type SlashMessageDetails } from "../slash/slash-live-state.ts";
38
- import { inspectSubagentStatus } from "../runs/background/run-status.ts";
39
- import { waitForSubagents } from "../runs/background/wait.ts";
40
- import registerSubagentNotify, { type SubagentNotifyDetails } from "../runs/background/notify.ts";
41
- import { SUBAGENT_CHILD_ENV, SUBAGENT_PARENT_SESSION_ENV } from "../runs/shared/dm-args.ts";
42
- import { formatDuration, shortenPath } from "../shared/formatters.ts";
43
- import { loadConfig } from "./config.ts";
44
- import { buildSubagentToolDescription } from "./tool-description.ts";
45
- import {
46
- type Details,
47
- type SubagentState,
48
- ASYNC_DIR,
49
- DEFAULT_ARTIFACT_CONFIG,
50
- RESULTS_DIR,
51
- SLASH_RESULT_TYPE,
52
- SLASH_TEXT_RESULT_TYPE,
53
- SUBAGENT_ASYNC_COMPLETE_EVENT,
54
- SUBAGENT_ASYNC_STARTED_EVENT,
55
- SUBAGENT_CONTROL_EVENT,
56
- WIDGET_KEY,
57
- } from "../shared/types.ts";
58
- import {
59
- clearPendingForegroundControlNotices,
60
- formatSubagentControlNotice,
61
- handleSubagentControlNotice,
62
- SUBAGENT_CONTROL_MESSAGE_TYPE,
63
- type SubagentControlMessageDetails,
64
- } from "./control-notices.ts";
65
-
66
- export { loadConfig } from "./config.ts";
67
-
68
- /**
69
- * Derive subagent session base directory from parent session file.
70
- * If parent session is ~/.dm/agent/sessions/abc123.jsonl,
71
- * returns ~/.dm/agent/sessions/abc123/ as the base.
72
- * Callers add runId to create the actual session root: abc123/{runId}/
73
- * Falls back to a unique temp directory if no parent session.
74
- */
75
- function getSubagentSessionRoot(parentSessionFile: string | null): string {
76
- if (parentSessionFile) {
77
- const baseName = path.basename(parentSessionFile, ".jsonl");
78
- const sessionsDir = path.dirname(parentSessionFile);
79
- return path.join(sessionsDir, baseName);
80
- }
81
- return fs.mkdtempSync(path.join(os.tmpdir(), "dm-subagent-session-"));
82
- }
83
-
84
- function expandTilde(p: string): string {
85
- return p.startsWith("~/") ? path.join(os.homedir(), p.slice(2)) : p;
86
- }
87
-
88
- /**
89
- * Create a directory and verify it is actually accessible.
90
- * On Windows with Azure AD/Entra ID, directories created shortly after
91
- * wake-from-sleep can end up with broken NTFS ACLs (null DACL) when the
92
- * cloud SID cannot be resolved without network connectivity. This leaves
93
- * the directory completely inaccessible to the creating user.
94
- */
95
- function ensureAccessibleDir(dirPath: string): void {
96
- fs.mkdirSync(dirPath, { recursive: true });
97
- try {
98
- fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
99
- } catch {
100
- try {
101
- fs.rmSync(dirPath, { recursive: true, force: true });
102
- } catch {
103
- // Best effort: retry mkdir/access even if cleanup fails.
104
- }
105
- fs.mkdirSync(dirPath, { recursive: true });
106
- fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
107
- }
108
- }
109
-
110
- function isSlashResultRunning(result: { details?: Details }): boolean {
111
- return result.details?.progress?.some((entry) => entry.status === "running")
112
- || result.details?.results.some((entry) => entry.progress?.status === "running")
113
- || false;
114
- }
115
-
116
- // Drives the inline running-indicator braille animation for foreground subagent
117
- // results. Foreground runs receive progress only on child events, so the glyph
118
- // (derived from progress fields) would freeze between events. While a result is
119
- // running we tick a frame counter + invalidate() every 80ms so renderSubagentResult
120
- // can blend the frame into runningGlyph and produce a smooth spinner.
121
- function subagentResultIsRunning(result: { details?: Details }): boolean {
122
- return result.details?.progress?.some((entry) => entry.status === "running")
123
- || result.details?.results.some((entry) => entry.progress?.status === "running")
124
- || false;
125
- }
126
-
127
- function ensureSubagentResultAnimation(context: { state: Record<string, unknown>; invalidate?: () => void }): void {
128
- const state = context.state as { subagentResultAnimationTimer?: ReturnType<typeof setInterval>; frame?: number };
129
- if (state.subagentResultAnimationTimer) return;
130
- if (typeof context.invalidate !== "function") return;
131
- if (state.frame === undefined) state.frame = 0;
132
- state.subagentResultAnimationTimer = setInterval(() => {
133
- state.frame = ((state.frame ?? 0) + 1) % 10;
134
- try {
135
- context.invalidate();
136
- } catch {}
137
- }, 80);
138
- }
139
-
140
- function isSlashResultError(result: { details?: Details }): boolean {
141
- return result.details?.results.some((entry) => entry.exitCode !== 0 && entry.progress?.status !== "running") || false;
142
- }
143
-
144
- function isStaleExtensionContextError(error: unknown): boolean {
145
- return error instanceof Error && error.message.includes("Extension context no longer active");
146
- }
147
-
148
- function rebuildSlashResultContainer(
149
- container: Container,
150
- result: AgentToolResult<Details>,
151
- options: { expanded: boolean },
152
- theme: ExtensionContext["ui"]["theme"],
153
- ): void {
154
- container.clear();
155
- container.addChild(new Spacer(1));
156
- const boxTheme = isSlashResultRunning(result) ? "toolPendingBg" : isSlashResultError(result) ? "toolErrorBg" : "toolSuccessBg";
157
- const box = new Box(1, 1, (text: string) => theme.bg(boxTheme, text));
158
- box.addChild(renderSubagentResult(result, options, theme));
159
- container.addChild(box);
160
- }
161
-
162
- function createSlashResultComponent(
163
- details: SlashMessageDetails,
164
- options: { expanded: boolean },
165
- theme: ExtensionContext["ui"]["theme"],
166
- ): Container {
167
- const container = new Container();
168
- let lastVersion = -1;
169
- container.render = (width: number): string[] => {
170
- const snapshot = getSlashRenderableSnapshot(details);
171
- if (snapshot.version !== lastVersion || isSlashResultRunning(snapshot.result)) {
172
- lastVersion = snapshot.version;
173
- rebuildSlashResultContainer(container, snapshot.result, options, theme);
174
- }
175
- return Container.prototype.render.call(container, width);
176
- };
177
- return container;
178
- }
179
-
180
- function parseSubagentNotifyContent(content: string): SubagentNotifyDetails | undefined {
181
- const lines = content.split("\n");
182
- const header = lines[0] ?? "";
183
- const match = header.match(/^Background task (completed|failed|paused): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
184
- if (!match) return undefined;
185
- const body = lines.slice(2);
186
- let sessionIndex = -1;
187
- for (let i = body.length - 1; i >= 1; i--) {
188
- if (body[i - 1]?.trim() === "" && /^(Session|Session file|Session share error):\s+/.test(body[i]!)) {
189
- sessionIndex = i;
190
- break;
191
- }
192
- }
193
- const sessionLine = sessionIndex >= 0 ? body[sessionIndex] : undefined;
194
- const resultLines = sessionIndex >= 0 ? body.slice(0, sessionIndex) : body;
195
- const resultPreview = resultLines.join("\n").trim() || "(no output)";
196
- let sessionLabel: string | undefined;
197
- let sessionValue: string | undefined;
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[2]!,
205
- status: match[1] as SubagentNotifyDetails["status"],
206
- ...(match[3] ? { taskInfo: match[3] } : {}),
207
- resultPreview,
208
- ...(sessionLabel && sessionValue ? { sessionLabel, sessionValue } : {}),
209
- };
210
- }
211
-
212
- class SubagentControlNoticeComponent implements Component {
213
- constructor(
214
- private readonly details: SubagentControlMessageDetails,
215
- private readonly theme: ExtensionContext["ui"]["theme"],
216
- ) {}
217
-
218
- invalidate(): void {}
219
-
220
- render(width: number): string[] {
221
- const eventLabel = this.details.event.type.replaceAll("_", " ");
222
- if (width < 3) return [truncateToWidth(`Subagent ${eventLabel}`, width)];
223
- const bodyWidth = Math.max(1, width - 2);
224
- const borderChar = "─";
225
- const header = ` ⚠ Subagent ${eventLabel}: ${this.details.event.agent} `;
226
- const headerText = truncateToWidth(header, bodyWidth, "");
227
- const headerPadding = Math.max(0, bodyWidth - visibleWidth(headerText));
228
- const lines = [this.theme.fg("accent", `╭${headerText}${borderChar.repeat(headerPadding)}╮`)];
229
-
230
- for (const line of wrapTextWithAnsi(formatSubagentControlNotice(this.details), bodyWidth)) {
231
- const text = truncateToWidth(line, bodyWidth, "");
232
- const padding = Math.max(0, bodyWidth - visibleWidth(text));
233
- lines.push(this.theme.fg("accent", `│${text}${" ".repeat(padding)}│`));
234
- }
235
- lines.push(this.theme.fg("accent", `╰${borderChar.repeat(bodyWidth)}╯`));
236
- return lines;
237
- }
238
- }
239
-
240
- export default function registerSubagentExtension(pi: ExtensionAPI): void {
241
- if (process.env[SUBAGENT_CHILD_ENV] === "1") {
242
- return;
243
- }
244
- const globalStore = globalThis as Record<string, unknown>;
245
- const runtimeCleanupStoreKey = "__piSubagentRuntimeCleanup";
246
- const previousRuntimeCleanup = globalStore[runtimeCleanupStoreKey];
247
- if (typeof previousRuntimeCleanup === "function") {
248
- try {
249
- previousRuntimeCleanup();
250
- } catch {
251
- // Best effort cleanup for stale timers from an older reload.
252
- }
253
- }
254
-
255
- ensureAccessibleDir(RESULTS_DIR);
256
- ensureAccessibleDir(ASYNC_DIR);
257
- cleanupOldChainDirs();
258
-
259
- const config = loadConfig();
260
- const asyncByDefault = config.asyncByDefault === true;
261
- const tempArtifactsDir = getArtifactsDir(null);
262
- cleanupAllArtifactDirs(DEFAULT_ARTIFACT_CONFIG.cleanupDays);
263
-
264
- const state: SubagentState = {
265
- baseCwd: "",
266
- currentSessionId: null,
267
- subagentInProgress: false,
268
- subagentSpawns: { sessionId: null, count: 0 },
269
- asyncJobs: new Map(),
270
- foregroundRuns: new Map(),
271
- foregroundControls: new Map(),
272
- lastForegroundControlId: null,
273
- pendingForegroundControlNotices: new Map(),
274
- cleanupTimers: new Map(),
275
- lastUiContext: null,
276
- poller: null,
277
- completionSeen: new Map(),
278
- watcher: null,
279
- watcherRestartTimer: null,
280
- resultFileCoalescer: {
281
- schedule: () => false,
282
- clear: () => {},
283
- },
284
- };
285
-
286
- const supervisorChannel = createNativeSupervisorChannel(pi, state);
287
- const { startResultWatcher, primeExistingResults, stopResultWatcher } = createResultWatcher(
288
- pi,
289
- state,
290
- RESULTS_DIR,
291
- 10 * 60 * 1000,
292
- );
293
- startResultWatcher();
294
- primeExistingResults();
295
-
296
- const runtimeCleanup = () => {
297
- stopResultWatcher();
298
- scheduledRunManager.stop();
299
- supervisorChannel.dispose();
300
- clearPendingForegroundControlNotices(state);
301
- if (state.poller) {
302
- clearInterval(state.poller);
303
- state.poller = null;
304
- }
305
- };
306
- globalStore[runtimeCleanupStoreKey] = runtimeCleanup;
307
-
308
- const { ensurePoller, handleStarted, handleComplete, resetJobs, restoreActiveJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR);
309
- let executorExecute: ((id: string, params: SubagentParamsLike, signal: AbortSignal, onUpdate: ((r: AgentToolResult<Details>) => void) | undefined, ctx: ExtensionContext) => Promise<AgentToolResult<Details>>) | undefined;
310
- const scheduledRunManager = createScheduledRunManager({
311
- config,
312
- launch: (params, ctx, signal) => {
313
- if (!executorExecute) {
314
- return Promise.resolve({
315
- content: [{ type: "text", text: "Scheduled subagent launch is unavailable (executor not ready)." }],
316
- isError: true,
317
- details: { mode: "management" as const, results: [] },
318
- });
319
- }
320
- return executorExecute(randomUUID(), params, signal, undefined, ctx);
321
- },
322
- });
323
- const executor = createSubagentExecutor({
324
- pi,
325
- state,
326
- config,
327
- asyncByDefault,
328
- handleScheduledRunAction: (params, ctx) => scheduledRunManager.handleToolCall(params, ctx),
329
- tempArtifactsDir,
330
- getSubagentSessionRoot,
331
- expandTilde,
332
- discoverAgents,
333
- });
334
- executorExecute = executor.execute;
335
-
336
- pi.registerMessageRenderer<SlashMessageDetails>(SLASH_RESULT_TYPE, (message, options, theme) => {
337
- const details = resolveSlashMessageDetails(message.details);
338
- if (!details) return undefined;
339
- return createSlashResultComponent(details, options, theme);
340
- });
341
-
342
- pi.registerMessageRenderer<undefined>(SLASH_TEXT_RESULT_TYPE, (message, _options, _theme) => {
343
- const content = typeof message.content === "string"
344
- ? message.content
345
- : message.content
346
- .filter((entry) => entry.type === "text")
347
- .map((entry) => entry.text)
348
- .join("\n");
349
- return new Text(content, 0, 0);
350
- });
351
-
352
- pi.registerMessageRenderer<SubagentNotifyDetails>("subagent-notify", (message, options, theme) => {
353
- const content = typeof message.content === "string" ? message.content : "";
354
- const details = (message.details as SubagentNotifyDetails | undefined) ?? parseSubagentNotifyContent(content);
355
- if (!details) return new Text(content, 0, 0);
356
- const icon = details.status === "completed"
357
- ? theme.fg("success", "✓")
358
- : details.status === "paused"
359
- ? theme.fg("warning", "■")
360
- : theme.fg("error", "✗");
361
- const parts: string[] = [];
362
- if (details.taskInfo) parts.push(details.taskInfo);
363
- if (details.durationMs !== undefined) parts.push(formatDuration(details.durationMs));
364
- let text = `${icon} ${theme.bold(details.agent)} ${theme.fg("dim", details.status)}`;
365
- if (parts.length > 0) text += ` ${theme.fg("dim", "·")} ${parts.map((part) => theme.fg("dim", part)).join(` ${theme.fg("dim", "·")} `)}`;
366
- const trimmedPreview = details.resultPreview.trim();
367
- const previewLines = options.expanded
368
- ? trimmedPreview.split("\n").filter((line) => line.trim())
369
- : [trimmedPreview.split("\n", 1)[0] ?? ""].filter((line) => line.trim());
370
- for (const line of previewLines.length > 0 ? previewLines : ["(no output)"]) {
371
- text += `\n ${theme.fg("dim", `⎿ ${line}`)}`;
372
- }
373
- if (!options.expanded && trimmedPreview.includes("\n")) {
374
- const expandKey = keyText("app.tools.expand");
375
- text += `\n ${theme.fg("dim", `${expandKey} full notification`)}`;
376
- }
377
- if (details.sessionLabel && details.sessionValue) {
378
- text += `\n ${theme.fg("muted", `${details.sessionLabel}: ${shortenPath(details.sessionValue)}`)}`;
379
- }
380
- return new Text(text, 0, 0);
381
- });
382
-
383
- pi.registerMessageRenderer<SubagentControlMessageDetails>(SUBAGENT_CONTROL_MESSAGE_TYPE, (message, _options, theme) => {
384
- const details = message.details as SubagentControlMessageDetails | undefined;
385
- if (!details?.event) return undefined;
386
- const content = typeof message.content === "string" ? message.content : undefined;
387
- return new SubagentControlNoticeComponent({ ...details, noticeText: formatSubagentControlNotice(details, content) }, theme);
388
- });
389
-
390
- const executeSubagentCollapsed = (id: string, params: SubagentParamsLike, signal: AbortSignal, onUpdate: ((result: AgentToolResult<Details>) => void) | undefined, ctx: ExtensionContext) => {
391
- if (ctx.hasUI) ctx.ui.setToolsExpanded(false);
392
- return executor.execute(id, params, signal, onUpdate, ctx);
393
- };
394
-
395
- const slashBridge = registerSlashSubagentBridge({
396
- events: pi.events,
397
- getContext: () => state.lastUiContext,
398
- execute: (id, params, signal, onUpdate, ctx) =>
399
- executeSubagentCollapsed(id, params, signal, onUpdate, ctx),
400
- });
401
-
402
- const promptTemplateBridge = registerPromptTemplateDelegationBridge({
403
- events: pi.events,
404
- getContext: () => state.lastUiContext,
405
- execute: async (requestId, request, signal, ctx, onUpdate) => {
406
- if (request.tasks && request.tasks.length > 0) {
407
- return executeSubagentCollapsed(
408
- requestId,
409
- {
410
- tasks: request.tasks,
411
- context: request.context,
412
- cwd: request.cwd,
413
- worktree: request.worktree,
414
- async: false,
415
- clarify: false,
416
- },
417
- signal,
418
- onUpdate,
419
- ctx,
420
- );
421
- }
422
- return executeSubagentCollapsed(
423
- requestId,
424
- {
425
- agent: request.agent,
426
- task: request.task,
427
- context: request.context,
428
- cwd: request.cwd,
429
- model: request.model,
430
- async: false,
431
- clarify: false,
432
- },
433
- signal,
434
- onUpdate,
435
- ctx,
436
- );
437
- },
438
- });
439
-
440
- const rpcBridge = registerSubagentRpcBridge({
441
- events: pi.events,
442
- getContext: () => state.lastUiContext,
443
- execute: (id, params, signal, onUpdate, ctx) => executor.execute(id, params, signal, onUpdate, ctx),
444
- });
445
-
446
- function effectiveParallelTaskCount(tasks: Array<{ count?: unknown }> | undefined): number {
447
- if (!tasks || tasks.length === 0) return 0;
448
- return tasks.reduce((total, task) => {
449
- const count = typeof task.count === "number" && Number.isInteger(task.count) && task.count >= 1 ? task.count : 1;
450
- return total + count;
451
- }, 0);
452
- }
453
-
454
- const tool: ToolDefinition<typeof SubagentParams, Details> = {
455
- name: "subagent",
456
- label: "Subagent",
457
- description: buildSubagentToolDescription(config),
458
- parameters: SubagentParams,
459
-
460
- execute(id, params, signal, onUpdate, ctx) {
461
- return executeSubagentCollapsed(id, params, signal, onUpdate, ctx);
462
- },
463
-
464
- renderCall(args, theme) {
465
- if (args.action) {
466
- const target = args.agent || args.chainName || "";
467
- return new Text(
468
- `${theme.fg("toolTitle", theme.bold("subagent "))}${args.action}${target ? ` ${theme.fg("accent", target)}` : ""}`,
469
- 0, 0,
470
- );
471
- }
472
- const isParallel = (args.tasks?.length ?? 0) > 0;
473
- const parallelCount = effectiveParallelTaskCount(args.tasks as Array<{ count?: unknown }> | undefined);
474
- const asyncLabel = args.async === true && args.clarify !== true ? theme.fg("warning", " [async]") : "";
475
- if (args.chain?.length)
476
- return new Text(
477
- `${theme.fg("toolTitle", theme.bold("subagent "))}chain (${args.chain.length})${asyncLabel}`,
478
- 0,
479
- 0,
480
- );
481
- if (isParallel)
482
- return new Text(
483
- `${theme.fg("toolTitle", theme.bold("subagent "))}parallel (${parallelCount})${asyncLabel}`,
484
- 0,
485
- 0,
486
- );
487
- return new Text(
488
- `${theme.fg("toolTitle", theme.bold("subagent "))}${theme.fg("accent", args.agent || "?")}${asyncLabel}`,
489
- 0,
490
- 0,
491
- );
492
- },
493
-
494
- renderResult(result, options, theme, context) {
495
- if (subagentResultIsRunning(result)) {
496
- ensureSubagentResultAnimation(context);
497
- } else {
498
- clearLegacyResultAnimationTimer(context);
499
- }
500
- const frame = (context.state as { frame?: number } | undefined)?.frame ?? 0;
501
- return renderSubagentResult(result, options, theme, frame);
502
- },
503
-
504
- };
505
-
506
- pi.registerTool(tool);
507
-
508
- const waitTool: ToolDefinition<typeof WaitParams, Details> = {
509
- name: "wait",
510
- label: "Wait",
511
- description: `Block until background (async) subagent runs started in this session finish, then return.
512
-
513
- 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.
514
-
515
- • { } — 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.
516
- • { all: true } — block until EVERY active run in this session is finished.
517
- • { id: "..." } — wait for one specific run (id or prefix) to finish.
518
- • { timeoutMs: 600000 } — stop waiting after N ms (the runs keep going regardless; default 30 min)
519
-
520
- 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.`,
521
- parameters: WaitParams,
522
- execute(_id, params, signal, _onUpdate, _ctx) {
523
- return waitForSubagents(params, signal, { state, events: pi.events });
524
- },
525
- };
526
- pi.registerTool(waitTool);
527
-
528
- registerSlashCommands(pi, state);
529
-
530
- const eventUnsubscribeStoreKey = "__piSubagentEventUnsubscribes";
531
- const controlNoticeSeenStoreKey = "__piSubagentVisibleControlNotices";
532
- const previousEventUnsubscribes = globalStore[eventUnsubscribeStoreKey];
533
- if (Array.isArray(previousEventUnsubscribes)) {
534
- for (const unsubscribe of previousEventUnsubscribes) {
535
- if (typeof unsubscribe !== "function") continue;
536
- try {
537
- unsubscribe();
538
- } catch {
539
- // Best effort cleanup for stale handlers from an older reload.
540
- }
541
- }
542
- }
543
- registerSubagentNotify(pi, state, { batchConfig: config.completionBatch });
544
-
545
- const existingVisibleControlNotices = globalStore[controlNoticeSeenStoreKey];
546
- const visibleControlNotices = existingVisibleControlNotices instanceof Set ? existingVisibleControlNotices as Set<string> : new Set<string>();
547
- globalStore[controlNoticeSeenStoreKey] = visibleControlNotices;
548
- const controlEventHandler = (payload: unknown) => {
549
- handleSubagentControlNotice({
550
- pi,
551
- state,
552
- visibleControlNotices,
553
- details: payload as SubagentControlMessageDetails,
554
- });
555
- };
556
- const eventUnsubscribes = [
557
- pi.events.on(SUBAGENT_ASYNC_STARTED_EVENT, handleStarted),
558
- pi.events.on(SUBAGENT_ASYNC_COMPLETE_EVENT, handleComplete),
559
- pi.events.on(SUBAGENT_CONTROL_EVENT, controlEventHandler),
560
- rpcBridge.dispose,
561
- ];
562
- globalStore[eventUnsubscribeStoreKey] = eventUnsubscribes;
563
-
564
- pi.on("tool_result", (event, ctx) => {
565
- if (event.toolName !== "subagent") return;
566
- if (!ctx.hasUI) return;
567
- state.lastUiContext = ctx;
568
- if (state.asyncJobs.size > 0) {
569
- renderWidget(ctx, Array.from(state.asyncJobs.values()));
570
- ctx.ui.requestRender?.();
571
- ensurePoller();
572
- }
573
- });
574
-
575
- const cleanupSessionArtifacts = (ctx: ExtensionContext) => {
576
- try {
577
- const sessionFile = ctx.sessionManager.getSessionFile();
578
- if (sessionFile) {
579
- cleanupOldArtifacts(getArtifactsDir(sessionFile), DEFAULT_ARTIFACT_CONFIG.cleanupDays);
580
- }
581
- } catch {
582
- // Cleanup failures should not block session lifecycle events.
583
- }
584
- };
585
-
586
- const resetSessionState = (ctx: ExtensionContext) => {
587
- state.baseCwd = ctx.cwd;
588
- state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
589
- state.subagentSpawns = { sessionId: state.currentSessionId, count: 0 };
590
- // Set DM_SUBAGENT_PARENT_SESSION for permission-system forwarding.
591
- // Only set in the root session (the interactive UI session), not in
592
- // child subagent processes — children inherit the parent's value
593
- // through the process environment at spawn time and must not overwrite
594
- // it with their own session identity.
595
- if (!process.env[SUBAGENT_CHILD_ENV]) {
596
- const sessionId = ctx.sessionManager.getSessionId();
597
- if (sessionId) {
598
- process.env[SUBAGENT_PARENT_SESSION_ENV] = sessionId;
599
- }
600
- }
601
- state.lastUiContext = ctx;
602
- cleanupSessionArtifacts(ctx);
603
- clearPendingForegroundControlNotices(state);
604
- resetJobs(ctx);
605
- restoreActiveJobs(ctx);
606
- scheduledRunManager.bindSession(ctx);
607
- restoreSlashFinalSnapshots(ctx.sessionManager.getEntries());
608
- primeExistingResults();
609
- };
610
-
611
- pi.on("session_start", (_event, ctx) => {
612
- resetSessionState(ctx);
613
- rpcBridge.emitReady(ctx);
614
- supervisorChannel.start();
615
- });
616
-
617
- pi.on("session_shutdown", () => {
618
- delete process.env[SUBAGENT_PARENT_SESSION_ENV];
619
- for (const unsubscribe of eventUnsubscribes) {
620
- try {
621
- unsubscribe();
622
- } catch {
623
- // Best effort cleanup during shutdown.
624
- }
625
- }
626
- if (globalStore[eventUnsubscribeStoreKey] === eventUnsubscribes) {
627
- delete globalStore[eventUnsubscribeStoreKey];
628
- }
629
- stopResultWatcher();
630
- scheduledRunManager.stop();
631
- if (state.poller) clearInterval(state.poller);
632
- state.poller = null;
633
- clearPendingForegroundControlNotices(state);
634
- for (const timer of state.cleanupTimers.values()) {
635
- clearTimeout(timer);
636
- }
637
- state.cleanupTimers.clear();
638
- state.asyncJobs.clear();
639
- clearSlashSnapshots();
640
- slashBridge.cancelAll();
641
- slashBridge.dispose();
642
- promptTemplateBridge.cancelAll();
643
- promptTemplateBridge.dispose();
644
- supervisorChannel.dispose();
645
- if (globalStore[runtimeCleanupStoreKey] === runtimeCleanup) {
646
- delete globalStore[runtimeCleanupStoreKey];
647
- }
648
- try {
649
- if (state.lastUiContext?.hasUI) {
650
- state.lastUiContext.ui.setWidget(WIDGET_KEY, undefined);
651
- }
652
- } catch (error) {
653
- if (!isStaleExtensionContextError(error)) throw error;
654
- }
655
- });
656
- }