@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,180 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as os from "node:os";
3
- import * as path from "node:path";
4
- import type { AgentConfig } from "../agents/agents.ts";
5
- import type { ExtensionConfig, IntercomBridgeConfig, IntercomBridgeMode } from "../shared/types.ts";
6
- import { getAgentDir } from "../shared/utils.ts";
7
-
8
- export const NATIVE_INTERCOM_EXTENSION_DIR = "native:dm-subagents-supervisor-channel";
9
-
10
- function defaultAgentDir(): string {
11
- return getAgentDir();
12
- }
13
-
14
- function defaultSubagentConfigDir(agentDir = defaultAgentDir()): string {
15
- return path.join(agentDir, "extensions", "subagent");
16
- }
17
-
18
- const DEFAULT_INTERCOM_TARGET_PREFIX = "subagent-chat";
19
- export const INTERCOM_BRIDGE_MARKER = "Intercom orchestration channel:";
20
- const DEFAULT_INTERCOM_BRIDGE_TEMPLATE = `The inherited thread is reference-only. Do not continue that conversation or send questions, status updates, or completion handoffs to the supervisor in normal assistant text.
21
-
22
- Use contact_supervisor first. It resolves the supervisor session "{orchestratorTarget}" and run metadata automatically.
23
- - Need a decision, blocked, approval, or product/API/scope ambiguity: contact_supervisor({ reason: "need_decision", message: "<question>" })
24
- - After contact_supervisor with reason "need_decision", stay alive and continue only after the reply arrives. Do not finish your final response with a choose-one question.
25
- - Do not ask for clarification when the only conflict is review-only/no-edit versus progress-writing or artifact-writing instructions. Review-only/no-edit wins; leave files unchanged and mention the conflict in your final result only if it matters.
26
- - Meaningful progress or unexpected discoveries that change the plan: contact_supervisor({ reason: "progress_update", message: "UPDATE: <summary>" })
27
- - Generic intercom is lower-level plumbing/fallback only: intercom({ action: "ask", to: "{orchestratorTarget}", message: "<question>" })
28
-
29
- Do not use contact_supervisor or intercom for routine completion handoffs. If no coordination is needed, return a focused task result.`;
30
-
31
- export interface IntercomBridgeState {
32
- active: boolean;
33
- mode: IntercomBridgeMode;
34
- orchestratorTarget?: string;
35
- extensionDir: string;
36
- instruction: string;
37
- }
38
-
39
- export interface IntercomBridgeDiagnostic {
40
- active: boolean;
41
- mode: IntercomBridgeMode;
42
- wantsIntercom: boolean;
43
- supervisorChannelAvailable: boolean;
44
- extensionDir: string;
45
- orchestratorTarget?: string;
46
- reason?: string;
47
- }
48
-
49
- interface ResolveIntercomBridgeInput {
50
- config: ExtensionConfig["intercomBridge"];
51
- context: "fresh" | "fork" | undefined;
52
- orchestratorTarget?: string;
53
- settingsDir?: string;
54
- agentDir?: string;
55
- }
56
-
57
- export function resolveIntercomSessionTarget(sessionName: string | undefined, sessionId: string): string {
58
- const trimmedName = sessionName?.trim();
59
- if (trimmedName) return trimmedName;
60
- const normalizedSessionId = sessionId.startsWith("session-") ? sessionId.slice("session-".length) : sessionId;
61
- return `${DEFAULT_INTERCOM_TARGET_PREFIX}-${normalizedSessionId.slice(0, 8)}`;
62
- }
63
-
64
- function sanitizeIntercomTargetPart(value: string): string {
65
- return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
66
- }
67
-
68
- export function resolveSubagentIntercomTarget(runId: string, agent: string, index?: number): string {
69
- const stepSuffix = index !== undefined ? `-${index + 1}` : "";
70
- return `subagent-${sanitizeIntercomTargetPart(agent)}-${sanitizeIntercomTargetPart(runId)}${stepSuffix}`;
71
- }
72
-
73
- export function resolveIntercomBridgeMode(value: unknown): IntercomBridgeMode {
74
- if (value === "off" || value === "always" || value === "fork-only") return value;
75
- return "always";
76
- }
77
-
78
- function resolveIntercomBridgeConfig(value: ExtensionConfig["intercomBridge"]): Required<IntercomBridgeConfig> {
79
- if (!value || typeof value !== "object" || Array.isArray(value)) {
80
- return { mode: "always", instructionFile: "" };
81
- }
82
- return {
83
- mode: resolveIntercomBridgeMode(value.mode),
84
- instructionFile: typeof value.instructionFile === "string" ? value.instructionFile : "",
85
- };
86
- }
87
-
88
- function expandTilde(filePath: string): string {
89
- return filePath.startsWith("~/") ? path.join(os.homedir(), filePath.slice(2)) : filePath;
90
- }
91
-
92
- function resolveInstructionTemplate(instructionFile: string, settingsDir: string): string {
93
- if (!instructionFile) return DEFAULT_INTERCOM_BRIDGE_TEMPLATE;
94
- const expandedPath = expandTilde(instructionFile);
95
- const resolvedPath = path.isAbsolute(expandedPath)
96
- ? expandedPath
97
- : path.resolve(settingsDir, expandedPath);
98
- try {
99
- return fs.readFileSync(resolvedPath, "utf-8");
100
- } catch (error) {
101
- console.warn(`Failed to read intercom bridge instructionFile at '${resolvedPath}'. Using default instructions.`, error);
102
- return DEFAULT_INTERCOM_BRIDGE_TEMPLATE;
103
- }
104
- }
105
-
106
- function buildIntercomBridgeInstruction(orchestratorTarget: string, template: string): string {
107
- const instruction = template.replaceAll("{orchestratorTarget}", orchestratorTarget).trim();
108
- if (instruction.startsWith(INTERCOM_BRIDGE_MARKER)) return instruction;
109
- return `${INTERCOM_BRIDGE_MARKER}\n${instruction}`;
110
- }
111
-
112
- function inactiveReason(mode: IntercomBridgeMode, context: "fresh" | "fork" | undefined, orchestratorTarget: string | undefined): string | undefined {
113
- if (mode === "off") return "bridge mode is off";
114
- if (mode === "fork-only" && context !== "fork") return "bridge mode is fork-only and context is not fork";
115
- if (!orchestratorTarget) return "orchestrator target is not available";
116
- return undefined;
117
- }
118
-
119
- export function diagnoseIntercomBridge(input: ResolveIntercomBridgeInput): IntercomBridgeDiagnostic {
120
- const config = resolveIntercomBridgeConfig(input.config);
121
- const mode = config.mode;
122
- const orchestratorTarget = input.orchestratorTarget?.trim();
123
- const wantsIntercom = mode !== "off" && !(mode === "fork-only" && input.context !== "fork");
124
- const reason = inactiveReason(mode, input.context, orchestratorTarget);
125
- return {
126
- active: reason === undefined,
127
- mode,
128
- wantsIntercom,
129
- supervisorChannelAvailable: true,
130
- extensionDir: NATIVE_INTERCOM_EXTENSION_DIR,
131
- ...(orchestratorTarget ? { orchestratorTarget } : {}),
132
- ...(reason ? { reason } : {}),
133
- };
134
- }
135
-
136
- export function resolveIntercomBridge(input: ResolveIntercomBridgeInput): IntercomBridgeState {
137
- const config = resolveIntercomBridgeConfig(input.config);
138
- const mode = config.mode;
139
- const orchestratorTarget = input.orchestratorTarget?.trim();
140
- const agentDir = path.resolve(input.agentDir ?? defaultAgentDir());
141
- const settingsDir = path.resolve(input.settingsDir ?? defaultSubagentConfigDir(agentDir));
142
- const defaultInstruction = buildIntercomBridgeInstruction(
143
- orchestratorTarget || "{orchestratorTarget}",
144
- DEFAULT_INTERCOM_BRIDGE_TEMPLATE,
145
- );
146
- const reason = inactiveReason(mode, input.context, orchestratorTarget);
147
- if (reason || !orchestratorTarget) {
148
- return { active: false, mode, extensionDir: NATIVE_INTERCOM_EXTENSION_DIR, instruction: defaultInstruction };
149
- }
150
- return {
151
- active: true,
152
- mode,
153
- orchestratorTarget,
154
- extensionDir: NATIVE_INTERCOM_EXTENSION_DIR,
155
- instruction: buildIntercomBridgeInstruction(orchestratorTarget, resolveInstructionTemplate(config.instructionFile, settingsDir)),
156
- };
157
- }
158
-
159
- export function applyIntercomBridgeToAgent(agent: AgentConfig, bridge: IntercomBridgeState): AgentConfig {
160
- if (!bridge.active || !bridge.orchestratorTarget) return agent;
161
-
162
- const bridgeTools = ["intercom", "contact_supervisor"];
163
- const tools = agent.tools
164
- ? [...agent.tools, ...bridgeTools.filter((tool) => !agent.tools?.includes(tool))]
165
- : agent.tools;
166
- const instruction = bridge.instruction;
167
- const trimmedPrompt = agent.systemPrompt?.trim() || "";
168
- const systemPrompt = trimmedPrompt.includes(INTERCOM_BRIDGE_MARKER)
169
- ? trimmedPrompt
170
- : trimmedPrompt
171
- ? `${trimmedPrompt}\n\n${instruction}`
172
- : instruction;
173
-
174
- if (tools === agent.tools && systemPrompt === agent.systemPrompt) return agent;
175
- return {
176
- ...agent,
177
- tools,
178
- systemPrompt,
179
- };
180
- }
@@ -1,519 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import * as fs from "node:fs";
3
- import * as path from "node:path";
4
- import type { AgentToolResult } from "@duckmind/dm-agent-core";
5
- import type { ExtensionAPI, ExtensionContext, ToolDefinition } from "@duckmind/dm-coding-agent";
6
- import { Type } from "typebox";
7
- import {
8
- SUBAGENT_CHILD_AGENT_ENV,
9
- SUBAGENT_CHILD_INDEX_ENV,
10
- SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV,
11
- SUBAGENT_ORCHESTRATOR_TARGET_ENV,
12
- SUBAGENT_RUN_ID_ENV,
13
- SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV,
14
- } from "../runs/shared/dm-args.ts";
15
- import { POLL_INTERVAL_MS, TEMP_ROOT_DIR, type SubagentState } from "../shared/types.ts";
16
- import { writeAtomicJson } from "../shared/atomic-json.ts";
17
-
18
- const SUPERVISOR_CHANNEL_ROOT = path.join(TEMP_ROOT_DIR, "supervisor-channels");
19
- const REQUESTS_DIR = "requests";
20
- const REPLIES_DIR = "replies";
21
- export const NATIVE_SUPERVISOR_TOOL_NAME = "subagent_supervisor";
22
- const MAX_MESSAGE_BYTES = 64 * 1024;
23
- const DEFAULT_ASK_TIMEOUT_MS = 10 * 60 * 1000;
24
- const CHANNEL_POLL_MS = Math.min(POLL_INTERVAL_MS, 500);
25
-
26
- type SupervisorReason = "need_decision" | "interview_request" | "progress_update";
27
-
28
- interface SupervisorRequest {
29
- type: "subagent.supervisor.request";
30
- id: string;
31
- createdAt: number;
32
- reason: SupervisorReason;
33
- message: string;
34
- expectsReply: boolean;
35
- orchestratorTarget?: string;
36
- orchestratorSessionId?: string;
37
- runId: string;
38
- agent: string;
39
- childIndex: number;
40
- childTarget?: string;
41
- interview?: unknown;
42
- }
43
-
44
- interface PendingSupervisorRequest extends SupervisorRequest {
45
- channelDir: string;
46
- requestFile: string;
47
- }
48
-
49
- interface SupervisorReply {
50
- type: "subagent.supervisor.reply";
51
- requestId: string;
52
- createdAt: number;
53
- message: string;
54
- }
55
-
56
- interface ContactSupervisorParams {
57
- reason: SupervisorReason;
58
- message?: string;
59
- interview?: unknown;
60
- }
61
-
62
- interface IntercomParams {
63
- action: "list" | "send" | "ask" | "reply" | "pending" | "status";
64
- to?: string;
65
- message?: string;
66
- replyTo?: string;
67
- }
68
-
69
- const ContactSupervisorParamsSchema = Type.Object({
70
- reason: Type.String({ enum: ["need_decision", "interview_request", "progress_update"] }),
71
- message: Type.Optional(Type.String()),
72
- interview: Type.Optional(Type.Unsafe({ type: "object", additionalProperties: true })),
73
- }, { additionalProperties: false });
74
-
75
- const IntercomParamsSchema = Type.Object({
76
- action: Type.String({ enum: ["list", "send", "ask", "reply", "pending", "status"] }),
77
- to: Type.Optional(Type.String()),
78
- message: Type.Optional(Type.String()),
79
- replyTo: Type.Optional(Type.String()),
80
- }, { additionalProperties: false });
81
-
82
- function safeSegment(value: string): string {
83
- return value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
84
- }
85
-
86
- export function resolveSupervisorChannelDir(runId: string, agent: string, childIndex: number): string {
87
- return path.join(SUPERVISOR_CHANNEL_ROOT, `${safeSegment(runId)}-${safeSegment(agent)}-${childIndex}`);
88
- }
89
-
90
- export function ensureSupervisorChannelDir(channelDir: string): void {
91
- fs.mkdirSync(path.join(channelDir, REQUESTS_DIR), { recursive: true, mode: 0o700 });
92
- fs.mkdirSync(path.join(channelDir, REPLIES_DIR), { recursive: true, mode: 0o700 });
93
- }
94
-
95
- function requestPath(channelDir: string, requestId: string): string {
96
- return path.join(channelDir, REQUESTS_DIR, `${safeSegment(requestId)}.json`);
97
- }
98
-
99
- function replyPath(channelDir: string, requestId: string): string {
100
- return path.join(channelDir, REPLIES_DIR, `${safeSegment(requestId)}.json`);
101
- }
102
-
103
- function readTextEnv(name: string): string | undefined {
104
- const value = process.env[name]?.trim();
105
- return value ? value : undefined;
106
- }
107
-
108
- function readChildMetadata(): {
109
- channelDir: string;
110
- runId: string;
111
- agent: string;
112
- childIndex: number;
113
- orchestratorTarget?: string;
114
- orchestratorSessionId?: string;
115
- childTarget?: string;
116
- } | undefined {
117
- const channelDir = readTextEnv(SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV);
118
- const runId = readTextEnv(SUBAGENT_RUN_ID_ENV);
119
- const agent = readTextEnv(SUBAGENT_CHILD_AGENT_ENV);
120
- const rawIndex = readTextEnv(SUBAGENT_CHILD_INDEX_ENV);
121
- const orchestratorSessionId = readTextEnv(SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV);
122
- if (!channelDir || !runId || !agent || !orchestratorSessionId || rawIndex === undefined || !/^\d+$/.test(rawIndex)) return undefined;
123
- return {
124
- channelDir,
125
- runId,
126
- agent,
127
- childIndex: Number(rawIndex),
128
- orchestratorTarget: readTextEnv(SUBAGENT_ORCHESTRATOR_TARGET_ENV),
129
- orchestratorSessionId,
130
- childTarget: readTextEnv("DM_SUBAGENT_INTERCOM_SESSION_NAME"),
131
- };
132
- }
133
-
134
- function reasonHeading(reason: SupervisorReason): string {
135
- if (reason === "interview_request") return "Subagent requests a structured supervisor interview.";
136
- if (reason === "progress_update") return "Subagent progress update.";
137
- return "Subagent needs a supervisor decision.";
138
- }
139
-
140
- function formatChildMessage(input: {
141
- reason: SupervisorReason;
142
- message?: string;
143
- interview?: unknown;
144
- runId: string;
145
- agent: string;
146
- childIndex: number;
147
- childTarget?: string;
148
- }): string {
149
- const lines = [
150
- reasonHeading(input.reason),
151
- `Run: ${input.runId}`,
152
- `Agent: ${input.agent}`,
153
- `Child index: ${input.childIndex}`,
154
- ];
155
- if (input.childTarget) lines.push(`Child intercom target: ${input.childTarget}`);
156
- lines.push("");
157
- if (input.message?.trim()) lines.push(input.message.trim());
158
- if (input.reason === "interview_request") {
159
- lines.push(
160
- "",
161
- "Structured response requested. Reply with JSON, optionally fenced in ```json, matching the requested interview shape.",
162
- );
163
- if (input.interview !== undefined) lines.push(JSON.stringify(input.interview, null, "\t"));
164
- }
165
- return lines.join("\n").trimEnd();
166
- }
167
-
168
- function parseStructuredReply(message: string): { value?: unknown; error?: string } {
169
- const trimmed = message.trim();
170
- const fenced = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i)?.[1]?.trim();
171
- try {
172
- return { value: JSON.parse(fenced ?? trimmed) };
173
- } catch (error) {
174
- return { error: error instanceof Error ? `${error.name}: ${error.message}` : String(error) };
175
- }
176
- }
177
-
178
- function askTimeoutMs(): number {
179
- const parsed = Number(process.env.PI_INTERCOM_ASK_TIMEOUT_MS);
180
- return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ASK_TIMEOUT_MS;
181
- }
182
-
183
- function delay(ms: number, signal?: AbortSignal): Promise<void> {
184
- return new Promise((resolve, reject) => {
185
- if (signal?.aborted) {
186
- reject(new Error("Supervisor request cancelled."));
187
- return;
188
- }
189
- let timer: ReturnType<typeof setTimeout> | undefined;
190
- const cleanup = () => {
191
- if (timer) clearTimeout(timer);
192
- signal?.removeEventListener("abort", onAbort);
193
- };
194
- const onAbort = () => {
195
- cleanup();
196
- reject(new Error("Supervisor request cancelled."));
197
- };
198
- timer = setTimeout(() => {
199
- cleanup();
200
- resolve();
201
- }, ms);
202
- signal?.addEventListener("abort", onAbort, { once: true });
203
- });
204
- }
205
-
206
- async function waitForReply(channelDir: string, requestId: string, signal?: AbortSignal): Promise<SupervisorReply> {
207
- const file = replyPath(channelDir, requestId);
208
- const deadline = Date.now() + askTimeoutMs();
209
- while (Date.now() <= deadline) {
210
- if (signal?.aborted) throw new Error("Supervisor request cancelled.");
211
- if (fs.existsSync(file)) {
212
- const parsed = JSON.parse(fs.readFileSync(file, "utf-8")) as Partial<SupervisorReply>;
213
- if (parsed.type === "subagent.supervisor.reply" && parsed.requestId === requestId && typeof parsed.message === "string") {
214
- return parsed as SupervisorReply;
215
- }
216
- }
217
- await delay(250, signal);
218
- }
219
- throw new Error("Timed out waiting for supervisor reply.");
220
- }
221
-
222
- async function sendSupervisorRequest(params: ContactSupervisorParams, signal?: AbortSignal): Promise<AgentToolResult<Record<string, unknown>>> {
223
- const metadata = readChildMetadata();
224
- if (!metadata) throw new Error("Native supervisor channel is not available for this subagent.");
225
- if (params.reason !== "progress_update" && !params.message?.trim() && params.reason !== "interview_request") {
226
- throw new Error("message is required for supervisor decisions.");
227
- }
228
- ensureSupervisorChannelDir(metadata.channelDir);
229
- const requestId = randomUUID();
230
- const expectsReply = params.reason !== "progress_update";
231
- const message = formatChildMessage({ ...metadata, reason: params.reason, message: params.message, interview: params.interview });
232
- const request: SupervisorRequest = {
233
- type: "subagent.supervisor.request",
234
- id: requestId,
235
- createdAt: Date.now(),
236
- reason: params.reason,
237
- message,
238
- expectsReply,
239
- ...(metadata.orchestratorTarget ? { orchestratorTarget: metadata.orchestratorTarget } : {}),
240
- ...(metadata.orchestratorSessionId ? { orchestratorSessionId: metadata.orchestratorSessionId } : {}),
241
- runId: metadata.runId,
242
- agent: metadata.agent,
243
- childIndex: metadata.childIndex,
244
- ...(metadata.childTarget ? { childTarget: metadata.childTarget } : {}),
245
- ...(params.interview !== undefined ? { interview: params.interview } : {}),
246
- };
247
- const serialized = JSON.stringify(request, null, "\t");
248
- if (Buffer.byteLength(serialized, "utf-8") > MAX_MESSAGE_BYTES) throw new Error("Supervisor request is too large.");
249
- writeAtomicJson(requestPath(metadata.channelDir, requestId), request);
250
-
251
- if (!expectsReply) {
252
- return {
253
- content: [{ type: "text", text: "Supervisor progress update queued." }],
254
- details: { delivered: true, requestId, reason: params.reason },
255
- };
256
- }
257
-
258
- const reply = await waitForReply(metadata.channelDir, requestId, signal);
259
- const details: Record<string, unknown> = { requestId, reason: params.reason };
260
- if (params.reason === "interview_request") {
261
- const structured = parseStructuredReply(reply.message);
262
- if (structured.error) details.structuredReplyParseError = structured.error;
263
- else details.structuredReply = structured.value;
264
- }
265
- return {
266
- content: [{ type: "text", text: `**Reply from supervisor:**\n${reply.message}` }],
267
- details,
268
- };
269
- }
270
-
271
- function hasTool(pi: ExtensionAPI, name: string): boolean {
272
- try {
273
- return pi.getAllTools?.().some((tool: { name?: unknown }) => tool.name === name) === true;
274
- } catch {
275
- return false;
276
- }
277
- }
278
-
279
- export function registerNativeSupervisorClient(pi: ExtensionAPI, options: { includeIntercomFallback?: boolean } = {}): void {
280
- if (!readChildMetadata()) return;
281
- const includeIntercomFallback = options.includeIntercomFallback !== false;
282
- if (!hasTool(pi, "contact_supervisor")) {
283
- const tool: ToolDefinition<typeof ContactSupervisorParamsSchema, Record<string, unknown>> = {
284
- name: "contact_supervisor",
285
- label: "Contact Supervisor",
286
- description: "Contact the parent/supervisor session for a blocking decision, structured interview, or progress update.",
287
- parameters: ContactSupervisorParamsSchema,
288
- execute(_id, params, signal) {
289
- return sendSupervisorRequest(params as ContactSupervisorParams, signal);
290
- },
291
- };
292
- pi.registerTool(tool);
293
- }
294
- if (includeIntercomFallback && !hasTool(pi, "intercom")) {
295
- const tool: ToolDefinition<typeof IntercomParamsSchema, Record<string, unknown>> = {
296
- name: "intercom",
297
- label: "Intercom",
298
- description: "Native supervisor-channel intercom fallback for subagents. Prefer contact_supervisor when available.",
299
- parameters: IntercomParamsSchema,
300
- async execute(_id, params, signal) {
301
- const action = (params as IntercomParams).action;
302
- if (action === "status") return { content: [{ type: "text", text: "Native supervisor channel is active." }], details: { active: true } };
303
- if (action === "list") return { content: [{ type: "text", text: "Supervisor session available through contact_supervisor." }], details: { sessions: [] } };
304
- if (action === "send") return sendSupervisorRequest({ reason: "progress_update", message: (params as IntercomParams).message ?? "" }, signal);
305
- if (action === "ask") return sendSupervisorRequest({ reason: "need_decision", message: (params as IntercomParams).message ?? "" }, signal);
306
- throw new Error("Native child intercom supports status, list, send, and ask. Use parent intercom reply from the supervisor session.");
307
- },
308
- };
309
- pi.registerTool(tool);
310
- }
311
- }
312
-
313
- function parseRequestFile(file: string, channelDir: string): PendingSupervisorRequest | undefined {
314
- try {
315
- const parsed = JSON.parse(fs.readFileSync(file, "utf-8")) as Partial<SupervisorRequest>;
316
- if (parsed.type !== "subagent.supervisor.request") return undefined;
317
- if (typeof parsed.id !== "string" || !parsed.id) return undefined;
318
- if (parsed.reason !== "need_decision" && parsed.reason !== "interview_request" && parsed.reason !== "progress_update") return undefined;
319
- if (typeof parsed.message !== "string" || !parsed.message) return undefined;
320
- if (typeof parsed.runId !== "string" || typeof parsed.agent !== "string" || typeof parsed.childIndex !== "number") return undefined;
321
- return { ...parsed as SupervisorRequest, channelDir, requestFile: file };
322
- } catch {
323
- return undefined;
324
- }
325
- }
326
-
327
- function listRequestFiles(): Array<{ channelDir: string; file: string }> {
328
- let channelEntries: fs.Dirent[];
329
- try {
330
- channelEntries = fs.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: true });
331
- } catch (error) {
332
- if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
333
- throw error;
334
- }
335
- const files: Array<{ channelDir: string; file: string }> = [];
336
- for (const entry of channelEntries) {
337
- if (!entry.isDirectory()) continue;
338
- const channelDir = path.join(SUPERVISOR_CHANNEL_ROOT, entry.name);
339
- const requestsDir = path.join(channelDir, REQUESTS_DIR);
340
- let requestEntries: fs.Dirent[];
341
- try {
342
- requestEntries = fs.readdirSync(requestsDir, { withFileTypes: true });
343
- } catch {
344
- continue;
345
- }
346
- for (const requestEntry of requestEntries) {
347
- if (requestEntry.isFile() && requestEntry.name.endsWith(".json")) files.push({ channelDir, file: path.join(requestsDir, requestEntry.name) });
348
- }
349
- }
350
- return files;
351
- }
352
-
353
- function currentContextSessionId(state: Pick<SubagentState, "currentSessionId">, ctx: ExtensionContext): string | undefined {
354
- try {
355
- const sessionId = ctx.sessionManager.getSessionId();
356
- if (sessionId) return sessionId;
357
- } catch {
358
- // Fall through to the last known identity.
359
- }
360
- return state.currentSessionId ?? undefined;
361
- }
362
-
363
- function requestMatchesContext(request: SupervisorRequest, state: Pick<SubagentState, "currentSessionId">, ctx: ExtensionContext): boolean {
364
- const currentSessionId = currentContextSessionId(state, ctx);
365
- return Boolean(currentSessionId && request.orchestratorSessionId === currentSessionId);
366
- }
367
-
368
- function formatPendingLine(request: PendingSupervisorRequest): string {
369
- const replyHint = request.expectsReply ? ` Reply: ${NATIVE_SUPERVISOR_TOOL_NAME}({ action: "reply", replyTo: "${request.id}", message: "..." })` : "";
370
- return `- ${request.id}: ${request.agent} [${request.runId}#${request.childIndex}] ${request.reason}.${replyHint}`;
371
- }
372
-
373
- function requestVisibleText(request: PendingSupervisorRequest): string {
374
- const lines = [request.message];
375
- if (request.expectsReply) {
376
- lines.push("", `Reply with: ${NATIVE_SUPERVISOR_TOOL_NAME}({ action: "reply", replyTo: "${request.id}", message: "..." })`);
377
- }
378
- return lines.join("\n");
379
- }
380
-
381
- function writeReply(request: PendingSupervisorRequest, message: string): void {
382
- if (!message.trim()) throw new Error("message is required for supervisor replies.");
383
- const reply: SupervisorReply = {
384
- type: "subagent.supervisor.reply",
385
- requestId: request.id,
386
- createdAt: Date.now(),
387
- message: message.trim(),
388
- };
389
- writeAtomicJson(replyPath(request.channelDir, request.id), reply);
390
- try {
391
- fs.rmSync(request.requestFile, { force: true });
392
- } catch {
393
- // Best effort: the reply file is authoritative for the child.
394
- }
395
- }
396
-
397
- function resolvePendingRequest(pending: Map<string, PendingSupervisorRequest>, params: IntercomParams): PendingSupervisorRequest {
398
- if (params.replyTo) {
399
- const request = pending.get(params.replyTo);
400
- if (!request) throw new Error(`No pending supervisor request found for replyTo '${params.replyTo}'.`);
401
- return request;
402
- }
403
- const requests = [...pending.values()].filter((request) => request.expectsReply);
404
- if (params.to) {
405
- const normalizedTo = params.to.toLowerCase();
406
- const matches = requests.filter((request) =>
407
- request.id.toLowerCase().startsWith(normalizedTo)
408
- || request.agent.toLowerCase() === normalizedTo
409
- || request.childTarget?.toLowerCase() === normalizedTo,
410
- );
411
- if (matches.length === 1) return matches[0]!;
412
- if (matches.length > 1) throw new Error(`Multiple pending supervisor requests match '${params.to}'. Use replyTo.`);
413
- }
414
- if (requests.length === 1) return requests[0]!;
415
- if (requests.length === 0) throw new Error("No pending supervisor requests need a reply.");
416
- throw new Error("Multiple pending supervisor requests need replies. Use replyTo.");
417
- }
418
-
419
- function publicPendingRequests(pending: Map<string, PendingSupervisorRequest>): Array<Record<string, unknown>> {
420
- return [...pending.values()].map((request) => ({
421
- id: request.id,
422
- runId: request.runId,
423
- agent: request.agent,
424
- childIndex: request.childIndex,
425
- reason: request.reason,
426
- expectsReply: request.expectsReply,
427
- }));
428
- }
429
-
430
- function buildParentIntercomTool(pending: Map<string, PendingSupervisorRequest>, name = "intercom"): ToolDefinition<typeof IntercomParamsSchema, Record<string, unknown>> {
431
- return {
432
- name,
433
- label: name === "intercom" ? "Intercom" : "Subagent Supervisor",
434
- description: name === "intercom"
435
- ? "Native dm-subagents supervisor channel. Use reply/pending/status to answer child subagent requests."
436
- : "Native dm-subagents supervisor channel. Use reply/pending/status to answer child subagent requests without overriding the optional intercom compatibility extension.",
437
- parameters: IntercomParamsSchema,
438
- async execute(_id, params) {
439
- const input = params as IntercomParams;
440
- if (input.action === "status") {
441
- return { content: [{ type: "text", text: `Native supervisor channel active. Pending replies: ${pending.size}.` }], details: { active: true, pending: pending.size, root: SUPERVISOR_CHANNEL_ROOT } };
442
- }
443
- if (input.action === "pending" || input.action === "list") {
444
- const lines = [...pending.values()].filter((request) => request.expectsReply).map(formatPendingLine);
445
- return { content: [{ type: "text", text: lines.length ? lines.join("\n") : "No pending supervisor requests." }], details: { pending: publicPendingRequests(pending) } };
446
- }
447
- if (input.action === "reply") {
448
- const request = resolvePendingRequest(pending, input);
449
- writeReply(request, input.message ?? "");
450
- pending.delete(request.id);
451
- return { content: [{ type: "text", text: `Replied to supervisor request ${request.id}.` }], details: { replyTo: request.id, runId: request.runId, agent: request.agent } };
452
- }
453
- if (input.action === "send" || input.action === "ask") {
454
- throw new Error("Native dm-subagents intercom currently handles supervisor replies. Child agents initiate asks with contact_supervisor.");
455
- }
456
- throw new Error(`Unsupported intercom action: ${input.action}`);
457
- },
458
- };
459
- }
460
-
461
- export function createNativeSupervisorChannel(pi: ExtensionAPI, state: SubagentState): { start: () => void; dispose: () => void; pending: Map<string, PendingSupervisorRequest> } {
462
- const pending = new Map<string, PendingSupervisorRequest>();
463
- const seenFiles = new Set<string>();
464
- let poller: ReturnType<typeof setInterval> | undefined;
465
-
466
- const registerParentTools = (): void => {
467
- if (!hasTool(pi, NATIVE_SUPERVISOR_TOOL_NAME)) pi.registerTool(buildParentIntercomTool(pending, NATIVE_SUPERVISOR_TOOL_NAME));
468
- if (!hasTool(pi, "intercom")) pi.registerTool(buildParentIntercomTool(pending));
469
- };
470
-
471
- const poll = (): void => {
472
- const ctx = state.lastUiContext;
473
- if (!ctx) return;
474
- for (const { channelDir, file } of listRequestFiles()) {
475
- if (seenFiles.has(file)) continue;
476
- const request = parseRequestFile(file, channelDir);
477
- if (!request || !requestMatchesContext(request, state, ctx)) continue;
478
- seenFiles.add(file);
479
- if (request.expectsReply) pending.set(request.id, request);
480
- else {
481
- try {
482
- fs.rmSync(request.requestFile, { force: true });
483
- } catch {
484
- // Non-blocking progress updates are already delivered to this session.
485
- }
486
- }
487
- pi.sendMessage({
488
- customType: "subagent_supervisor_request",
489
- content: requestVisibleText(request),
490
- display: true,
491
- details: {
492
- id: request.id,
493
- reason: request.reason,
494
- expectsReply: request.expectsReply,
495
- runId: request.runId,
496
- agent: request.agent,
497
- childIndex: request.childIndex,
498
- },
499
- });
500
- }
501
- };
502
-
503
- return {
504
- start: () => {
505
- if (poller) return;
506
- registerParentTools();
507
- poll();
508
- poller = setInterval(poll, CHANNEL_POLL_MS);
509
- poller.unref?.();
510
- },
511
- dispose: () => {
512
- if (poller) clearInterval(poller);
513
- poller = undefined;
514
- pending.clear();
515
- seenFiles.clear();
516
- },
517
- pending,
518
- };
519
- }