@duckmind/dm-darwin-x64 0.43.4 → 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 (251) hide show
  1. package/dm +0 -0
  2. package/extensions/.dm-extensions.json +241 -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/index.js +20 -0
  126. package/extensions/greedysearch-dm/package.json +1 -22
  127. package/extensions/greedysearch-dm/src/formatters/results.js +8 -0
  128. package/extensions/greedysearch-dm/src/formatters/sources.js +1 -0
  129. package/extensions/greedysearch-dm/src/formatters/synthesis.js +1 -0
  130. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +20 -0
  131. package/extensions/greedysearch-dm/src/tools/shared.js +9 -0
  132. package/extensions/greedysearch-dm/src/types.js +0 -0
  133. package/extensions/greedysearch-dm/src/utils/helpers.js +1 -0
  134. package/package.json +1 -1
  135. package/extensions/dm-9router-ext/src/index.ts +0 -541
  136. package/extensions/dm-ask-user/index.ts +0 -857
  137. package/extensions/dm-context/src/context.ts +0 -158
  138. package/extensions/dm-context/src/index.ts +0 -439
  139. package/extensions/dm-context/src/utils.ts +0 -6
  140. package/extensions/dm-fff/src/index.ts +0 -1023
  141. package/extensions/dm-fff/src/query.ts +0 -87
  142. package/extensions/dm-goal/src/goal.ts +0 -1073
  143. package/extensions/dm-grill-me/index.ts +0 -1085
  144. package/extensions/dm-subagents/src/agents/agent-management.ts +0 -1052
  145. package/extensions/dm-subagents/src/agents/agent-memory.ts +0 -254
  146. package/extensions/dm-subagents/src/agents/agent-scope.ts +0 -6
  147. package/extensions/dm-subagents/src/agents/agent-selection.ts +0 -25
  148. package/extensions/dm-subagents/src/agents/agent-serializer.ts +0 -121
  149. package/extensions/dm-subagents/src/agents/agents.ts +0 -1553
  150. package/extensions/dm-subagents/src/agents/chain-serializer.ts +0 -277
  151. package/extensions/dm-subagents/src/agents/frontmatter.ts +0 -93
  152. package/extensions/dm-subagents/src/agents/identity.ts +0 -30
  153. package/extensions/dm-subagents/src/agents/proactive-skills.ts +0 -191
  154. package/extensions/dm-subagents/src/agents/skills.ts +0 -729
  155. package/extensions/dm-subagents/src/extension/config.ts +0 -39
  156. package/extensions/dm-subagents/src/extension/control-notices.ts +0 -92
  157. package/extensions/dm-subagents/src/extension/doctor.ts +0 -214
  158. package/extensions/dm-subagents/src/extension/fanout-child.ts +0 -172
  159. package/extensions/dm-subagents/src/extension/index.ts +0 -656
  160. package/extensions/dm-subagents/src/extension/rpc.ts +0 -369
  161. package/extensions/dm-subagents/src/extension/schemas.ts +0 -309
  162. package/extensions/dm-subagents/src/intercom/intercom-bridge.ts +0 -180
  163. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.ts +0 -519
  164. package/extensions/dm-subagents/src/intercom/result-intercom.ts +0 -377
  165. package/extensions/dm-subagents/src/profiles/profiles.ts +0 -637
  166. package/extensions/dm-subagents/src/runs/background/async-execution.ts +0 -1065
  167. package/extensions/dm-subagents/src/runs/background/async-job-tracker.ts +0 -441
  168. package/extensions/dm-subagents/src/runs/background/async-resume.ts +0 -391
  169. package/extensions/dm-subagents/src/runs/background/async-status.ts +0 -395
  170. package/extensions/dm-subagents/src/runs/background/chain-append.ts +0 -282
  171. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.ts +0 -191
  172. package/extensions/dm-subagents/src/runs/background/completion-batcher.ts +0 -166
  173. package/extensions/dm-subagents/src/runs/background/completion-dedupe.ts +0 -63
  174. package/extensions/dm-subagents/src/runs/background/control-channel.ts +0 -332
  175. package/extensions/dm-subagents/src/runs/background/fleet-view.ts +0 -515
  176. package/extensions/dm-subagents/src/runs/background/notify.ts +0 -225
  177. package/extensions/dm-subagents/src/runs/background/parallel-groups.ts +0 -45
  178. package/extensions/dm-subagents/src/runs/background/result-watcher.ts +0 -315
  179. package/extensions/dm-subagents/src/runs/background/run-id-resolver.ts +0 -84
  180. package/extensions/dm-subagents/src/runs/background/run-status.ts +0 -434
  181. package/extensions/dm-subagents/src/runs/background/scheduled-runs.ts +0 -514
  182. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.ts +0 -368
  183. package/extensions/dm-subagents/src/runs/background/subagent-runner.ts +0 -3171
  184. package/extensions/dm-subagents/src/runs/background/top-level-async.ts +0 -13
  185. package/extensions/dm-subagents/src/runs/background/wait.ts +0 -353
  186. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.ts +0 -1333
  187. package/extensions/dm-subagents/src/runs/foreground/chain-execution.ts +0 -1313
  188. package/extensions/dm-subagents/src/runs/foreground/execution.ts +0 -1239
  189. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.ts +0 -3613
  190. package/extensions/dm-subagents/src/runs/shared/acceptance.ts +0 -879
  191. package/extensions/dm-subagents/src/runs/shared/chain-outputs.ts +0 -116
  192. package/extensions/dm-subagents/src/runs/shared/completion-guard.ts +0 -147
  193. package/extensions/dm-subagents/src/runs/shared/dm-args.ts +0 -271
  194. package/extensions/dm-subagents/src/runs/shared/dm-spawn.ts +0 -147
  195. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.ts +0 -295
  196. package/extensions/dm-subagents/src/runs/shared/long-running-guard.ts +0 -175
  197. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +0 -365
  198. package/extensions/dm-subagents/src/runs/shared/model-fallback.ts +0 -292
  199. package/extensions/dm-subagents/src/runs/shared/model-scope.ts +0 -128
  200. package/extensions/dm-subagents/src/runs/shared/nested-events.ts +0 -908
  201. package/extensions/dm-subagents/src/runs/shared/nested-path.ts +0 -52
  202. package/extensions/dm-subagents/src/runs/shared/nested-render.ts +0 -115
  203. package/extensions/dm-subagents/src/runs/shared/parallel-utils.ts +0 -198
  204. package/extensions/dm-subagents/src/runs/shared/run-history.ts +0 -60
  205. package/extensions/dm-subagents/src/runs/shared/single-output.ts +0 -180
  206. package/extensions/dm-subagents/src/runs/shared/structured-output.ts +0 -77
  207. package/extensions/dm-subagents/src/runs/shared/subagent-control.ts +0 -223
  208. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.ts +0 -342
  209. package/extensions/dm-subagents/src/runs/shared/tool-budget.ts +0 -74
  210. package/extensions/dm-subagents/src/runs/shared/turn-budget.ts +0 -52
  211. package/extensions/dm-subagents/src/runs/shared/workflow-graph.ts +0 -206
  212. package/extensions/dm-subagents/src/runs/shared/worktree.ts +0 -600
  213. package/extensions/dm-subagents/src/shared/artifacts.ts +0 -113
  214. package/extensions/dm-subagents/src/shared/atomic-json.ts +0 -86
  215. package/extensions/dm-subagents/src/shared/child-transcript.ts +0 -212
  216. package/extensions/dm-subagents/src/shared/file-coalescer.ts +0 -40
  217. package/extensions/dm-subagents/src/shared/fork-context.ts +0 -194
  218. package/extensions/dm-subagents/src/shared/formatters.ts +0 -133
  219. package/extensions/dm-subagents/src/shared/jsonl-writer.ts +0 -81
  220. package/extensions/dm-subagents/src/shared/model-info.ts +0 -78
  221. package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
  222. package/extensions/dm-subagents/src/shared/session-identity.ts +0 -10
  223. package/extensions/dm-subagents/src/shared/session-tokens.ts +0 -44
  224. package/extensions/dm-subagents/src/shared/settings.ts +0 -450
  225. package/extensions/dm-subagents/src/shared/status-format.ts +0 -49
  226. package/extensions/dm-subagents/src/shared/types.ts +0 -1257
  227. package/extensions/dm-subagents/src/shared/utils.ts +0 -554
  228. package/extensions/dm-subagents/src/slash/prompt-template-bridge.ts +0 -420
  229. package/extensions/dm-subagents/src/slash/prompt-workflows.ts +0 -330
  230. package/extensions/dm-subagents/src/slash/slash-bridge.ts +0 -176
  231. package/extensions/dm-subagents/src/slash/slash-commands.ts +0 -1296
  232. package/extensions/dm-subagents/src/slash/slash-live-state.ts +0 -292
  233. package/extensions/dm-subagents/src/tui/render-helpers.ts +0 -80
  234. package/extensions/dm-subagents/src/tui/render.ts +0 -1748
  235. package/extensions/dm-tasks/src/auto-clear.ts +0 -91
  236. package/extensions/dm-tasks/src/index.ts +0 -1145
  237. package/extensions/dm-tasks/src/process-tracker.ts +0 -140
  238. package/extensions/dm-tasks/src/task-store.ts +0 -305
  239. package/extensions/dm-tasks/src/tasks-config.ts +0 -23
  240. package/extensions/dm-tasks/src/types.ts +0 -40
  241. package/extensions/dm-tasks/src/ui/settings-menu.ts +0 -100
  242. package/extensions/dm-tasks/src/ui/task-widget.ts +0 -265
  243. package/extensions/dm-usage/index.ts +0 -1718
  244. package/extensions/greedysearch-dm/index.ts +0 -177
  245. package/extensions/greedysearch-dm/src/formatters/results.ts +0 -163
  246. package/extensions/greedysearch-dm/src/formatters/sources.ts +0 -116
  247. package/extensions/greedysearch-dm/src/formatters/synthesis.ts +0 -87
  248. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.ts +0 -370
  249. package/extensions/greedysearch-dm/src/tools/shared.ts +0 -187
  250. package/extensions/greedysearch-dm/src/types.ts +0 -110
  251. 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
- }