mixdog 0.9.1 → 0.9.3

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 (301) hide show
  1. package/package.json +9 -1
  2. package/scripts/_bench-cwc.json +20 -0
  3. package/scripts/agent-loop-policy-test.mjs +37 -0
  4. package/scripts/agent-parallel-smoke.mjs +54 -10
  5. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  6. package/scripts/background-task-meta-smoke.mjs +1 -1
  7. package/scripts/bench-run.mjs +262 -0
  8. package/scripts/build-tui.mjs +13 -1
  9. package/scripts/compact-smoke.mjs +12 -0
  10. package/scripts/compact-trigger-migration-smoke.mjs +67 -1
  11. package/scripts/explore-bench.mjs +124 -0
  12. package/scripts/hook-bus-test.mjs +191 -0
  13. package/scripts/ingest-pure-conversation-smoke.mjs +148 -0
  14. package/scripts/internal-comms-bench.mjs +727 -0
  15. package/scripts/internal-comms-smoke.mjs +75 -0
  16. package/scripts/lead-workflow-smoke.mjs +4 -4
  17. package/scripts/live-worker-smoke.mjs +9 -9
  18. package/scripts/output-style-bench.mjs +285 -0
  19. package/scripts/output-style-smoke.mjs +13 -10
  20. package/scripts/patch-replay.mjs +90 -0
  21. package/scripts/path-suffix-test.mjs +57 -0
  22. package/scripts/provider-stream-stall-test.mjs +276 -0
  23. package/scripts/provider-toolcall-test.mjs +599 -1
  24. package/scripts/recall-bench.mjs +207 -0
  25. package/scripts/routing-corpus.mjs +281 -0
  26. package/scripts/session-bench.mjs +1526 -0
  27. package/scripts/session-diag.mjs +595 -0
  28. package/scripts/task-bench.mjs +207 -0
  29. package/scripts/tool-failures.mjs +6 -6
  30. package/scripts/tool-smoke.mjs +310 -67
  31. package/scripts/toolcall-args-test.mjs +81 -0
  32. package/src/agents/debugger/AGENT.md +4 -4
  33. package/src/agents/heavy-worker/AGENT.md +20 -9
  34. package/src/agents/reviewer/AGENT.md +4 -4
  35. package/src/agents/worker/AGENT.md +17 -9
  36. package/src/app.mjs +10 -6
  37. package/src/defaults/{hidden-roles.json → agents.json} +7 -7
  38. package/src/examples/schedules/SCHEDULE.example.md +32 -0
  39. package/src/examples/webhooks/WEBHOOK.example.md +40 -0
  40. package/src/headless-role.mjs +14 -14
  41. package/src/help.mjs +1 -0
  42. package/src/lib/rules-builder.cjs +32 -54
  43. package/src/mixdog-session-runtime.mjs +1040 -2036
  44. package/src/output-styles/default.md +12 -7
  45. package/src/output-styles/minimal.md +25 -0
  46. package/src/output-styles/oneline.md +21 -0
  47. package/src/output-styles/simple.md +10 -9
  48. package/src/repl.mjs +17 -7
  49. package/src/rules/agent/00-common.md +7 -5
  50. package/src/rules/agent/30-explorer.md +8 -12
  51. package/src/rules/lead/01-general.md +3 -1
  52. package/src/rules/lead/lead-tool.md +13 -2
  53. package/src/rules/shared/01-tool.md +23 -12
  54. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +90 -32
  55. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +32 -0
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +18 -6
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +23 -20
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +48 -14
  59. package/src/runtime/agent/orchestrator/agent-trace.mjs +87 -12
  60. package/src/runtime/agent/orchestrator/config.mjs +3 -0
  61. package/src/runtime/agent/orchestrator/context/collect.mjs +182 -67
  62. package/src/runtime/agent/orchestrator/{internal-roles.mjs → internal-agents.mjs} +72 -72
  63. package/src/runtime/agent/orchestrator/internal-tools.mjs +13 -26
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +100 -18
  65. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -0
  66. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +188 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +444 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +313 -84
  70. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +104 -38
  71. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +28 -33
  72. package/src/runtime/agent/orchestrator/providers/gemini.mjs +184 -17
  73. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +8 -1
  74. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  75. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +18 -8
  76. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +227 -31
  78. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +83 -6
  79. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +229 -115
  80. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +213 -33
  81. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +18 -0
  82. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +49 -17
  83. package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -1
  84. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +22 -17
  85. package/src/runtime/agent/orchestrator/session/compact.mjs +560 -51
  86. package/src/runtime/agent/orchestrator/session/context-utils.mjs +250 -3
  87. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  88. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +262 -0
  89. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  90. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  91. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  92. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +49 -0
  93. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  94. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  95. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  96. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  97. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  98. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  99. package/src/runtime/agent/orchestrator/session/loop.mjs +513 -1000
  100. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +227 -0
  101. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  102. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +137 -0
  103. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  104. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  105. package/src/runtime/agent/orchestrator/session/manager.mjs +232 -1152
  106. package/src/runtime/agent/orchestrator/session/store.mjs +4 -4
  107. package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +61 -0
  108. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +5 -0
  109. package/src/runtime/agent/orchestrator/stall-policy.mjs +63 -15
  110. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +194 -24
  111. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +143 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +34 -18
  113. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +162 -0
  115. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  116. package/src/runtime/agent/orchestrator/tools/builtin/list-formatting.mjs +10 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +5 -4
  118. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  119. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +15 -0
  120. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +9 -44
  121. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -1
  122. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +14 -5
  123. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  124. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +10 -17
  125. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +23 -2
  126. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +50 -39
  127. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +3 -2
  128. package/src/runtime/agent/orchestrator/tools/builtin/tool-output-limit.mjs +10 -0
  129. package/src/runtime/agent/orchestrator/tools/builtin.mjs +70 -1
  130. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  131. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  132. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  133. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +497 -0
  134. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  135. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  136. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  137. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  138. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  139. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1192 -0
  140. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  141. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  142. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  143. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  144. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +5 -5
  145. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +39 -4189
  146. package/src/runtime/agent/orchestrator/tools/patch.mjs +119 -5
  147. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -2
  148. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +1 -2
  149. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  150. package/src/runtime/channels/backends/discord.mjs +99 -9
  151. package/src/runtime/channels/backends/telegram.mjs +501 -0
  152. package/src/runtime/channels/index.mjs +437 -1439
  153. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  154. package/src/runtime/channels/lib/config.mjs +54 -2
  155. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  156. package/src/runtime/channels/lib/format.mjs +4 -2
  157. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  158. package/src/runtime/channels/lib/output-forwarder.mjs +88 -67
  159. package/src/runtime/channels/lib/runtime-paths.mjs +29 -0
  160. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  161. package/src/runtime/channels/lib/telegram-format.mjs +280 -0
  162. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  163. package/src/runtime/channels/lib/transcript-discovery.mjs +19 -1
  164. package/src/runtime/channels/lib/webhook.mjs +59 -31
  165. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  166. package/src/runtime/channels/tool-defs.mjs +1 -1
  167. package/src/runtime/memory/index.mjs +465 -345
  168. package/src/runtime/memory/lib/agent-ipc.mjs +2 -2
  169. package/src/runtime/memory/lib/core-memory-store.mjs +352 -2
  170. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  171. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  172. package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
  173. package/src/runtime/memory/lib/memory-cycle2.mjs +65 -9
  174. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
  175. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  176. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  177. package/src/runtime/memory/lib/memory.mjs +121 -4
  178. package/src/runtime/memory/lib/pg/adapter.mjs +139 -15
  179. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  180. package/src/runtime/memory/lib/recall-format.mjs +183 -0
  181. package/src/runtime/memory/lib/session-ingest.mjs +107 -0
  182. package/src/runtime/memory/lib/trace-store.mjs +69 -22
  183. package/src/runtime/memory/tool-defs.mjs +10 -7
  184. package/src/runtime/shared/abort-controller.mjs +1 -1
  185. package/src/runtime/shared/background-tasks.mjs +2 -3
  186. package/src/runtime/shared/buffered-appender.mjs +149 -0
  187. package/src/runtime/shared/channel-notification-routing.mjs +12 -0
  188. package/src/runtime/shared/channel-notification-routing.test.mjs +45 -0
  189. package/src/runtime/shared/config.mjs +9 -0
  190. package/src/runtime/shared/llm/http-agent.mjs +12 -5
  191. package/src/runtime/shared/schedules-store.mjs +21 -19
  192. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  193. package/src/runtime/shared/task-notification-envelope.test.mjs +107 -0
  194. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  195. package/src/runtime/shared/tool-surface.mjs +98 -13
  196. package/src/runtime/shared/transcript-writer.mjs +156 -0
  197. package/src/runtime/shared/update-checker.mjs +214 -0
  198. package/src/session-runtime/config-helpers.mjs +209 -0
  199. package/src/session-runtime/effort.mjs +128 -0
  200. package/src/session-runtime/fs-utils.mjs +10 -0
  201. package/src/session-runtime/model-capabilities.mjs +130 -0
  202. package/src/session-runtime/output-styles.mjs +124 -0
  203. package/src/session-runtime/plugin-mcp.mjs +114 -0
  204. package/src/session-runtime/session-text.mjs +100 -0
  205. package/src/session-runtime/statusline-route.mjs +35 -0
  206. package/src/session-runtime/tool-catalog.mjs +720 -0
  207. package/src/session-runtime/workflow.mjs +358 -0
  208. package/src/standalone/agent-tool.mjs +302 -117
  209. package/src/standalone/channel-admin.mjs +133 -40
  210. package/src/standalone/channel-worker.mjs +10 -292
  211. package/src/standalone/explore-tool.mjs +12 -5
  212. package/src/standalone/hook-bus.mjs +165 -8
  213. package/src/standalone/memory-runtime-proxy.mjs +3 -1
  214. package/src/standalone/opencode-go-login.mjs +121 -0
  215. package/src/standalone/provider-admin.mjs +25 -3
  216. package/src/standalone/usage-dashboard.mjs +1 -1
  217. package/src/tui/App.jsx +2883 -778
  218. package/src/tui/components/ConfirmBar.jsx +47 -0
  219. package/src/tui/components/ContextPanel.jsx +5 -3
  220. package/src/tui/components/ItemRightHintOverprint.jsx +54 -0
  221. package/src/tui/components/Markdown.jsx +22 -98
  222. package/src/tui/components/Message.jsx +14 -35
  223. package/src/tui/components/Picker.jsx +87 -12
  224. package/src/tui/components/PromptInput.jsx +355 -22
  225. package/src/tui/components/QueuedCommands.jsx +1 -1
  226. package/src/tui/components/SlashCommandPalette.jsx +8 -5
  227. package/src/tui/components/Spinner.jsx +7 -7
  228. package/src/tui/components/StatusLine.jsx +40 -21
  229. package/src/tui/components/TextEntryPanel.jsx +51 -7
  230. package/src/tui/components/ToolExecution.jsx +183 -101
  231. package/src/tui/components/TurnDone.jsx +4 -4
  232. package/src/tui/components/UsagePanel.jsx +1 -1
  233. package/src/tui/components/tool-output-format.mjs +161 -23
  234. package/src/tui/components/tool-output-format.test.mjs +87 -0
  235. package/src/tui/display-width.mjs +69 -0
  236. package/src/tui/display-width.test.mjs +35 -0
  237. package/src/tui/dist/index.mjs +6731 -2333
  238. package/src/tui/engine/agent-envelope.mjs +296 -0
  239. package/src/tui/engine/boot-profile.mjs +21 -0
  240. package/src/tui/engine/labels.mjs +67 -0
  241. package/src/tui/engine/notice-text.mjs +112 -0
  242. package/src/tui/engine/queue-helpers.mjs +161 -0
  243. package/src/tui/engine/session-stats.mjs +46 -0
  244. package/src/tui/engine/tool-call-fields.mjs +23 -0
  245. package/src/tui/engine/tool-result-text.mjs +126 -0
  246. package/src/tui/engine.mjs +569 -948
  247. package/src/tui/index.jsx +117 -7
  248. package/src/tui/input-editing.mjs +58 -8
  249. package/src/tui/input-editing.selection.test.mjs +75 -0
  250. package/src/tui/keyboard-protocol.mjs +42 -0
  251. package/src/tui/lib/voice-recorder.mjs +469 -0
  252. package/src/tui/markdown/format-token.mjs +128 -76
  253. package/src/tui/markdown/format-token.test.mjs +61 -19
  254. package/src/tui/markdown/measure-rendered-rows.mjs +85 -0
  255. package/src/tui/markdown/render-ansi.test.mjs +1 -1
  256. package/src/tui/markdown/streaming-markdown.mjs +167 -0
  257. package/src/tui/markdown/streaming-markdown.test.mjs +70 -0
  258. package/src/tui/markdown/table-layout.mjs +9 -9
  259. package/src/tui/paste-attachments.mjs +36 -9
  260. package/src/tui/paste-fix.test.mjs +119 -0
  261. package/src/tui/prompt-history-store.mjs +129 -0
  262. package/src/tui/prompt-history-store.test.mjs +52 -0
  263. package/src/tui/statusline-ansi-bridge.test.mjs +3 -3
  264. package/src/tui/theme.mjs +41 -657
  265. package/src/tui/themes/base.mjs +86 -0
  266. package/src/tui/themes/basic.mjs +85 -0
  267. package/src/tui/themes/catppuccin.mjs +72 -0
  268. package/src/tui/themes/dracula.mjs +70 -0
  269. package/src/tui/themes/everforest.mjs +71 -0
  270. package/src/tui/themes/gruvbox.mjs +71 -0
  271. package/src/tui/themes/index.mjs +71 -0
  272. package/src/tui/themes/indigo.mjs +78 -0
  273. package/src/tui/themes/kanagawa.mjs +80 -0
  274. package/src/tui/themes/light.mjs +81 -0
  275. package/src/tui/themes/nord.mjs +72 -0
  276. package/src/tui/themes/onedark.mjs +16 -0
  277. package/src/tui/themes/rosepine.mjs +70 -0
  278. package/src/tui/themes/teal.mjs +80 -0
  279. package/src/tui/themes/tokyonight.mjs +79 -0
  280. package/src/tui/themes/utils.mjs +106 -0
  281. package/src/tui/themes/warm.mjs +79 -0
  282. package/src/tui/transcript-tool-failures.mjs +13 -2
  283. package/src/ui/markdown.mjs +1 -1
  284. package/src/ui/model-display.mjs +2 -2
  285. package/src/ui/statusline.mjs +75 -27
  286. package/src/vendor/statusline/bin/statusline-route.mjs +5 -12
  287. package/src/vendor/statusline/src/gateway/claude-current.mjs +3 -3
  288. package/src/vendor/statusline/src/gateway/route-meta.mjs +30 -16
  289. package/src/workflows/default/WORKFLOW.md +46 -12
  290. package/src/workflows/sequential/WORKFLOW.md +51 -0
  291. package/src/workflows/solo/WORKFLOW.md +12 -1
  292. package/vendor/ink/build/display-width.js +62 -0
  293. package/vendor/ink/build/ink.js +100 -12
  294. package/vendor/ink/build/measure-text.js +4 -1
  295. package/vendor/ink/build/output.js +115 -9
  296. package/vendor/ink/build/render-node-to-output.js +4 -1
  297. package/vendor/ink/build/render.js +4 -0
  298. package/src/output-styles/extreme-simple.md +0 -20
  299. package/src/rules/lead/04-workflow.md +0 -51
  300. package/src/workflows/default/workflow.json +0 -13
  301. package/src/workflows/solo/workflow.json +0 -7
@@ -1,50 +1,50 @@
1
1
  /**
2
- * Internal hidden roles — Mixdog-managed, user-untouchable.
2
+ * Internal hidden agents — Mixdog-managed, user-untouchable.
3
3
  *
4
- * Unlike public workflow agents, these hidden roles are NEVER exposed to callers of the `agent` tool. They are
4
+ * Unlike public workflow agents, these hidden agents are NEVER exposed to callers of the `agent` tool. They are
5
5
  * invoked only by internal handlers (explore / recall / search) and carry
6
6
  * their own system prompt + tool-set policy.
7
7
  *
8
8
  * Lookup order (agent-dispatch.resolvePresetName):
9
9
  * 1. explicit preset arg
10
10
  * 2. opts.preset
11
- * 3. hidden-role registry (defaults/hidden-roles.json + systemFile frontmatter)
11
+ * 3. hidden-agent registry (defaults/agents.json + systemFile frontmatter)
12
12
  *
13
- * Role definitions live in defaults/hidden-roles.json and the referenced
13
+ * Agent definitions live in defaults/agents.json and the referenced
14
14
  * systemFile markdown frontmatter. Editing those files is a Mixdog source change.
15
15
  *
16
16
  * The preset names refer to entries seeded in mixdog-config.json (agent.presets)
17
17
  * via DEFAULT_PRESETS (see config.mjs). If the user deletes the referenced preset
18
- * from their config the hidden roles degrade gracefully — `resolvePresetName`
18
+ * from their config the hidden agents degrade gracefully — `resolvePresetName`
19
19
  * returns a name, but session creation will fail with a clear "preset not
20
20
  * found" error rather than silently mis-dispatching.
21
21
  *
22
22
  * Kind classification:
23
- * - 'retrieval' : short-lived hidden retrieval roles (explore).
24
- * - 'maintenance' : background-trigger hidden roles (memory cycle, scheduler,
23
+ * - 'retrieval' : short-lived hidden retrieval agents (explore).
24
+ * - 'maintenance' : background-trigger hidden agents (memory cycle, scheduler,
25
25
  * webhook). Receive only their own self section in BP2.
26
26
  *
27
27
  * Permission classification:
28
28
  * - 'read' : read-only — apply_patch/shell blocked at loop.mjs runtime
29
- * guard with the same error string public read-only roles see.
30
- * - 'read-write' : full tool surface — used by hidden roles that legitimately
29
+ * guard with the same error string public read-only agents see.
30
+ * - 'read-write' : full tool surface — used by hidden agents that legitimately
31
31
  * mutate state (scheduler-task launches commands,
32
32
  * webhook-handler persists payloads). agent-dispatch honors this
33
- * declaratively instead of forcing every hidden role to 'read'.
33
+ * declaratively instead of forcing every hidden agent to 'read'.
34
34
  *
35
35
  * Tool schema profile:
36
- * - 'none' : no tools exposed; pure transform/classifier roles.
36
+ * - 'none' : no tools exposed; pure transform/classifier agents.
37
37
  * - 'read' : read/search/code-navigation tools only.
38
38
  * - 'full' : shared agent tool schema for provider cache reuse.
39
39
  *
40
- * Role-specific instruction metadata (consumed by rules-builder.cjs
40
+ * Agent-specific instruction metadata (consumed by rules-builder.cjs
41
41
  * buildAgentRoleSpecificContent + collect.mjs):
42
- * - inboundEvent : role reports results back to Lead and must carry the
42
+ * - inboundEvent : agent reports results back to Lead and must carry the
43
43
  * skip-protocol rule (rules/agent/20-skip-protocol.md)
44
- * in its BP2 role-rule block so no-op outputs opt out of the Lead
44
+ * in its BP2 agent-rule block so no-op outputs opt out of the Lead
45
45
  * inject.
46
46
  * - instructionDir : DATA_DIR subdir whose *.md tree is folded into the
47
- * role's BP4-adjacent user/task data (webhook-handler →
47
+ * agent's BP4-adjacent user/task data (webhook-handler →
48
48
  * webhooks, scheduler-task → schedules).
49
49
  */
50
50
 
@@ -56,24 +56,24 @@ import {
56
56
  parseMarkdownFrontmatter,
57
57
  } from '../../shared/markdown-frontmatter.mjs'
58
58
 
59
- // Resolve the path to defaults/hidden-roles.json once.
59
+ // Resolve the path to defaults/agents.json once.
60
60
  const _MIXDOG_ROOT = mixdogRoot()
61
- const _HIDDEN_ROLES_PATH = (() => {
62
- return join(_MIXDOG_ROOT, 'defaults', 'hidden-roles.json')
61
+ const _AGENTS_PATH = (() => {
62
+ return join(_MIXDOG_ROOT, 'defaults', 'agents.json')
63
63
  })()
64
64
 
65
65
  /** @type {{ mtime: number, map: object } | null} */
66
- let _hiddenRolesCache = null
66
+ let _hiddenAgentsCache = null
67
67
 
68
68
  function _mtimeSafe(file) {
69
69
  try { return statSync(file).mtimeMs } catch { return 0 }
70
70
  }
71
71
 
72
- function _hiddenRolesDependencyMtime() {
73
- let mtime = _mtimeSafe(_HIDDEN_ROLES_PATH)
72
+ function _hiddenAgentsDependencyMtime() {
73
+ let mtime = _mtimeSafe(_AGENTS_PATH)
74
74
  try {
75
- const raw = JSON.parse(readFileSync(_HIDDEN_ROLES_PATH, 'utf8'))
76
- for (const entry of (raw.roles || [])) {
75
+ const raw = JSON.parse(readFileSync(_AGENTS_PATH, 'utf8'))
76
+ for (const entry of (raw.agents || [])) {
77
77
  const systemFile = typeof entry?.systemFile === 'string' ? entry.systemFile.trim() : ''
78
78
  if (!systemFile) continue
79
79
  mtime = Math.max(mtime, _mtimeSafe(join(_MIXDOG_ROOT, systemFile)))
@@ -82,7 +82,7 @@ function _hiddenRolesDependencyMtime() {
82
82
  return mtime
83
83
  }
84
84
 
85
- function _loadHiddenRoleFrontmatter(systemFile) {
85
+ function _loadHiddenAgentFrontmatter(systemFile) {
86
86
  const rel = typeof systemFile === 'string' ? systemFile.trim() : ''
87
87
  if (!rel) return {}
88
88
  try {
@@ -92,8 +92,8 @@ function _loadHiddenRoleFrontmatter(systemFile) {
92
92
  }
93
93
  }
94
94
 
95
- function _mergeHiddenRoleFrontmatter(entry) {
96
- const fm = _loadHiddenRoleFrontmatter(entry.systemFile)
95
+ function _mergeHiddenAgentFrontmatter(entry) {
96
+ const fm = _loadHiddenAgentFrontmatter(entry.systemFile)
97
97
  const merged = { ...entry }
98
98
  const permission = normalizeAgentPermissionOrNone(fm.permission)
99
99
  if (permission) merged.permission = permission
@@ -111,62 +111,62 @@ function _mergeHiddenRoleFrontmatter(entry) {
111
111
  }
112
112
 
113
113
  /**
114
- * Read and parse defaults/hidden-roles.json. Throws on missing/malformed.
114
+ * Read and parse defaults/agents.json. Throws on missing/malformed.
115
115
  */
116
- function _loadHiddenRoles() {
116
+ function _loadHiddenAgents() {
117
117
  try {
118
- const raw = JSON.parse(readFileSync(_HIDDEN_ROLES_PATH, 'utf8'))
118
+ const raw = JSON.parse(readFileSync(_AGENTS_PATH, 'utf8'))
119
119
  const map = Object.create(null)
120
- for (const entry of (raw.roles || [])) {
121
- if (entry && entry.name) map[entry.name] = Object.freeze(_mergeHiddenRoleFrontmatter(entry))
120
+ for (const entry of (raw.agents || [])) {
121
+ if (entry && entry.agent) map[entry.agent] = Object.freeze(_mergeHiddenAgentFrontmatter(entry))
122
122
  }
123
123
  return Object.freeze(map)
124
124
  } catch (err) {
125
- // Fail loudly — a missing or malformed hidden-roles.json breaks dispatch.
126
- throw new Error(`[internal-roles] failed to load defaults/hidden-roles.json: ${err.message}`)
125
+ // Fail loudly — a missing or malformed agents.json breaks dispatch.
126
+ throw new Error(`[internal-agents] failed to load defaults/agents.json: ${err.message}`)
127
127
  }
128
128
  }
129
129
 
130
130
  /**
131
- * Return the hidden-role map, re-reading from disk when the file mtime has
131
+ * Return the hidden-agent map, re-reading from disk when the file mtime has
132
132
  * changed since the last load. This ensures that BP2 cache rebuilds triggered
133
- * by a hidden-roles.json modification (tracked via maxMtimeRecursive in
133
+ * by an agents.json modification (tracked via maxMtimeRecursive in
134
134
  * collect.mjs) consume fresh metadata in the same process.
135
135
  */
136
- function _getHiddenRoles() {
136
+ function _getHiddenAgents() {
137
137
  try {
138
- const mtime = _hiddenRolesDependencyMtime()
139
- if (_hiddenRolesCache && mtime <= _hiddenRolesCache.mtime) {
140
- return _hiddenRolesCache.map
138
+ const mtime = _hiddenAgentsDependencyMtime()
139
+ if (_hiddenAgentsCache && mtime <= _hiddenAgentsCache.mtime) {
140
+ return _hiddenAgentsCache.map
141
141
  }
142
- const map = _loadHiddenRoles()
143
- _hiddenRolesCache = { mtime, map }
142
+ const map = _loadHiddenAgents()
143
+ _hiddenAgentsCache = { mtime, map }
144
144
  return map
145
145
  } catch (err) {
146
146
  // Fail loudly — re-throw with a clear message. A cache hit is never used
147
147
  // when statSync fails because the caller expects current data.
148
- throw new Error(`[internal-roles] failed to load defaults/hidden-roles.json: ${err.message}`)
148
+ throw new Error(`[internal-agents] failed to load defaults/agents.json: ${err.message}`)
149
149
  }
150
150
  }
151
151
 
152
152
  // Eager validate at module init so startup failures are immediate.
153
- _getHiddenRoles()
153
+ _getHiddenAgents()
154
154
 
155
155
  /**
156
- * Return the hidden-role definition, or null if the name is not internal.
156
+ * Return the hidden-agent definition, or null if the name is not internal.
157
157
  */
158
- export function getHiddenRole(name) {
158
+ export function getHiddenAgent(name) {
159
159
  if (!name) return null
160
- return _getHiddenRoles()[name] || null
160
+ return _getHiddenAgents()[name] || null
161
161
  }
162
162
 
163
163
  /**
164
- * Resolve permission stamped on a agent session. Hidden roles declared
164
+ * Resolve permission stamped on a agent session. Hidden agents declared
165
165
  * `permission: 'read'` are read-locked — caller opts cannot upgrade them.
166
166
  * Other built-in permissions include `none`, `read-write`, `mcp`, and `full`.
167
167
  */
168
- export function resolveAgentSessionPermission(role, callerPermission) {
169
- const hidden = getHiddenRole(role)
168
+ export function resolveAgentSessionPermission(agent, callerPermission) {
169
+ const hidden = getHiddenAgent(agent)
170
170
  if (hidden && hidden.permission === 'read') return 'read'
171
171
  if (callerPermission != null && callerPermission !== '') return callerPermission
172
172
  if (hidden?.permission) return hidden.permission
@@ -176,61 +176,61 @@ export function resolveAgentSessionPermission(role, callerPermission) {
176
176
  /**
177
177
  * Boolean check — useful for branching inside agent-dispatch / session-manager.
178
178
  */
179
- export function isHiddenRole(name) {
179
+ export function isHiddenAgent(name) {
180
180
  if (!name) return false
181
- return Object.prototype.hasOwnProperty.call(_getHiddenRoles(), name)
181
+ return Object.prototype.hasOwnProperty.call(_getHiddenAgents(), name)
182
182
  }
183
183
 
184
184
  /**
185
- * List all hidden role names. Used by diagnostics / setup UI guards to ensure
186
- * a user-defined role doesn't collide with an internal one.
185
+ * List all hidden agent names. Used by diagnostics / setup UI guards to ensure
186
+ * a user-defined agent doesn't collide with an internal one.
187
187
  */
188
- export function listHiddenRoleNames() {
189
- return Object.keys(_getHiddenRoles())
188
+ export function listHiddenAgentNames() {
189
+ return Object.keys(_getHiddenAgents())
190
190
  }
191
191
 
192
192
  /**
193
- * List hidden role names matching a given kind ('retrieval' | 'maintenance').
194
- * Consumed by collect.mjs to drive BP2 role-shard classification dynamically
195
- * instead of hard-coding role-name sets.
193
+ * List hidden agent names matching a given kind ('retrieval' | 'maintenance').
194
+ * Consumed by collect.mjs to drive BP2 agent-shard classification dynamically
195
+ * instead of hard-coding agent-name sets.
196
196
  */
197
- export function listHiddenRolesByKind(kind) {
197
+ export function listHiddenAgentsByKind(kind) {
198
198
  const out = []
199
- for (const [name, def] of Object.entries(_getHiddenRoles())) {
199
+ for (const [name, def] of Object.entries(_getHiddenAgents())) {
200
200
  if (def.kind === kind) out.push(name)
201
201
  }
202
202
  return out
203
203
  }
204
204
 
205
205
  /**
206
- * Return the agents/<name>.md sections a hidden role shares in its BP2 catalog
206
+ * Return the agents/<name>.md sections a hidden agent shares in its BP2 catalog
207
207
  * (in addition to its own self section). Drives the explorer→worker cache
208
- * alignment declaratively instead of a hard-coded role-name branch in
209
- * collect.mjs. Returns [] when the role declares none.
208
+ * alignment declaratively instead of a hard-coded agent-name branch in
209
+ * collect.mjs. Returns [] when the agent declares none.
210
210
  */
211
- export function getRoleCatalogShareAgents(name) {
212
- const hidden = getHiddenRole(name)
211
+ export function getAgentCatalogShareAgents(name) {
212
+ const hidden = getHiddenAgent(name)
213
213
  if (!hidden || !Array.isArray(hidden.catalogShareAgents)) return []
214
214
  return hidden.catalogShareAgents.map((n) => String(n || '').trim()).filter(Boolean)
215
215
  }
216
216
 
217
217
  /**
218
- * True when a hidden role reports results back to Lead and must carry the
219
- * skip-protocol rule in its BP2 role-rule block. Replaces the hard-coded
218
+ * True when a hidden agent reports results back to Lead and must carry the
219
+ * skip-protocol rule in its BP2 agent-rule block. Replaces the hard-coded
220
220
  * INBOUND_EVENT_ROLES set in collect.mjs.
221
221
  */
222
- export function isInboundEventRole(name) {
223
- const hidden = getHiddenRole(name)
222
+ export function isInboundEventAgent(name) {
223
+ const hidden = getHiddenAgent(name)
224
224
  return !!(hidden && hidden.inboundEvent === true)
225
225
  }
226
226
 
227
227
  /**
228
228
  * Return the DATA_DIR subdir whose *.md tree rides as BP4-adjacent
229
- * user/task data, or null when the role declares none. Replaces the
229
+ * user/task data, or null when the agent declares none. Replaces the
230
230
  * webhook/scheduler ternary in rules-builder.cjs buildAgentRoleSpecificContent.
231
231
  */
232
- export function getRoleInstructionDir(name) {
233
- const hidden = getHiddenRole(name)
232
+ export function getAgentInstructionDir(name) {
233
+ const hidden = getHiddenAgent(name)
234
234
  const dir = hidden && typeof hidden.instructionDir === 'string' ? hidden.instructionDir.trim() : ''
235
235
  return dir || null
236
236
  }
@@ -15,6 +15,8 @@
15
15
  * no-tool role guard) — not a permission check. No gating is needed here.
16
16
  */
17
17
 
18
+ import { isToolEnvelope, makeToolEnvelope } from './session/tool-envelope.mjs';
19
+
18
20
  let _executor = null;
19
21
  let _tools = [];
20
22
  let _names = new Set();
@@ -24,35 +26,14 @@ let _bootResolver = null;
24
26
  const _bootPromise = new Promise((r) => { _bootResolver = r; });
25
27
  export function markBootReady() { if (_bootReady) return; _bootReady = true; _bootResolver(); }
26
28
 
27
- // Per-tool executor overrides. Populated by addInternalTools() for extra
28
- // internal tools that bypass the main dispatch (tools.json + dispatchTool).
29
- const _overrides = new Map();
30
-
31
29
  export function setInternalToolsProvider({ executor, tools }) {
32
30
  if (typeof executor !== 'function') throw new Error('internal-tools: executor must be a function');
33
31
  _executor = executor;
34
32
  const base = Array.isArray(tools) ? [...tools] : [];
35
- // Re-registration (handleToolCall idempotent fallback) must preserve any
36
- // override-backed tools previously registered via addInternalTools.
37
- if (_overrides.size > 0) {
38
- const baseNames = new Set(base.map(t => t?.name).filter(Boolean));
39
- for (const [name] of _overrides) {
40
- if (baseNames.has(name)) continue;
41
- const existing = _tools.find(t => t?.name === name);
42
- if (existing) base.push(existing);
43
- }
44
- }
45
33
  _tools = base;
46
34
  _names = new Set(_tools.map(t => t?.name).filter(Boolean));
47
35
  }
48
36
 
49
- /**
50
- * Register additional tools that aren't declared in tools.json — each comes
51
- * with its own executor.
52
- *
53
- * Re-registration is idempotent; later calls overwrite earlier entries with
54
- * the same name.
55
- */
56
37
  export function getInternalTools() {
57
38
  return _tools;
58
39
  }
@@ -63,11 +44,6 @@ export function isInternalTool(name) {
63
44
 
64
45
  export async function executeInternalTool(name, args, callerCtx = {}) {
65
46
  if (!_names.has(name)) throw new Error(`internal-tools: "${name}" is not registered`);
66
- const override = _overrides.get(name);
67
- if (override) {
68
- const result = await override(args ?? {}, callerCtx);
69
- return _normalize(result);
70
- }
71
47
  if (!_executor) throw new Error(`internal-tools: executor not initialized (tool=${name})`);
72
48
  const result = await _executor(name, args ?? {}, callerCtx);
73
49
  return _normalize(result);
@@ -77,6 +53,17 @@ export async function executeInternalTool(name, args, callerCtx = {}) {
77
53
  // plain string either way. Worker/module handlers return the MCP-shaped
78
54
  // `{ content: [{type:'text', text}] }` envelope directly.
79
55
  function _normalize(result) {
56
+ // General newMessages tool-result channel: a tool may return a
57
+ // `{ __toolEnvelope, result, newMessages }` envelope (e.g. Skill, whose
58
+ // body rides ONE injected user message). Preserve the envelope shape, only
59
+ // normalizing its inner `result` to a string, so the agent loop's central
60
+ // normalizeToolEnvelope still splits it into stub + injected user body.
61
+ // Without this guard the envelope object would fall through to
62
+ // JSON.stringify below and the loop would see the stringified envelope as
63
+ // the tool_result (body inlined + duplicated, no newMessages).
64
+ if (isToolEnvelope(result)) {
65
+ return makeToolEnvelope(_normalize(result.result), result.newMessages);
66
+ }
80
67
  if (result && typeof result === 'object' && Array.isArray(result.content)) {
81
68
  return result.content
82
69
  .map((c) => (c?.type === 'text' ? c.text || '' : JSON.stringify(c)))
@@ -1,4 +1,5 @@
1
- import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
1
+ import { readFileSync, existsSync, mkdirSync } from 'fs';
2
+ import { writeFile } from 'fs/promises';
2
3
  import { join } from 'path';
3
4
  import { tmpdir } from 'os';
4
5
  import { randomUUID } from 'crypto';
@@ -37,13 +38,58 @@ async function loadMcpSdk() {
37
38
  import('@modelcontextprotocol/sdk/client/index.js'),
38
39
  import('@modelcontextprotocol/sdk/client/stdio.js'),
39
40
  import('@modelcontextprotocol/sdk/client/streamableHttp.js'),
40
- ]).then(([clientMod, stdioMod, httpMod]) => ({
41
+ import('@modelcontextprotocol/sdk/client/sse.js'),
42
+ import('@modelcontextprotocol/sdk/client/websocket.js'),
43
+ ]).then(([clientMod, stdioMod, httpMod, sseMod, wsMod]) => ({
41
44
  Client: clientMod.Client,
42
45
  StdioClientTransport: stdioMod.StdioClientTransport,
43
46
  StreamableHTTPClientTransport: httpMod.StreamableHTTPClientTransport,
47
+ SSEClientTransport: sseMod.SSEClientTransport,
48
+ WebSocketClientTransport: wsMod.WebSocketClientTransport,
44
49
  }));
45
50
  return mcpSdkPromise;
46
51
  }
52
+ /**
53
+ * Expand `${VAR}` and `${env:VAR}` references in string values using the
54
+ * provided env map (defaults to process.env). Recurses into arrays/objects.
55
+ * Unknown vars expand to an empty string. No shell execution.
56
+ */
57
+ export function expandEnvVars(value, env = process.env) {
58
+ if (typeof value === 'string') {
59
+ return value.replace(/\$\{(?:env:)?([A-Za-z_][A-Za-z0-9_]*)\}/g, (_m, name) => {
60
+ const v = env?.[name];
61
+ return v == null ? '' : String(v);
62
+ });
63
+ }
64
+ if (Array.isArray(value)) {
65
+ return value.map((v) => expandEnvVars(v, env));
66
+ }
67
+ if (value && typeof value === 'object') {
68
+ const out = {};
69
+ for (const [k, v] of Object.entries(value)) {
70
+ out[k] = expandEnvVars(v, env);
71
+ }
72
+ return out;
73
+ }
74
+ return value;
75
+ }
76
+ /**
77
+ * Resolve the canonical transport kind for an MCP server config entry.
78
+ * Returns one of: 'autoDetect' | 'stdio' | 'http' | 'sse' | 'ws'.
79
+ * Throws when no transport can be determined.
80
+ */
81
+ export function resolveMcpTransportKind(cfg) {
82
+ if (cfg?.autoDetect) return 'autoDetect';
83
+ if (cfg?.type != null && cfg.type !== '') {
84
+ let t = String(cfg.type).toLowerCase();
85
+ if (t === 'streamable-http' || t === 'streamablehttp') t = 'http';
86
+ if (t === 'stdio' || t === 'http' || t === 'sse' || t === 'ws') return t;
87
+ }
88
+ if (cfg?.transport === 'http') return 'http';
89
+ if (cfg?.command) return 'stdio';
90
+ if (cfg?.url) return 'http';
91
+ throw new Error(`Invalid config: need autoDetect, type (stdio/http/sse/ws), url (http), or command (stdio)`);
92
+ }
47
93
  // --- Public API ---
48
94
  /**
49
95
  * Connect to MCP servers defined in config.
@@ -92,11 +138,13 @@ export function getMcpServerStatus() {
92
138
  name: tool.name,
93
139
  description: tool.description || '',
94
140
  })),
95
- transport: server.cfg?.autoDetect
96
- ? 'autoDetect'
97
- : server.cfg?.transport === 'http' || server.cfg?.url
98
- ? 'http'
99
- : 'stdio',
141
+ transport: (() => {
142
+ try {
143
+ return resolveMcpTransportKind(server.cfg);
144
+ } catch {
145
+ return 'stdio';
146
+ }
147
+ })(),
100
148
  }));
101
149
  }
102
150
  /**
@@ -226,7 +274,10 @@ function capMcpOutput(content) {
226
274
  const dir = join(tmpdir(), 'mixdog-mcp-output');
227
275
  mkdirSync(dir, { recursive: true });
228
276
  spillPath = join(dir, `mcp-${Date.now()}-${randomUUID().slice(0, 8)}.txt`);
229
- writeFileSync(spillPath, s, 'utf-8');
277
+ // Fire-and-forget: the spill path is returned to the caller
278
+ // immediately (below) for later recovery; the write itself must not
279
+ // block this hot tool-result path.
280
+ writeFile(spillPath, s, 'utf-8').catch(() => { /* spill best-effort */ });
230
281
  } catch { /* spill best-effort */ }
231
282
  const spillNote = spillPath
232
283
  ? `\n\n... [full output spilled to ${spillPath}] ...`
@@ -275,11 +326,18 @@ export async function disconnectAll() {
275
326
  _invalidateMcpToolFieldMemo();
276
327
  }
277
328
  async function connectServer(name, cfg) {
278
- const { Client, StdioClientTransport, StreamableHTTPClientTransport } = await loadMcpSdk();
329
+ const {
330
+ Client,
331
+ StdioClientTransport,
332
+ StreamableHTTPClientTransport,
333
+ SSEClientTransport,
334
+ WebSocketClientTransport,
335
+ } = await loadMcpSdk();
279
336
  const client = new Client({ name: `mixdog-agent/${name}`, version: '1.0.0' });
280
337
  let transport;
338
+ const kind = resolveMcpTransportKind(cfg);
281
339
  // Auto-detect: read port from a running service's port file
282
- if (cfg.autoDetect) {
340
+ if (kind === 'autoDetect') {
283
341
  const spec = AUTO_DETECT_PORTS[cfg.autoDetect];
284
342
  if (!spec)
285
343
  throw new Error(`Unknown autoDetect target: "${cfg.autoDetect}"`);
@@ -314,16 +372,40 @@ async function connectServer(name, cfg) {
314
372
  transport = new StreamableHTTPClientTransport(new URL(url));
315
373
  mcpLog(`[mcp-client] Connecting "${name}" via autoDetect HTTP: ${url}\n`);
316
374
  }
317
- else if (cfg.transport === 'http' && cfg.url) {
318
- transport = new StreamableHTTPClientTransport(new URL(cfg.url));
319
- mcpLog(`[mcp-client] Connecting "${name}" via HTTP: ${cfg.url}\n`);
375
+ else if (kind === 'http') {
376
+ const url = expandEnvVars(String(cfg.url ?? ''));
377
+ const headers = expandEnvVars(cfg.headers && typeof cfg.headers === 'object' ? cfg.headers : {});
378
+ const opts = (headers && Object.keys(headers).length > 0)
379
+ ? { requestInit: { headers } }
380
+ : undefined;
381
+ transport = opts
382
+ ? new StreamableHTTPClientTransport(new URL(url), opts)
383
+ : new StreamableHTTPClientTransport(new URL(url));
384
+ mcpLog(`[mcp-client] Connecting "${name}" via HTTP: ${url}\n`);
385
+ }
386
+ else if (kind === 'sse') {
387
+ const url = expandEnvVars(String(cfg.url ?? ''));
388
+ const headers = expandEnvVars(cfg.headers && typeof cfg.headers === 'object' ? cfg.headers : {});
389
+ const opts = (headers && Object.keys(headers).length > 0)
390
+ ? { requestInit: { headers } }
391
+ : undefined;
392
+ transport = opts
393
+ ? new SSEClientTransport(new URL(url), opts)
394
+ : new SSEClientTransport(new URL(url));
395
+ mcpLog(`[mcp-client] Connecting "${name}" via SSE: ${url}\n`);
396
+ }
397
+ else if (kind === 'ws') {
398
+ // WebSocketClientTransport ctor takes only a URL; headers are ignored.
399
+ const url = expandEnvVars(String(cfg.url ?? ''));
400
+ transport = new WebSocketClientTransport(new URL(url));
401
+ mcpLog(`[mcp-client] Connecting "${name}" via WebSocket: ${url}\n`);
320
402
  }
321
- else if (cfg.command) {
403
+ else if (kind === 'stdio') {
322
404
  transport = new StdioClientTransport({
323
- command: cfg.command,
324
- args: cfg.args,
405
+ command: expandEnvVars(String(cfg.command ?? '')),
406
+ args: Array.isArray(cfg.args) ? expandEnvVars(cfg.args) : cfg.args,
325
407
  cwd: cfg.cwd,
326
- env: { ...process.env, ...cfg.env },
408
+ env: { ...process.env, ...expandEnvVars(cfg.env && typeof cfg.env === 'object' ? cfg.env : {}) },
327
409
  stderr: cfg.stderr ?? 'pipe',
328
410
  });
329
411
  transport.stderr?.on?.('data', (chunk) => {
@@ -331,7 +413,7 @@ async function connectServer(name, cfg) {
331
413
  });
332
414
  }
333
415
  else {
334
- throw new Error(`Invalid config for "${name}": need autoDetect, url (http), or command (stdio)`);
416
+ throw new Error(`Invalid config for "${name}": need autoDetect, type (stdio/http/sse/ws), url (http), or command (stdio)`);
335
417
  }
336
418
  await client.connect(transport);
337
419
  const toolsResult = await client.listTools();
@@ -1,7 +1,10 @@
1
+ import { EFFORT_BETA_HEADER } from './anthropic-effort.mjs';
2
+
1
3
  export const EXTENDED_CACHE_TTL_BETA_HEADER = 'extended-cache-ttl-2025-04-11';
2
4
  export const INTERLEAVED_THINKING_BETA_HEADER = 'interleaved-thinking-2025-05-14';
3
5
  export const FAST_MODE_BETA_HEADER = 'fast-mode-2026-02-01';
4
6
  export const TOOL_SEARCH_BETA_HEADER = 'advanced-tool-use-2025-11-20';
7
+ export { EFFORT_BETA_HEADER };
5
8
 
6
9
  export function supportsAnthropicFastMode(model) {
7
10
  const id = String(model || '').toLowerCase().replace(/\./g, '-');
@@ -12,6 +15,7 @@ export function buildAnthropicBetaHeaders({
12
15
  base = `${INTERLEAVED_THINKING_BETA_HEADER},${EXTENDED_CACHE_TTL_BETA_HEADER}`,
13
16
  fastMode = false,
14
17
  toolSearch = false,
18
+ effort = false,
15
19
  } = {}) {
16
20
  const headers = String(base || '')
17
21
  .split(',')
@@ -23,5 +27,8 @@ export function buildAnthropicBetaHeaders({
23
27
  if (toolSearch && !headers.includes(TOOL_SEARCH_BETA_HEADER)) {
24
28
  headers.push(TOOL_SEARCH_BETA_HEADER);
25
29
  }
30
+ if (effort && !headers.includes(EFFORT_BETA_HEADER)) {
31
+ headers.push(EFFORT_BETA_HEADER);
32
+ }
26
33
  return headers.join(',');
27
34
  }