@duckmind/dm-windows-x64 0.61.4 → 0.61.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +211 -67
  3. package/extensions/dm-subagents/agents/claude-code-writer.md +15 -0
  4. package/extensions/dm-subagents/agents/claude-code.md +15 -0
  5. package/extensions/dm-subagents/agents/codex-exec-writer.md +15 -0
  6. package/extensions/dm-subagents/agents/codex-exec.md +15 -0
  7. package/extensions/dm-subagents/agents/cursor-agent-writer.md +14 -0
  8. package/extensions/dm-subagents/agents/cursor-agent.md +14 -0
  9. package/extensions/dm-subagents/agents/delegate.md +3 -2
  10. package/extensions/dm-subagents/agents/oracle.md +10 -5
  11. package/extensions/dm-subagents/agents/researcher.md +2 -2
  12. package/extensions/dm-subagents/agents/reviewer.md +17 -7
  13. package/extensions/dm-subagents/agents/scout.md +5 -5
  14. package/extensions/dm-subagents/agents/worker.md +6 -2
  15. package/extensions/dm-subagents/async-retention-discovery-worker.mjs +167 -0
  16. package/extensions/dm-subagents/index.js +4 -0
  17. package/extensions/dm-subagents/inspector-runner.mjs +10 -0
  18. package/extensions/dm-subagents/install.mjs +3 -2
  19. package/extensions/dm-subagents/package.json +2 -2
  20. package/extensions/dm-subagents/prompts/council.md +60 -0
  21. package/extensions/dm-subagents/prompts/parallel-review.md +5 -1
  22. package/extensions/dm-subagents/prompts/review-loop.md +13 -7
  23. package/extensions/dm-subagents/skills/council-mode/SKILL.md +59 -0
  24. package/extensions/dm-subagents/skills/council-mode/references/pass-contracts.md +150 -0
  25. package/extensions/dm-subagents/skills/dm-subagents/SKILL.md +96 -911
  26. package/extensions/dm-subagents/skills/dm-subagents/references/constraints-and-recipes.md +70 -0
  27. package/extensions/dm-subagents/skills/dm-subagents/references/execution-controls.md +539 -0
  28. package/extensions/dm-subagents/skills/dm-subagents/references/management-authoring-rpc.md +161 -0
  29. package/extensions/dm-subagents/skills/dm-subagents/references/multi-lane-orchestration.md +51 -0
  30. package/extensions/dm-subagents/skills/dm-subagents/references/prompting-and-roles.md +295 -0
  31. package/extensions/dm-subagents/skills/dm-subagents/references/review-and-validation.md +73 -0
  32. package/extensions/dm-subagents/src/agents/agent-management.js +716 -484
  33. package/extensions/dm-subagents/src/agents/agent-refinements.js +563 -0
  34. package/extensions/dm-subagents/src/agents/agent-serializer.js +70 -5
  35. package/extensions/dm-subagents/src/agents/agents.js +1447 -299
  36. package/extensions/dm-subagents/src/agents/builtin-names.js +15 -0
  37. package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -7
  38. package/extensions/dm-subagents/src/agents/frontmatter.js +64 -14
  39. package/extensions/dm-subagents/src/agents/identity.js +1 -1
  40. package/extensions/dm-subagents/src/agents/proactive-skills.js +14 -11
  41. package/extensions/dm-subagents/src/agents/runtime-agent-events.js +49 -0
  42. package/extensions/dm-subagents/src/agents/runtime-agent-registry.js +412 -0
  43. package/extensions/dm-subagents/src/agents/skills.js +52 -35
  44. package/extensions/dm-subagents/src/api/agents.js +6 -0
  45. package/extensions/dm-subagents/src/api/background-work.js +151 -0
  46. package/extensions/dm-subagents/src/api/capability-ceiling.js +12 -0
  47. package/extensions/dm-subagents/src/api/control-channel.js +3 -0
  48. package/extensions/dm-subagents/src/api/delegation.js +5 -0
  49. package/extensions/dm-subagents/src/api/dm-args.js +3 -0
  50. package/extensions/dm-subagents/src/api/external-job-provider.js +137 -0
  51. package/extensions/dm-subagents/src/api/external-runs.js +233 -0
  52. package/extensions/dm-subagents/src/api/intercom-bridge.js +3 -0
  53. package/extensions/dm-subagents/src/api/preflight.js +322 -0
  54. package/extensions/dm-subagents/src/api/project-panes.js +11 -0
  55. package/extensions/dm-subagents/src/api/shared-types.js +4 -0
  56. package/extensions/dm-subagents/src/extension/config.js +175 -0
  57. package/extensions/dm-subagents/src/extension/control-notices.js +4 -43
  58. package/extensions/dm-subagents/src/extension/doctor.js +71 -17
  59. package/extensions/dm-subagents/src/extension/fanout-child.js +49 -32
  60. package/extensions/dm-subagents/src/extension/index.js +711 -265
  61. package/extensions/dm-subagents/src/extension/public-execution.js +114 -0
  62. package/extensions/dm-subagents/src/extension/rpc.js +427 -22
  63. package/extensions/dm-subagents/src/extension/schemas.js +158 -65
  64. package/extensions/dm-subagents/src/extension/steering-notices.js +23 -0
  65. package/extensions/dm-subagents/src/extension/subagent-guide.js +31 -0
  66. package/extensions/dm-subagents/src/extension/tool-description.js +92 -74
  67. package/extensions/dm-subagents/src/extension/tool-result.js +7 -0
  68. package/extensions/dm-subagents/src/inspectors/herdr/actions.js +218 -0
  69. package/extensions/dm-subagents/src/inspectors/herdr/client.js +123 -0
  70. package/extensions/dm-subagents/src/inspectors/herdr/focus.js +47 -0
  71. package/extensions/dm-subagents/src/inspectors/herdr/inspector-runner.js +160 -0
  72. package/extensions/dm-subagents/src/inspectors/herdr/project-panes.js +618 -0
  73. package/extensions/dm-subagents/src/inspectors/herdr/session-roots-codec.js +21 -0
  74. package/extensions/dm-subagents/src/inspectors/herdr/shell-command.js +15 -0
  75. package/extensions/dm-subagents/src/integrations/herdr-status.js +377 -0
  76. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +17 -13
  77. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +371 -79
  78. package/extensions/dm-subagents/src/intercom/result-intercom.js +47 -7
  79. package/extensions/dm-subagents/src/missions/actions.js +394 -0
  80. package/extensions/dm-subagents/src/missions/goal-driver.js +149 -0
  81. package/extensions/dm-subagents/src/missions/lifecycle.js +331 -0
  82. package/extensions/dm-subagents/src/missions/store.js +548 -0
  83. package/extensions/dm-subagents/src/missions/types.js +9 -0
  84. package/extensions/dm-subagents/src/missions/workflow-state.js +245 -0
  85. package/extensions/dm-subagents/src/policy/authority.js +37 -0
  86. package/extensions/dm-subagents/src/profiles/profiles.js +36 -18
  87. package/extensions/dm-subagents/src/runs/background/active-async-capacity.js +427 -0
  88. package/extensions/dm-subagents/src/runs/background/active-run-index.js +122 -0
  89. package/extensions/dm-subagents/src/runs/background/async-execution.js +925 -137
  90. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +515 -148
  91. package/extensions/dm-subagents/src/runs/background/async-resume.js +378 -51
  92. package/extensions/dm-subagents/src/runs/background/async-retention.js +828 -0
  93. package/extensions/dm-subagents/src/runs/background/async-status-snapshot.js +31 -0
  94. package/extensions/dm-subagents/src/runs/background/async-status.js +259 -22
  95. package/extensions/dm-subagents/src/runs/background/auto-drain.js +46 -0
  96. package/extensions/dm-subagents/src/runs/background/chain-append.js +50 -15
  97. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +67 -12
  98. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +5 -1
  99. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +3 -11
  100. package/extensions/dm-subagents/src/runs/background/completion-replay.js +245 -0
  101. package/extensions/dm-subagents/src/runs/background/control-channel.js +423 -36
  102. package/extensions/dm-subagents/src/runs/background/fleet-view.js +132 -59
  103. package/extensions/dm-subagents/src/runs/background/index-segment.js +38 -0
  104. package/extensions/dm-subagents/src/runs/background/inspect-rpc.js +373 -0
  105. package/extensions/dm-subagents/src/runs/background/notify.js +357 -57
  106. package/extensions/dm-subagents/src/runs/background/owned-process-tree.js +86 -0
  107. package/extensions/dm-subagents/src/runs/background/process-terminal.js +269 -0
  108. package/extensions/dm-subagents/src/runs/background/result-delivery-ownership.js +34 -0
  109. package/extensions/dm-subagents/src/runs/background/result-files.js +469 -0
  110. package/extensions/dm-subagents/src/runs/background/result-watcher.js +540 -75
  111. package/extensions/dm-subagents/src/runs/background/resume-guidance.js +44 -0
  112. package/extensions/dm-subagents/src/runs/background/retained-children.js +119 -0
  113. package/extensions/dm-subagents/src/runs/background/run-id-query.js +5 -0
  114. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +93 -9
  115. package/extensions/dm-subagents/src/runs/background/run-status.js +303 -32
  116. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +784 -376
  117. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +75 -34
  118. package/extensions/dm-subagents/src/runs/background/steering.js +221 -0
  119. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +3106 -844
  120. package/extensions/dm-subagents/src/runs/background/subagent-wait.js +529 -0
  121. package/extensions/dm-subagents/src/runs/background/terminal-run-index.js +106 -0
  122. package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -1
  123. package/extensions/dm-subagents/src/runs/background/wait-completions.js +155 -0
  124. package/extensions/dm-subagents/src/runs/background/wait-config.js +46 -0
  125. package/extensions/dm-subagents/src/runs/background/wait-subscriptions.js +278 -0
  126. package/extensions/dm-subagents/src/runs/background/wait-tool.js +47 -0
  127. package/extensions/dm-subagents/src/runs/foreground/async-dismiss-action.js +81 -0
  128. package/extensions/dm-subagents/src/runs/foreground/async-steering-action.js +245 -0
  129. package/extensions/dm-subagents/src/runs/foreground/async-stop-action.js +74 -0
  130. package/extensions/dm-subagents/src/runs/foreground/execution.js +1401 -342
  131. package/extensions/dm-subagents/src/runs/foreground/foreground-control.js +133 -0
  132. package/extensions/dm-subagents/src/runs/foreground/foreground-history.js +148 -0
  133. package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +139 -0
  134. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +4278 -1441
  135. package/extensions/dm-subagents/src/runs/foreground/workflow-detach-reconcile.js +278 -0
  136. package/extensions/dm-subagents/src/runs/foreground/workflow-foreground-steering.js +155 -0
  137. package/extensions/dm-subagents/src/runs/shared/abort-recovery.js +97 -0
  138. package/extensions/dm-subagents/src/runs/shared/acceptance.js +597 -148
  139. package/extensions/dm-subagents/src/runs/shared/agent-contract.js +35 -0
  140. package/extensions/dm-subagents/src/runs/shared/async-status-projection.js +472 -0
  141. package/extensions/dm-subagents/src/runs/shared/background-process-options.js +6 -0
  142. package/extensions/dm-subagents/src/runs/shared/capability-ceiling.js +175 -0
  143. package/extensions/dm-subagents/src/runs/shared/child-identity.js +32 -0
  144. package/extensions/dm-subagents/src/runs/shared/child-launch-plan.js +65 -0
  145. package/extensions/dm-subagents/src/runs/shared/child-protocol.js +447 -0
  146. package/extensions/dm-subagents/src/runs/shared/claude-code-adapter.js +120 -0
  147. package/extensions/dm-subagents/src/runs/shared/codex-exec-adapter.js +129 -0
  148. package/extensions/dm-subagents/src/runs/shared/completion-evidence.js +40 -0
  149. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +140 -83
  150. package/extensions/dm-subagents/src/runs/shared/context-mode.js +38 -0
  151. package/extensions/dm-subagents/src/runs/shared/cursor-agent-adapter.js +101 -0
  152. package/extensions/dm-subagents/src/runs/shared/dm-args.js +445 -72
  153. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +27 -16
  154. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +19 -6
  155. package/extensions/dm-subagents/src/runs/shared/extension-bindings.js +81 -0
  156. package/extensions/dm-subagents/src/runs/shared/external-cli-contract.js +134 -0
  157. package/extensions/dm-subagents/src/runs/shared/external-cli-preflight.js +98 -0
  158. package/extensions/dm-subagents/src/runs/shared/external-cli-runner.js +419 -0
  159. package/extensions/dm-subagents/src/runs/shared/external-job-bridge.js +404 -0
  160. package/extensions/dm-subagents/src/runs/shared/external-job-runner.js +334 -0
  161. package/extensions/dm-subagents/src/runs/shared/fast-mode-extension.js +8 -0
  162. package/extensions/dm-subagents/src/runs/shared/host-step-status.js +228 -0
  163. package/extensions/dm-subagents/src/runs/shared/lane-metadata.js +104 -0
  164. package/extensions/dm-subagents/src/runs/shared/launch-cwd.js +17 -0
  165. package/extensions/dm-subagents/src/runs/shared/llm-intent-arbiter.js +190 -0
  166. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +48 -3
  167. package/extensions/dm-subagents/src/runs/shared/mcp-config-sources.js +387 -0
  168. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +212 -137
  169. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-grant.js +131 -0
  170. package/extensions/dm-subagents/src/runs/shared/model-exclusions.js +207 -0
  171. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +225 -55
  172. package/extensions/dm-subagents/src/runs/shared/model-scope.js +85 -28
  173. package/extensions/dm-subagents/src/runs/shared/mutation-evidence.js +182 -0
  174. package/extensions/dm-subagents/src/runs/shared/nested-events.js +264 -102
  175. package/extensions/dm-subagents/src/runs/shared/nested-render.js +15 -5
  176. package/extensions/dm-subagents/src/runs/shared/orca-progress-tabs.js +505 -0
  177. package/extensions/dm-subagents/src/runs/shared/parallel-handoff.js +653 -0
  178. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +29 -12
  179. package/extensions/dm-subagents/src/runs/shared/permissions.js +108 -0
  180. package/extensions/dm-subagents/src/runs/shared/process-signal.js +13 -0
  181. package/extensions/dm-subagents/src/runs/shared/run-fanout-budget.js +257 -0
  182. package/extensions/dm-subagents/src/runs/shared/run-history.js +133 -13
  183. package/extensions/dm-subagents/src/runs/shared/runtime-acknowledged-extensions.js +62 -0
  184. package/extensions/dm-subagents/src/runs/shared/session-lease.js +225 -0
  185. package/extensions/dm-subagents/src/runs/shared/single-output.js +129 -27
  186. package/extensions/dm-subagents/src/runs/shared/spawn-budget.js +95 -0
  187. package/extensions/dm-subagents/src/runs/shared/structured-output.js +129 -10
  188. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +66 -11
  189. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +548 -70
  190. package/extensions/dm-subagents/src/runs/shared/subagent-startup-retry.js +50 -0
  191. package/extensions/dm-subagents/src/runs/shared/task-intent.js +130 -0
  192. package/extensions/dm-subagents/src/runs/shared/tool-availability.js +59 -0
  193. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +7 -5
  194. package/extensions/dm-subagents/src/runs/shared/tool-timeout.js +64 -0
  195. package/extensions/dm-subagents/src/runs/shared/usage-budget.js +74 -0
  196. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +22 -0
  197. package/extensions/dm-subagents/src/runs/shared/worktree-cleanup-plan.js +721 -0
  198. package/extensions/dm-subagents/src/runs/shared/worktree.js +168 -19
  199. package/extensions/dm-subagents/src/shared/accessible-dir.js +35 -0
  200. package/extensions/dm-subagents/src/shared/agent-stream-options.js +3 -0
  201. package/extensions/dm-subagents/src/shared/artifacts.js +170 -11
  202. package/extensions/dm-subagents/src/shared/atomic-json.js +36 -38
  203. package/extensions/dm-subagents/src/shared/capacity-resilient-json.js +77 -0
  204. package/extensions/dm-subagents/src/shared/child-session-name.js +15 -0
  205. package/extensions/dm-subagents/src/shared/child-transcript.js +57 -2
  206. package/extensions/dm-subagents/src/shared/completion-owner.js +7 -0
  207. package/extensions/dm-subagents/src/shared/display-text.js +142 -0
  208. package/extensions/dm-subagents/src/shared/extension-context.js +17 -0
  209. package/extensions/dm-subagents/src/shared/file-coalescer.js +9 -0
  210. package/extensions/dm-subagents/src/shared/file-system-retry.js +56 -0
  211. package/extensions/dm-subagents/src/shared/fork-context.js +96 -33
  212. package/extensions/dm-subagents/src/shared/formatters.js +21 -7
  213. package/extensions/dm-subagents/src/shared/launch-contract.js +94 -0
  214. package/extensions/dm-subagents/src/shared/model-info.js +10 -5
  215. package/extensions/dm-subagents/src/shared/node-executable.js +19 -0
  216. package/extensions/dm-subagents/src/shared/prompt-resources.js +10 -0
  217. package/extensions/dm-subagents/src/shared/pruned-fork.js +427 -0
  218. package/extensions/dm-subagents/src/shared/session-file-trust.js +19 -0
  219. package/extensions/dm-subagents/src/shared/session-tokens.js +14 -3
  220. package/extensions/dm-subagents/src/shared/settings.js +39 -47
  221. package/extensions/dm-subagents/src/shared/shortcuts.js +16 -0
  222. package/extensions/dm-subagents/src/shared/status-format.js +9 -2
  223. package/extensions/dm-subagents/src/shared/thinking-ceiling.js +41 -0
  224. package/extensions/dm-subagents/src/shared/types.js +47 -7
  225. package/extensions/dm-subagents/src/shared/utf8.js +12 -0
  226. package/extensions/dm-subagents/src/shared/utils.js +151 -131
  227. package/extensions/dm-subagents/src/shared/watch-strategy.js +3 -0
  228. package/extensions/dm-subagents/src/shared/workflow-child-permit.js +84 -0
  229. package/extensions/dm-subagents/src/slash/delegation-adapters.js +274 -0
  230. package/extensions/dm-subagents/src/slash/delegation-json.js +113 -0
  231. package/extensions/dm-subagents/src/slash/delegation-request.js +152 -0
  232. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +294 -243
  233. package/extensions/dm-subagents/src/slash/prompt-workflows.js +35 -73
  234. package/extensions/dm-subagents/src/slash/selector.js +101 -0
  235. package/extensions/dm-subagents/src/slash/slash-bridge.js +17 -1
  236. package/extensions/dm-subagents/src/slash/slash-commands.js +722 -732
  237. package/extensions/dm-subagents/src/slash/slash-live-state.js +37 -19
  238. package/extensions/dm-subagents/src/slash/subagents-admin.js +410 -0
  239. package/extensions/dm-subagents/src/tui/fleet-status.js +824 -0
  240. package/extensions/dm-subagents/src/tui/fleet-transcript.js +479 -0
  241. package/extensions/dm-subagents/src/tui/fleet.js +1326 -0
  242. package/extensions/dm-subagents/src/tui/render-helpers.js +22 -0
  243. package/extensions/dm-subagents/src/tui/render.js +1511 -261
  244. package/extensions/dm-subagents/src/watchdog/change-signature.js +220 -0
  245. package/extensions/dm-subagents/src/watchdog/child-status.js +151 -0
  246. package/extensions/dm-subagents/src/watchdog/emission-guard.js +90 -0
  247. package/extensions/dm-subagents/src/watchdog/lsp-diagnostics.js +484 -0
  248. package/extensions/dm-subagents/src/watchdog/model-selection.js +154 -0
  249. package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +138 -0
  250. package/extensions/dm-subagents/src/watchdog/register-child.js +112 -0
  251. package/extensions/dm-subagents/src/watchdog/register-main.js +419 -0
  252. package/extensions/dm-subagents/src/watchdog/render.js +54 -0
  253. package/extensions/dm-subagents/src/watchdog/review.js +251 -0
  254. package/extensions/dm-subagents/src/watchdog/runtime.js +803 -0
  255. package/extensions/dm-subagents/src/watchdog/scope.js +56 -0
  256. package/extensions/dm-subagents/src/watchdog/settings.js +515 -0
  257. package/extensions/dm-subagents/src/watchdog/tool-actions.js +151 -0
  258. package/extensions/dm-subagents/src/watchdog/turn-delta.js +169 -0
  259. package/extensions/dm-subagents/src/watchdog/types.js +29 -0
  260. package/extensions/dm-subagents/src/watchdog/warning-format.js +58 -0
  261. package/extensions/dm-subagents/src/workflows/chat-progress.js +116 -0
  262. package/extensions/dm-subagents/src/workflows/host-command.js +227 -0
  263. package/extensions/dm-subagents/src/workflows/scripted-workflow.js +2011 -0
  264. package/extensions/dm-subagents/src/workflows/workflow-child-summary.js +116 -0
  265. package/extensions/dm-subagents/src/workflows/workflow-preflight.js +243 -0
  266. package/extensions/dm-subagents/src/workflows/workflow-receipt.js +387 -0
  267. package/extensions/dm-subagents/src/workflows/workflow-settlement.js +189 -0
  268. package/package.json +4 -3
  269. package/extensions/dm-fff/package.json +0 -21
  270. package/extensions/dm-fff/src/index.js +0 -691
  271. package/extensions/dm-fff/src/query.js +0 -60
  272. package/extensions/dm-subagents/agents/context-builder.md +0 -46
  273. package/extensions/dm-subagents/agents/planner.md +0 -55
  274. package/extensions/dm-subagents/prompts/parallel-context-build.md +0 -55
  275. package/extensions/dm-subagents/prompts/parallel-handoff-plan.md +0 -61
  276. package/extensions/dm-subagents/src/runs/background/wait.js +0 -206
  277. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +0 -1013
  278. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +0 -981
  279. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +0 -50
@@ -0,0 +1,17 @@
1
+ import * as fs from "node:fs";
2
+ export function preflightLaunchCwd(requestedCwd, effectiveCwd) {
3
+ const resolution = requestedCwd === effectiveCwd ? "" : `
4
+ (resolved from ${JSON.stringify(requestedCwd)})`;
5
+ try {
6
+ if (!fs.statSync(effectiveCwd).isDirectory()) {
7
+ return `Subagent launch aborted: cwd is not a directory: ${effectiveCwd}${resolution}`;
8
+ }
9
+ } catch (error) {
10
+ if (error.code === "ENOENT") {
11
+ return `Subagent launch aborted: cwd does not exist: ${effectiveCwd}${resolution}`;
12
+ }
13
+ return `Subagent launch aborted: cwd could not be accessed: ${effectiveCwd}${resolution}
14
+ ${error instanceof Error ? error.message : String(error)}`;
15
+ }
16
+ return;
17
+ }
@@ -0,0 +1,190 @@
1
+ import { createHash } from "node:crypto";
2
+ import { Agent } from "@duckmind/dm-agent-core";
3
+ import { convertToLlm } from "@duckmind/dm-coding-agent";
4
+ import { streamSimple } from "@duckmind/dm-ai/compat";
5
+ import { Type } from "typebox";
6
+ import { agentStreamOptions } from "../../shared/agent-stream-options.js";
7
+ const COMPLETION_GUARD_ERROR_PREFIX = "Subagent completed without making edits for an implementation task.";
8
+ const DecisionParams = Type.Object({
9
+ classification: Type.String({ enum: ["read_only", "implementation"] }),
10
+ confidence: Type.String({
11
+ enum: ["low", "medium", "high"],
12
+ description: "Confidence in the classification. Only read_only with high confidence rescues a failed run."
13
+ }),
14
+ reason: Type.String({ description: "One concise reason for this classification." })
15
+ }, { additionalProperties: false });
16
+ export function mapArbiterDecision(decision) {
17
+ if (!decision)
18
+ return "unavailable";
19
+ if (decision.classification === "read_only" && decision.confidence === "high")
20
+ return "read-only";
21
+ return "implementation";
22
+ }
23
+ const DEFAULT_ARBITER_TIMEOUT_MS = 1e4;
24
+ function resolveArbiterModel(ctx, options) {
25
+ const registry = ctx.modelRegistry;
26
+ const explicit = options?.model?.trim();
27
+ if (explicit) {
28
+ const [provider, id] = explicit.split("/");
29
+ if (provider && id)
30
+ return registry.find?.(provider, id) ?? null;
31
+ return null;
32
+ }
33
+ if (ctx.model)
34
+ return ctx.model;
35
+ const first = registry.getAvailable?.()[0];
36
+ if (first?.provider && first.id)
37
+ return registry.find?.(first.provider, first.id) ?? null;
38
+ return null;
39
+ }
40
+ function resolveArbiterRuntime(ctx, options) {
41
+ const model = resolveArbiterModel(ctx, options);
42
+ if (!model?.provider || !model.id)
43
+ return null;
44
+ const registry = ctx.modelRegistry;
45
+ const modelApi = model.api;
46
+ const registered = registry.getRegisteredProviderConfig?.(model.provider);
47
+ const baseStreamFn = options?.streamFn ?? (registered?.streamSimple && registered.api === modelApi ? registered.streamSimple : streamSimple);
48
+ return {
49
+ model,
50
+ baseStreamFn,
51
+ timeoutMs: options?.timeoutMs ?? DEFAULT_ARBITER_TIMEOUT_MS
52
+ };
53
+ }
54
+ async function resolveArbiterAuth(ctx, model) {
55
+ const registry = ctx.modelRegistry;
56
+ if (!registry.getApiKeyAndHeaders)
57
+ return {};
58
+ try {
59
+ const auth = await registry.getApiKeyAndHeaders(model);
60
+ if (auth.ok === false)
61
+ return {};
62
+ return {
63
+ ...auth.apiKey ? { apiKey: auth.apiKey } : {},
64
+ ...auth.headers ? { headers: auth.headers } : {},
65
+ ...auth.env ? { env: auth.env } : {}
66
+ };
67
+ } catch {
68
+ return {};
69
+ }
70
+ }
71
+ function authWrappedStreamFn(base, auth) {
72
+ return (model, context, streamOptions) => base(model, context, {
73
+ ...streamOptions ?? {},
74
+ ...auth.apiKey ? { apiKey: auth.apiKey } : {},
75
+ ...auth.env || streamOptions?.env ? { env: { ...auth.env ?? {}, ...streamOptions?.env ?? {} } } : {},
76
+ headers: { ...streamOptions?.headers ?? {}, ...auth.headers ?? {} }
77
+ });
78
+ }
79
+ async function runArbitration(runtime, auth, task) {
80
+ let decision;
81
+ const tool = {
82
+ name: "task_mutation_decision",
83
+ label: "Task mutation decision",
84
+ description: "Classify whether the task instructed file/code changes. Call exactly once.",
85
+ parameters: DecisionParams,
86
+ executionMode: "sequential",
87
+ async execute(_toolCallId, params) {
88
+ if (!decision)
89
+ decision = params;
90
+ return { content: [{ type: "text", text: "Decision recorded." }], details: { recorded: true } };
91
+ }
92
+ };
93
+ const agent = new Agent({
94
+ initialState: {
95
+ systemPrompt: [
96
+ "You classify whether a delegated coding-agent task instructed file or code changes.",
97
+ "A task that asks to review, inspect, verify, report, or summarize is read-only even when it contains words like 'fix' as severity vocabulary ('must-fix items') or conditional change instructions that leave the change optional.",
98
+ "Classify read_only only when the task text alone clearly indicates a read-only outcome. When in doubt, classify implementation.",
99
+ "Set confidence to high only when you are certain the task is read-only; a read_only classification without high confidence is treated as implementation.",
100
+ "The agent's own final message is never evidence: an agent that made no edits may still have failed to implement.",
101
+ "Call task_mutation_decision exactly once with read_only or implementation, a confidence level, and a concise reason."
102
+ ].join(`
103
+ `),
104
+ model: runtime.model,
105
+ tools: [tool]
106
+ },
107
+ convertToLlm,
108
+ ...agentStreamOptions(authWrappedStreamFn(runtime.baseStreamFn, auth)),
109
+ getApiKey: (providerName) => providerName === runtime.model.provider ? auth.apiKey : undefined,
110
+ beforeToolCall: async ({ toolCall }) => toolCall.name === tool.name ? undefined : { block: true, reason: "Only task_mutation_decision is allowed." },
111
+ toolExecution: "sequential"
112
+ });
113
+ try {
114
+ if (task.length > 8000)
115
+ return "unavailable";
116
+ const prompt = `TASK:
117
+ ${task}`;
118
+ await Promise.race([
119
+ agent.prompt(prompt),
120
+ new Promise((_, reject) => {
121
+ const timeout = setTimeout(() => {
122
+ agent.abort();
123
+ reject(new Error("Task mutation arbiter timed out."));
124
+ }, runtime.timeoutMs);
125
+ timeout.unref?.();
126
+ })
127
+ ]);
128
+ if (!decision)
129
+ return "unavailable";
130
+ return mapArbiterDecision(decision);
131
+ } catch {
132
+ return "unavailable";
133
+ }
134
+ }
135
+ export function createTaskMutationArbiter(ctx, options) {
136
+ if (process.env.DM_SUBAGENTS_LLM_INTENT_ARBITER === "0")
137
+ return;
138
+ const runtime = resolveArbiterRuntime(ctx, options);
139
+ if (!runtime)
140
+ return;
141
+ const cache = new Map;
142
+ return async (task) => {
143
+ const key = createHash("sha256").update(task).digest("base64url");
144
+ const cached = cache.get(key);
145
+ if (cached)
146
+ return cached;
147
+ const auth = await resolveArbiterAuth(ctx, runtime.model);
148
+ const verdict = await runArbitration(runtime, auth, task);
149
+ if (cache.size > 200)
150
+ cache.clear();
151
+ cache.set(key, verdict);
152
+ return verdict;
153
+ };
154
+ }
155
+ export function isCompletionGuardFailure(result) {
156
+ return result.error?.startsWith(COMPLETION_GUARD_ERROR_PREFIX) === true;
157
+ }
158
+ export async function arbitrateCompletionGuardRescue(input) {
159
+ if (!input.guardTriggered || !input.arbiter) {
160
+ return { triggered: input.guardTriggered, rescued: false };
161
+ }
162
+ if (input.task.length > 8000) {
163
+ return { triggered: true, rescued: false };
164
+ }
165
+ try {
166
+ const verdict = await input.arbiter(input.task);
167
+ if (verdict === "read-only")
168
+ return { triggered: false, rescued: true };
169
+ return { triggered: true, rescued: false };
170
+ } catch {
171
+ return { triggered: true, rescued: false };
172
+ }
173
+ }
174
+ export async function maybeRescueCompletionGuardFailure(result, task, arbiter) {
175
+ if (!arbiter || !isCompletionGuardFailure(result))
176
+ return false;
177
+ const verdict = await arbitrateWithGuard(arbiter, task);
178
+ if (verdict !== "read-only")
179
+ return false;
180
+ result.exitCode = 0;
181
+ delete result.error;
182
+ return true;
183
+ }
184
+ async function arbitrateWithGuard(arbiter, task) {
185
+ try {
186
+ return await arbiter(task);
187
+ } catch {
188
+ return "unavailable";
189
+ }
190
+ }
@@ -7,7 +7,7 @@ const MUTATING_BASH_PATTERNS = [
7
7
  /(^|[;&|()\s])git\s+apply\b/,
8
8
  /(^|[;&|()\s])patch\s+/,
9
9
  /(^|[;&|()\s])sed\s+[^\n;&|]*\s-i\b/,
10
- /(^|[;&|()\s])perl\s+[^\n;&|]*\s-pi\b/,
10
+ /(^|[;&|()\s])perl\s+[^\n;&|]*\s-dm\b/,
11
11
  /(^|[;&|()]|\n)\s*tee\s+[^|&;]+/,
12
12
  /\b(writeFile|writeFileSync|appendFile|appendFileSync)\b/,
13
13
  /\bwrite_text\s*\(/,
@@ -77,14 +77,59 @@ function hasUnquotedFileRedirection(command) {
77
77
  }
78
78
  return false;
79
79
  }
80
+ function unquotedShellText(command) {
81
+ let inSingle = false;
82
+ let inDouble = false;
83
+ let escaped = false;
84
+ let result = "";
85
+ for (const char of command) {
86
+ if (escaped) {
87
+ result += inSingle || inDouble ? "_" : char;
88
+ escaped = false;
89
+ continue;
90
+ }
91
+ if (char === "\\" && !inSingle) {
92
+ escaped = true;
93
+ result += inDouble ? "_" : char;
94
+ continue;
95
+ }
96
+ if (char === "'" && !inDouble) {
97
+ inSingle = !inSingle;
98
+ continue;
99
+ }
100
+ if (char === '"' && !inSingle) {
101
+ inDouble = !inDouble;
102
+ continue;
103
+ }
104
+ result += inSingle || inDouble ? "_" : char;
105
+ }
106
+ return result;
107
+ }
108
+ function hasMutatingGitCommand(command) {
109
+ const unquoted = unquotedShellText(command);
110
+ for (const segment of unquoted.split(/(?:&&|\|\||[;|()\n])/)) {
111
+ const trimmed = segment.trim();
112
+ if (!trimmed || /^(?:echo|printf)\b/.test(trimmed))
113
+ continue;
114
+ if (/^git\s+(?:(?:(?:-C|--git-dir|--work-tree)\s+\S+|(?:--git-dir|--work-tree)=\S+|--paginate)\s+)*(?:add|commit|push)\b/.test(trimmed))
115
+ return true;
116
+ }
117
+ return false;
118
+ }
80
119
  export function isMutatingBashCommand(command) {
81
- return hasUnquotedFileRedirection(command) || MUTATING_BASH_PATTERNS.some((pattern) => pattern.test(command));
120
+ return hasUnquotedFileRedirection(command) || hasMutatingGitCommand(command) || MUTATING_BASH_PATTERNS.some((pattern) => pattern.test(command));
82
121
  }
83
- export function isMutatingTool(toolName, args) {
122
+ export function isMutatingTool(toolName, args, mutationTools) {
84
123
  if (!toolName)
85
124
  return false;
125
+ if (mutationTools?.includes(toolName))
126
+ return true;
86
127
  if (toolName === "edit" || toolName === "write")
87
128
  return true;
129
+ if (toolName === "cursor") {
130
+ const activityTitle = typeof args?.activityTitle === "string" ? args.activityTitle : "";
131
+ return /^Cursor (?:edit|write)\b/i.test(activityTitle);
132
+ }
88
133
  if (toolName !== "bash")
89
134
  return false;
90
135
  const command = typeof args?.command === "string" ? args.command : "";
@@ -0,0 +1,387 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import { findConfiguredProjectRoot } from "../../agents/agents.js";
5
+ import { getAgentDir, getConfigDirName, getProjectConfigDir } from "../../shared/utils.js";
6
+ const PACKAGE_CONFIG_ROOT = "npm";
7
+ const PACKAGE_GIT_ROOT = "git";
8
+ const PLUGIN_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
9
+ const PLUGIN_MCP_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json";
10
+ const PLUGIN_NAME_PATTERN = /^(?!.*(?:--|\.\.))[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/;
11
+ const PLUGIN_STDIO_FIELDS = new Set(["type", "command", "args", "env", "cwd"]);
12
+ const PLUGIN_HTTP_FIELDS = new Set(["type", "url", "headers"]);
13
+ export function isMcpServerDefinition(value) {
14
+ if (!isRecord(value))
15
+ return false;
16
+ for (const field of ["command", "socket", "cwd", "url", "bearerToken", "bearerTokenEnv", "protocolVersion", "httpTransport", "pluginDataDir"]) {
17
+ if (value[field] !== undefined && typeof value[field] !== "string")
18
+ return false;
19
+ }
20
+ for (const field of ["args", "includeTools", "excludeTools"]) {
21
+ if (value[field] !== undefined && !isStringArray(value[field]))
22
+ return false;
23
+ }
24
+ for (const field of ["env", "headers"]) {
25
+ if (value[field] !== undefined && !isStringRecord(value[field]))
26
+ return false;
27
+ }
28
+ for (const field of ["exposeResources", "literalEnv"]) {
29
+ if (value[field] !== undefined && typeof value[field] !== "boolean")
30
+ return false;
31
+ }
32
+ if (value.requestHeadersCommand !== undefined && !isRequestHeadersCommand(value.requestHeadersCommand))
33
+ return false;
34
+ if (value.auth !== undefined && value.auth !== "oauth" && value.auth !== "bearer" && value.auth !== false)
35
+ return false;
36
+ return value.directTools === undefined || typeof value.directTools === "boolean" || isStringArray(value.directTools);
37
+ }
38
+ function isRecord(value) {
39
+ return value !== null && typeof value === "object" && !Array.isArray(value);
40
+ }
41
+ function isStringArray(value) {
42
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
43
+ }
44
+ function isStringRecord(value) {
45
+ return isRecord(value) && Object.values(value).every((entry) => typeof entry === "string");
46
+ }
47
+ function isRequestHeadersCommand(value) {
48
+ if (!isRecord(value) || typeof value.command !== "string")
49
+ return false;
50
+ if (value.args !== undefined && !isStringArray(value.args))
51
+ return false;
52
+ if (value.env !== undefined && !isStringRecord(value.env))
53
+ return false;
54
+ return value.timeoutMs === undefined || typeof value.timeoutMs === "number" && Number.isFinite(value.timeoutMs);
55
+ }
56
+ export function loadPackageMcpServers(cwd) {
57
+ const servers = {};
58
+ const seen = new Set;
59
+ for (const packageRoot of getConfiguredPackageRoots(cwd)) {
60
+ const manifest = readJson(path.join(packageRoot, "package.json"));
61
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
62
+ continue;
63
+ const packageName = manifest.name;
64
+ if (typeof packageName !== "string" || !packageName)
65
+ continue;
66
+ const mcpValue = manifest.dm?.mcp;
67
+ const configPaths = typeof mcpValue === "string" ? [mcpValue] : Array.isArray(mcpValue) && mcpValue.every((value) => typeof value === "string") ? mcpValue : [];
68
+ const packagePrefix = formatName(packageName, "package");
69
+ for (const configPath of configPaths) {
70
+ const resolvedPath = resolvePackageConfigPath(packageRoot, configPath);
71
+ if (!resolvedPath)
72
+ continue;
73
+ const config = readJson(resolvedPath);
74
+ if (!config || typeof config !== "object" || Array.isArray(config))
75
+ continue;
76
+ const rawServers = config.mcpServers;
77
+ if (!rawServers || typeof rawServers !== "object" || Array.isArray(rawServers))
78
+ continue;
79
+ for (const [serverName, definition] of Object.entries(rawServers)) {
80
+ if (!isMcpServerDefinition(definition))
81
+ continue;
82
+ const normalizedName = `${packagePrefix}__${formatName(serverName, "server")}`;
83
+ if (seen.has(normalizedName))
84
+ continue;
85
+ seen.add(normalizedName);
86
+ servers[normalizedName] = definition;
87
+ }
88
+ }
89
+ }
90
+ return servers;
91
+ }
92
+ export function loadAgentPluginMcpServers(paths, cwd) {
93
+ const servers = {};
94
+ if (!Array.isArray(paths))
95
+ return servers;
96
+ for (const configuredPath of paths) {
97
+ if (typeof configuredPath !== "string")
98
+ continue;
99
+ const pluginRoot = resolvePluginPath(configuredPath, cwd);
100
+ const manifest = readJson(path.join(pluginRoot, "plugin.json"));
101
+ if (!isValidPluginManifest(manifest))
102
+ continue;
103
+ const config = readJson(path.join(pluginRoot, "mcp.json"));
104
+ if (!isValidPluginConfig(config))
105
+ continue;
106
+ const rawServers = config.mcpServers;
107
+ for (const [serverName, rawDefinition] of Object.entries(rawServers)) {
108
+ const definition = translatePluginServer(manifest.name, pluginRoot, rawDefinition);
109
+ if (!definition)
110
+ continue;
111
+ const normalizedName = `${formatName(manifest.name, "plugin")}__${formatName(serverName, "server")}`;
112
+ if (Object.hasOwn(servers, normalizedName))
113
+ continue;
114
+ servers[normalizedName] = definition;
115
+ }
116
+ }
117
+ return servers;
118
+ }
119
+ function getConfiguredPackageRoots(cwd) {
120
+ const roots = [];
121
+ const projectConfigDir = findProjectConfigDir(cwd);
122
+ const sources = [
123
+ { path: path.join(projectConfigDir, "settings.json"), baseDir: projectConfigDir },
124
+ { path: path.join(getAgentDir(), "settings.json"), baseDir: getAgentDir() }
125
+ ];
126
+ for (const source of sources) {
127
+ const settings = readJson(source.path);
128
+ if (!settings || typeof settings !== "object" || Array.isArray(settings))
129
+ continue;
130
+ const packages = settings.packages;
131
+ if (!Array.isArray(packages))
132
+ continue;
133
+ for (const entry of packages) {
134
+ const packageSource = typeof entry === "string" ? entry : entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.source === "string" ? entry.source : undefined;
135
+ if (!packageSource)
136
+ continue;
137
+ const root = resolvePackageRoot(packageSource, source.baseDir);
138
+ if (root && !roots.includes(root))
139
+ roots.push(root);
140
+ }
141
+ }
142
+ return roots;
143
+ }
144
+ function findProjectConfigDir(cwd) {
145
+ const projectRoot = findConfiguredProjectRoot(path.resolve(cwd));
146
+ return projectRoot ? getProjectConfigDir(projectRoot) : path.join(path.resolve(cwd), getConfigDirName());
147
+ }
148
+ function resolvePackageRoot(source, baseDir) {
149
+ const trimmed = source.trim();
150
+ if (!trimmed)
151
+ return;
152
+ if (trimmed.startsWith("npm:")) {
153
+ const packageName = parseNpmPackageName(trimmed);
154
+ return packageName ? resolveContainedPath(path.join(baseDir, PACKAGE_CONFIG_ROOT, "node_modules"), packageName) ?? undefined : undefined;
155
+ }
156
+ if (trimmed.startsWith("git:") || /^(?:https?:\/\/|ssh:\/\/|git@[^:]+:)/.test(trimmed)) {
157
+ const parsed = parseGitPackagePath(trimmed.startsWith("git:") ? trimmed : `git:${trimmed}`);
158
+ return parsed ? path.join(baseDir, PACKAGE_GIT_ROOT, parsed.host, parsed.repoPath) : undefined;
159
+ }
160
+ const localPath = trimmed.startsWith("file:") ? trimmed.slice(5) : trimmed;
161
+ if (localPath === "~")
162
+ return os.homedir();
163
+ if (localPath.startsWith("~/"))
164
+ return path.join(os.homedir(), localPath.slice(2));
165
+ return path.isAbsolute(localPath) ? path.resolve(localPath) : path.resolve(baseDir, localPath);
166
+ }
167
+ function parseNpmPackageName(source) {
168
+ const spec = source.slice(4).trim();
169
+ if (!spec)
170
+ return;
171
+ const match = spec.match(/^(@?[^@]+(?:\/[^@]+)?)(?:@(.+))?$/);
172
+ const packageName = match?.[1] ?? spec;
173
+ return resolveContainedPath("/", packageName) ? packageName : undefined;
174
+ }
175
+ function parseGitPackagePath(source) {
176
+ const spec = source.slice(4).trim();
177
+ if (!spec)
178
+ return;
179
+ let host = "";
180
+ let repoPath = "";
181
+ const scpLike = spec.match(/^git@([^:]+):(.+)$/);
182
+ if (scpLike) {
183
+ host = scpLike[1] ?? "";
184
+ repoPath = scpLike[2] ?? "";
185
+ } else if (/^[a-z][a-z0-9+.-]*:\/\//i.test(spec)) {
186
+ try {
187
+ const url = new URL(spec);
188
+ host = url.hostname;
189
+ repoPath = url.pathname.replace(/^\/+/, "");
190
+ } catch {
191
+ return;
192
+ }
193
+ } else {
194
+ const slashIndex = spec.indexOf("/");
195
+ if (slashIndex < 0)
196
+ return;
197
+ host = spec.slice(0, slashIndex);
198
+ repoPath = spec.slice(slashIndex + 1);
199
+ }
200
+ const normalizedPath = stripGitRef(repoPath).replace(/\.git$/, "").replace(/^\/+/, "");
201
+ if (!host || !isSafePackagePath(host) || !isSafePackagePath(normalizedPath) || normalizedPath.split(/[\\/]/).length < 2) {
202
+ return;
203
+ }
204
+ return { host, repoPath: normalizedPath };
205
+ }
206
+ function stripGitRef(repoPath) {
207
+ const atIndex = repoPath.indexOf("@");
208
+ const hashIndex = repoPath.indexOf("#");
209
+ const refIndex = [atIndex, hashIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
210
+ return refIndex === undefined ? repoPath : repoPath.slice(0, refIndex);
211
+ }
212
+ function isSafePackagePath(value) {
213
+ return value.length > 0 && !path.isAbsolute(value) && value.split(/[\\/]/).every((part) => part.length > 0 && part !== "." && part !== "..");
214
+ }
215
+ function resolvePackageConfigPath(packageRoot, configuredPath) {
216
+ const lexicalPath = resolveContainedPath(packageRoot, configuredPath);
217
+ if (!lexicalPath || !fs.existsSync(lexicalPath))
218
+ return;
219
+ try {
220
+ if (!fs.statSync(lexicalPath).isFile())
221
+ return;
222
+ const packageRealPath = fs.realpathSync(packageRoot);
223
+ const configRealPath = fs.realpathSync(lexicalPath);
224
+ return resolveContainedPath(packageRealPath, configRealPath) ?? undefined;
225
+ } catch {
226
+ return;
227
+ }
228
+ }
229
+ function resolveContainedPath(root, value) {
230
+ const resolved = path.resolve(root, value);
231
+ const relative = path.relative(root, resolved);
232
+ return relative === "" || !relative.startsWith("..") && !relative.startsWith(path.sep) && !path.isAbsolute(relative) ? resolved : undefined;
233
+ }
234
+ function resolvePluginPath(configuredPath, cwd) {
235
+ if (configuredPath === "~")
236
+ return path.resolve(process.env.HOME ?? "", ".");
237
+ if (configuredPath.startsWith("~/"))
238
+ return path.resolve(process.env.HOME ?? "", configuredPath.slice(2));
239
+ return path.isAbsolute(configuredPath) ? path.resolve(configuredPath) : path.resolve(cwd, configuredPath);
240
+ }
241
+ function isValidPluginManifest(value) {
242
+ if (!value || typeof value !== "object" || Array.isArray(value))
243
+ return false;
244
+ const manifest = value;
245
+ return manifest.$schema === PLUGIN_SCHEMA && typeof manifest.name === "string" && manifest.name.length >= 1 && manifest.name.length <= 64 && PLUGIN_NAME_PATTERN.test(manifest.name);
246
+ }
247
+ function isValidPluginConfig(value) {
248
+ if (!value || typeof value !== "object" || Array.isArray(value))
249
+ return false;
250
+ const config = value;
251
+ return config.$schema === PLUGIN_MCP_SCHEMA && Object.keys(config).every((key) => key === "$schema" || key === "mcpServers") && !!config.mcpServers && typeof config.mcpServers === "object" && !Array.isArray(config.mcpServers);
252
+ }
253
+ function translatePluginServer(pluginName, pluginRoot, value) {
254
+ if (!value || typeof value !== "object" || Array.isArray(value))
255
+ return;
256
+ const raw = value;
257
+ if (raw.type === "stdio")
258
+ return translatePluginStdioServer(pluginName, pluginRoot, raw);
259
+ if (raw.type === "streamable-http" || raw.type === "sse")
260
+ return translatePluginHttpServer(raw);
261
+ return;
262
+ }
263
+ function translatePluginStdioServer(pluginName, pluginRoot, raw) {
264
+ if ([...Object.keys(raw)].some((key) => !PLUGIN_STDIO_FIELDS.has(key)))
265
+ return;
266
+ if (typeof raw.command !== "string" || !raw.command)
267
+ return;
268
+ if (!isBareCommand(raw.command) && !raw.command.startsWith("./"))
269
+ return;
270
+ const args = stringArray(raw.args);
271
+ if (raw.args !== undefined && !args)
272
+ return;
273
+ const env = stringRecord(raw.env);
274
+ if (raw.env !== undefined && !env)
275
+ return;
276
+ if (env && (Object.hasOwn(env, "PLUGIN_ROOT") || Object.hasOwn(env, "PLUGIN_DATA")))
277
+ return;
278
+ const pluginDataDir = path.join(getAgentDir(), "agent-plugin-data", pluginName);
279
+ const command = raw.command.startsWith("./") ? resolveContainedPath(pluginRoot, raw.command) : raw.command;
280
+ if (!command)
281
+ return;
282
+ const cwd = resolvePluginCwd(raw.cwd, pluginRoot, pluginDataDir);
283
+ if (!cwd)
284
+ return;
285
+ return {
286
+ command,
287
+ args: (args ?? []).map((value) => expandPluginPlaceholders(value, pluginRoot, pluginDataDir)),
288
+ env: {
289
+ ...Object.fromEntries(Object.entries(env ?? {}).map(([key, value]) => [key, expandPluginPlaceholders(value, pluginRoot, pluginDataDir)])),
290
+ PLUGIN_ROOT: pluginRoot,
291
+ PLUGIN_DATA: pluginDataDir
292
+ },
293
+ cwd,
294
+ pluginDataDir,
295
+ literalEnv: true
296
+ };
297
+ }
298
+ function translatePluginHttpServer(raw) {
299
+ if ([...Object.keys(raw)].some((key) => !PLUGIN_HTTP_FIELDS.has(key)))
300
+ return;
301
+ if (typeof raw.url !== "string" || !isValidPluginUrl(raw.url))
302
+ return;
303
+ const headers = stringRecord(raw.headers);
304
+ if (raw.headers !== undefined && !headers)
305
+ return;
306
+ if (headers) {
307
+ const normalized = new Set;
308
+ for (const key of Object.keys(headers)) {
309
+ const lower = key.toLowerCase();
310
+ if (normalized.has(lower))
311
+ return;
312
+ normalized.add(lower);
313
+ }
314
+ try {
315
+ new Headers(headers);
316
+ } catch {
317
+ return;
318
+ }
319
+ }
320
+ return { url: raw.url, ...headers ? { headers } : {}, httpTransport: raw.type };
321
+ }
322
+ function resolvePluginCwd(value, pluginRoot, pluginDataDir) {
323
+ if (value === undefined)
324
+ return pluginRoot;
325
+ if (typeof value !== "string")
326
+ return;
327
+ if (value.startsWith("./"))
328
+ return resolveContainedPath(pluginRoot, value);
329
+ if (value === "${PLUGIN_ROOT}" || value.startsWith("${PLUGIN_ROOT}/"))
330
+ return resolveContainedPath(pluginRoot, value.replace("${PLUGIN_ROOT}", "."));
331
+ if (value === "${PLUGIN_DATA}" || value.startsWith("${PLUGIN_DATA}/"))
332
+ return resolveContainedPath(pluginDataDir, value.replace("${PLUGIN_DATA}", "."));
333
+ return;
334
+ }
335
+ function expandPluginPlaceholders(value, pluginRoot, pluginDataDir) {
336
+ return value.replaceAll("${PLUGIN_ROOT}", pluginRoot).replaceAll("${PLUGIN_DATA}", pluginDataDir);
337
+ }
338
+ function isBareCommand(command) {
339
+ return !command.includes("/") && !command.includes("\\") && !command.includes("${PLUGIN_ROOT}") && !command.includes("${PLUGIN_DATA}");
340
+ }
341
+ function isValidPluginUrl(value) {
342
+ if (value.includes("${") || value.includes("$env:") || value.includes("{env:"))
343
+ return false;
344
+ let url;
345
+ try {
346
+ url = new URL(value);
347
+ } catch {
348
+ return false;
349
+ }
350
+ if (url.protocol !== "http:" && url.protocol !== "https:")
351
+ return false;
352
+ if (url.username || url.password || url.hash)
353
+ return false;
354
+ if (url.protocol === "https:")
355
+ return true;
356
+ const host = url.hostname.toLowerCase();
357
+ return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(host);
358
+ }
359
+ function stringArray(value) {
360
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : undefined;
361
+ }
362
+ function stringRecord(value) {
363
+ if (!value || typeof value !== "object" || Array.isArray(value))
364
+ return;
365
+ const entries = Object.entries(value);
366
+ if (entries.some(([, entry]) => typeof entry !== "string"))
367
+ return;
368
+ return Object.fromEntries(entries);
369
+ }
370
+ function formatName(value, fallback) {
371
+ return value.replace(/[^A-Za-z0-9_-]+/g, "_").replace(/^[_-]+|[_-]+$/g, "") || fallback;
372
+ }
373
+ function readJson(filePath) {
374
+ let content;
375
+ try {
376
+ content = fs.readFileSync(filePath, "utf-8");
377
+ } catch (error) {
378
+ if (error.code === "ENOENT")
379
+ return;
380
+ throw new Error(`Failed to read JSON file '${filePath}': ${error instanceof Error ? error.message : String(error)}`, { cause: error });
381
+ }
382
+ try {
383
+ return JSON.parse(content);
384
+ } catch (error) {
385
+ throw new Error(`Invalid JSON in '${filePath}': ${error instanceof Error ? error.message : String(error)}`, { cause: error });
386
+ }
387
+ }