mixdog 0.9.2 → 0.9.4

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 (417) hide show
  1. package/package.json +8 -3
  2. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  3. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  4. package/scripts/bench/lead-review-tasks.json +20 -0
  5. package/scripts/bench/r4-mixed-tasks.json +20 -0
  6. package/scripts/bench/review-tasks.json +20 -0
  7. package/scripts/bench/round-codex.json +114 -0
  8. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  9. package/scripts/bench/round-mixdog-lead.json +269 -0
  10. package/scripts/bench/round-mixdog.json +126 -0
  11. package/scripts/bench/round-r10-bigsample.json +679 -0
  12. package/scripts/bench/round-r11-codexalign.json +257 -0
  13. package/scripts/bench/round-r4-codex.json +114 -0
  14. package/scripts/bench/round-r4-mixed.json +225 -0
  15. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  16. package/scripts/bench/round-r6-codex.json +114 -0
  17. package/scripts/bench/round-r6-solo.json +257 -0
  18. package/scripts/bench/round-r7-full.json +254 -0
  19. package/scripts/bench/round-r8-fulldefault.json +255 -0
  20. package/scripts/bench-run.mjs +215 -29
  21. package/scripts/build-tui.mjs +13 -1
  22. package/scripts/explore-bench.mjs +124 -0
  23. package/scripts/freevar-smoke.mjs +95 -0
  24. package/scripts/hook-bus-test.mjs +191 -0
  25. package/scripts/internal-comms-bench.mjs +1 -0
  26. package/scripts/internal-comms-smoke.mjs +10 -9
  27. package/scripts/mouse-probe.mjs +45 -0
  28. package/scripts/output-style-bench.mjs +13 -6
  29. package/scripts/output-style-smoke.mjs +4 -4
  30. package/scripts/path-suffix-test.mjs +57 -0
  31. package/scripts/provider-toolcall-test.mjs +7 -3
  32. package/scripts/recall-bench.mjs +207 -0
  33. package/scripts/recall-usecase-cases.json +18 -0
  34. package/scripts/recall-usecase-probe.json +6 -0
  35. package/scripts/session-bench.mjs +152 -6
  36. package/scripts/tool-smoke.mjs +30 -67
  37. package/scripts/tui-render-smoke.mjs +90 -0
  38. package/scripts/webhook-smoke.mjs +208 -0
  39. package/src/agents/debugger/AGENT.md +5 -2
  40. package/src/agents/heavy-worker/AGENT.md +21 -11
  41. package/src/agents/maintainer/AGENT.md +4 -0
  42. package/src/agents/reviewer/AGENT.md +3 -2
  43. package/src/agents/worker/AGENT.md +21 -11
  44. package/src/lib/rules-builder.cjs +4 -0
  45. package/src/mixdog-session-runtime.mjs +933 -3731
  46. package/src/output-styles/default.md +34 -9
  47. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  48. package/src/output-styles/minimal.md +4 -1
  49. package/src/output-styles/simple.md +22 -7
  50. package/src/repl.mjs +5 -5
  51. package/src/rules/agent/00-common.md +2 -0
  52. package/src/rules/agent/30-explorer.md +8 -11
  53. package/src/rules/lead/lead-brief.md +12 -0
  54. package/src/rules/lead/lead-tool.md +2 -9
  55. package/src/rules/shared/01-tool.md +11 -5
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  59. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  60. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  61. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  62. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  63. package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
  66. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
  70. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  71. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
  72. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
  73. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  74. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  75. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  76. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  77. package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
  78. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
  79. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  80. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  81. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  82. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  83. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  84. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
  85. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  88. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
  92. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  94. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  96. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  98. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
  99. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
  100. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  101. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  102. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  103. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  107. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  108. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  109. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
  110. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  111. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  112. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  113. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  114. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
  115. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  116. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  117. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  118. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  120. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  121. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  122. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  123. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  124. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  125. package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
  126. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  127. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
  128. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  129. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
  130. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  131. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  132. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  133. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
  136. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  137. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  138. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  141. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
  142. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  143. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  144. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  145. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  146. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  148. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  149. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
  152. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
  155. package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
  156. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  157. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  158. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  159. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  161. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  163. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  164. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  165. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  166. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
  167. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  168. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  169. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  170. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  171. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  172. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  173. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
  174. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  175. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  176. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  177. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  178. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  179. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  180. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  181. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  182. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  183. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  184. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
  185. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
  186. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  187. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  188. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  189. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  190. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  191. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  192. package/src/runtime/channels/backends/discord.mjs +12 -292
  193. package/src/runtime/channels/index.mjs +241 -894
  194. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  195. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  196. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  197. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  198. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  199. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  200. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  201. package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
  202. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  203. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  204. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  205. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  206. package/src/runtime/channels/lib/telegram-format.mjs +19 -22
  207. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  208. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  209. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  210. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  211. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  212. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  213. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  214. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  215. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  216. package/src/runtime/channels/lib/webhook.mjs +36 -570
  217. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  218. package/src/runtime/channels/tool-defs.mjs +11 -130
  219. package/src/runtime/memory/index.mjs +258 -2050
  220. package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
  221. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  222. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  223. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  224. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  225. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  226. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  227. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  228. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  229. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  230. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  231. package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
  232. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  233. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  234. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  235. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  236. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  237. package/src/runtime/memory/lib/memory.mjs +20 -0
  238. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  239. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  240. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  241. package/src/runtime/memory/lib/recall-format.mjs +238 -0
  242. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  243. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  244. package/src/runtime/memory/tool-defs.mjs +6 -14
  245. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  246. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  247. package/src/runtime/search/lib/web-tools.mjs +24 -602
  248. package/src/runtime/shared/abort-controller.mjs +1 -1
  249. package/src/runtime/shared/atomic-file.mjs +26 -1
  250. package/src/runtime/shared/background-tasks.mjs +2 -3
  251. package/src/runtime/shared/buffered-appender.mjs +149 -0
  252. package/src/runtime/shared/launcher-control.mjs +2 -2
  253. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  254. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  255. package/src/runtime/shared/tool-primitives.mjs +308 -0
  256. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  257. package/src/runtime/shared/tool-surface.mjs +80 -898
  258. package/src/runtime/shared/transcript-writer.mjs +52 -2
  259. package/src/runtime/shared/update-checker.mjs +7 -4
  260. package/src/session-runtime/config-helpers.mjs +291 -0
  261. package/src/session-runtime/config-lifecycle.mjs +232 -0
  262. package/src/session-runtime/cwd-plugins.mjs +226 -0
  263. package/src/session-runtime/effort.mjs +128 -0
  264. package/src/session-runtime/fs-utils.mjs +10 -0
  265. package/src/session-runtime/mcp-glue.mjs +177 -0
  266. package/src/session-runtime/model-capabilities.mjs +130 -0
  267. package/src/session-runtime/model-recency.mjs +111 -0
  268. package/src/session-runtime/native-search.mjs +247 -0
  269. package/src/session-runtime/output-styles.mjs +126 -0
  270. package/src/session-runtime/plugin-mcp.mjs +114 -0
  271. package/src/session-runtime/prewarm.mjs +142 -0
  272. package/src/session-runtime/provider-models.mjs +278 -0
  273. package/src/session-runtime/provider-usage.mjs +120 -0
  274. package/src/session-runtime/quick-model-rows.mjs +170 -0
  275. package/src/session-runtime/quick-search-models.mjs +46 -0
  276. package/src/session-runtime/session-hooks.mjs +93 -0
  277. package/src/session-runtime/session-text.mjs +100 -0
  278. package/src/session-runtime/settings-api.mjs +319 -0
  279. package/src/session-runtime/statusline-route.mjs +35 -0
  280. package/src/session-runtime/tool-catalog.mjs +720 -0
  281. package/src/session-runtime/tool-defs.mjs +84 -0
  282. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  283. package/src/session-runtime/workflow.mjs +358 -0
  284. package/src/standalone/agent-tool/helpers.mjs +237 -0
  285. package/src/standalone/agent-tool/notify.mjs +107 -0
  286. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  287. package/src/standalone/agent-tool/render.mjs +152 -0
  288. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  289. package/src/standalone/agent-tool.mjs +155 -677
  290. package/src/standalone/channel-worker.mjs +7 -9
  291. package/src/standalone/explore-tool.mjs +40 -12
  292. package/src/standalone/hook-bus/config.mjs +207 -0
  293. package/src/standalone/hook-bus/constants.mjs +90 -0
  294. package/src/standalone/hook-bus/handlers.mjs +481 -0
  295. package/src/standalone/hook-bus/payload.mjs +31 -0
  296. package/src/standalone/hook-bus/rules.mjs +77 -0
  297. package/src/standalone/hook-bus.mjs +110 -746
  298. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  299. package/src/standalone/opencode-go-login.mjs +125 -0
  300. package/src/standalone/provider-admin.mjs +15 -19
  301. package/src/standalone/usage-dashboard.mjs +3 -1
  302. package/src/tui/App.jsx +1163 -7571
  303. package/src/tui/app/app-format.mjs +206 -0
  304. package/src/tui/app/channel-pickers.mjs +510 -0
  305. package/src/tui/app/clipboard.mjs +67 -0
  306. package/src/tui/app/core-memory-picker.mjs +210 -0
  307. package/src/tui/app/extension-pickers.mjs +506 -0
  308. package/src/tui/app/input-parsers.mjs +193 -0
  309. package/src/tui/app/maintenance-pickers.mjs +324 -0
  310. package/src/tui/app/model-options.mjs +330 -0
  311. package/src/tui/app/model-picker.mjs +365 -0
  312. package/src/tui/app/onboarding-steps.mjs +400 -0
  313. package/src/tui/app/project-picker.mjs +247 -0
  314. package/src/tui/app/provider-setup-picker.mjs +580 -0
  315. package/src/tui/app/resume-picker.mjs +55 -0
  316. package/src/tui/app/route-pickers.mjs +419 -0
  317. package/src/tui/app/settings-picker.mjs +490 -0
  318. package/src/tui/app/slash-commands.mjs +101 -0
  319. package/src/tui/app/slash-dispatch.mjs +427 -0
  320. package/src/tui/app/text-layout.mjs +46 -0
  321. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  322. package/src/tui/app/transcript-window.mjs +671 -0
  323. package/src/tui/app/use-mouse-input.mjs +460 -0
  324. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  325. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  326. package/src/tui/app/use-transcript-window.mjs +589 -0
  327. package/src/tui/components/ConfirmBar.jsx +1 -1
  328. package/src/tui/components/Picker.jsx +32 -4
  329. package/src/tui/components/PromptInput.jsx +259 -80
  330. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  331. package/src/tui/components/StatusLine.jsx +63 -12
  332. package/src/tui/components/TextEntryPanel.jsx +11 -0
  333. package/src/tui/components/ToolExecution.jsx +56 -588
  334. package/src/tui/components/TranscriptItem.jsx +105 -0
  335. package/src/tui/components/UsagePanel.jsx +18 -4
  336. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  337. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  338. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  339. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  340. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  341. package/src/tui/components/tool-output-format.mjs +2 -2
  342. package/src/tui/display-width.mjs +20 -3
  343. package/src/tui/dist/index.mjs +18034 -17188
  344. package/src/tui/engine/agent-envelope.mjs +296 -0
  345. package/src/tui/engine/agent-job-feed.mjs +133 -0
  346. package/src/tui/engine/boot-profile.mjs +21 -0
  347. package/src/tui/engine/labels.mjs +67 -0
  348. package/src/tui/engine/notice-text.mjs +112 -0
  349. package/src/tui/engine/notification-plan.mjs +76 -0
  350. package/src/tui/engine/queue-helpers.mjs +161 -0
  351. package/src/tui/engine/render-timing.mjs +17 -0
  352. package/src/tui/engine/session-stats.mjs +46 -0
  353. package/src/tui/engine/tool-approval.mjs +94 -0
  354. package/src/tui/engine/tool-call-fields.mjs +23 -0
  355. package/src/tui/engine/tool-card-results.mjs +234 -0
  356. package/src/tui/engine/tool-result-status.mjs +135 -0
  357. package/src/tui/engine/tool-result-text.mjs +126 -0
  358. package/src/tui/engine.mjs +405 -1385
  359. package/src/tui/figures.mjs +5 -0
  360. package/src/tui/index.jsx +105 -0
  361. package/src/tui/input-editing.mjs +60 -10
  362. package/src/tui/keyboard-protocol.mjs +2 -2
  363. package/src/tui/lib/voice-recorder.mjs +35 -19
  364. package/src/tui/markdown/format-token.mjs +11 -9
  365. package/src/tui/paste-attachments.mjs +38 -0
  366. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  367. package/src/tui/theme.mjs +6 -0
  368. package/src/tui/themes/base.mjs +2 -2
  369. package/src/tui/themes/kanagawa.mjs +4 -4
  370. package/src/tui/themes/teal.mjs +4 -5
  371. package/src/tui/themes/utils.mjs +1 -1
  372. package/src/ui/statusline-agents.mjs +213 -0
  373. package/src/ui/statusline-format.mjs +146 -0
  374. package/src/ui/statusline-segments.mjs +148 -0
  375. package/src/ui/statusline.mjs +77 -462
  376. package/src/ui/tool-card.mjs +0 -1
  377. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  378. package/src/workflows/default/WORKFLOW.md +16 -9
  379. package/src/workflows/sequential/WORKFLOW.md +16 -11
  380. package/src/workflows/solo/WORKFLOW.md +5 -1
  381. package/vendor/ink/build/display-width.js +19 -3
  382. package/vendor/ink/build/ink.js +103 -6
  383. package/vendor/ink/build/log-update.js +17 -3
  384. package/vendor/ink/build/wrap-text.js +125 -0
  385. package/scripts/_test-folder-dialog.mjs +0 -30
  386. package/scripts/fix-brief-fn.mjs +0 -35
  387. package/scripts/fix-format-tool-surface.mjs +0 -24
  388. package/scripts/fix-tool-exec-visible.mjs +0 -42
  389. package/scripts/patch-agent-brief.mjs +0 -48
  390. package/scripts/patch-app.mjs +0 -21
  391. package/scripts/patch-app2.mjs +0 -18
  392. package/scripts/patch-dist-brief.mjs +0 -96
  393. package/scripts/patch-tool-exec.mjs +0 -70
  394. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  395. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  396. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  397. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  398. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  399. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  400. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  401. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  402. package/src/standalone/agent-task-status.test.mjs +0 -76
  403. package/src/tui/components/tool-output-format.test.mjs +0 -399
  404. package/src/tui/display-width.test.mjs +0 -35
  405. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  406. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  407. package/src/tui/markdown/format-token.test.mjs +0 -354
  408. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  409. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  410. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  411. package/src/tui/prompt-history-store.test.mjs +0 -52
  412. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  413. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  414. package/src/ui/markdown.test.mjs +0 -70
  415. package/src/ui/statusline-context-label.test.mjs +0 -15
  416. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  417. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -0,0 +1,226 @@
1
+ // cwd-plugins.mjs — cwd resolution/apply + plugins-status + core-memory context,
2
+ // extracted from mixdog-session-runtime.mjs. Dependency-injected factory that
3
+ // closes over the facade's mutable cwd/config/session state via getter/setter
4
+ // injection (getCurrentCwd/setCurrentCwd/getConfig/getSession/...) plus the MCP
5
+ // glue + prewarm callbacks. The facade keeps ownership of the mutable locals;
6
+ // this module owns the pure logic that was previously inline.
7
+
8
+ export function createCwdPlugins({
9
+ // mutable-state injection
10
+ getCurrentCwd,
11
+ setCurrentCwd,
12
+ getConfig,
13
+ getSession,
14
+ getRoute,
15
+ getLastProjectMcpKey,
16
+ setLastProjectMcpKey,
17
+ isCodeGraphPrewarmLazy,
18
+ isCodeGraphFirstTurnPrewarmDone,
19
+ getCodeGraphPrewarmDelayMs,
20
+ setSessionNeedsCwdRefresh,
21
+ // callbacks / deps
22
+ connectConfiguredMcp,
23
+ invalidatePreSessionToolSurface,
24
+ scheduleCodeGraphPrewarm,
25
+ hooks,
26
+ hookCommonPayload,
27
+ bootProfile,
28
+ getMemoryModule,
29
+ // channel-admin / registry helpers
30
+ listRegisteredPlugins,
31
+ pluginAdminStatus,
32
+ pluginManifest,
33
+ pluginMcpServerName,
34
+ mcpScriptForPlugin,
35
+ countSkillFiles,
36
+ readProjectMcpServers,
37
+ writeLastSessionCwd,
38
+ // shared utils
39
+ clean,
40
+ resolve,
41
+ statSync,
42
+ existsSync,
43
+ cfgMod,
44
+ STANDALONE_DATA_DIR,
45
+ }) {
46
+ function resolveCwdPath(value) {
47
+ const raw = clean(value);
48
+ if (!raw) throw new Error('cwd: path is required for action=set');
49
+ const next = resolve(getCurrentCwd() || process.cwd(), raw);
50
+ const stat = statSync(next);
51
+ if (!stat.isDirectory()) throw new Error(`cwd: not a directory: ${next}`);
52
+ return next;
53
+ }
54
+
55
+ function applyResolvedCwd(nextCwd, { markRefresh = true } = {}) {
56
+ const resolved = resolve(nextCwd);
57
+ const stat = statSync(resolved);
58
+ if (!stat.isDirectory()) throw new Error(`cwd: not a directory: ${resolved}`);
59
+ const changed = resolve(getCurrentCwd()) !== resolved;
60
+ setCurrentCwd(resolved);
61
+ const currentCwd = resolved;
62
+ process.env.MIXDOG_SESSION_CWD = currentCwd;
63
+ writeLastSessionCwd(currentCwd);
64
+ const session = getSession();
65
+ if (session) session.cwd = currentCwd;
66
+ // cwd changes NEVER recreate the session: a mid-conversation cwd switch must
67
+ // preserve the full message history (and the BP1–BP3 prompt cache). We only
68
+ // retarget the live session's cwd in place; tool execution already reads the
69
+ // current cwd per turn. `cwd` is intentionally absent from the prompt
70
+ // context (see composeSystemPrompt), so there is nothing prompt-side to
71
+ // refresh either. `markRefresh`/`changed` are kept only for signature
72
+ // compatibility with existing callers.
73
+ void markRefresh;
74
+ // Lazy mode: before the first turn (e.g. the initial project-selection
75
+ // cwd set), do NOT prewarm — that is exactly the post-first-frame freeze
76
+ // we are avoiding. Once a turn has run, an in-session cwd switch DOES
77
+ // prewarm the new dir, since a lookup there is now likely.
78
+ if (isCodeGraphPrewarmLazy() && !isCodeGraphFirstTurnPrewarmDone()) {
79
+ bootProfile('code-graph:prewarm-lazy', { reason: 'cwd-deferred-to-first-turn' });
80
+ } else {
81
+ const delay = getCodeGraphPrewarmDelayMs();
82
+ scheduleCodeGraphPrewarm(changed ? 0 : delay, changed ? 'cwd-change' : 'cwd');
83
+ }
84
+ // Project-local `.mcp.json` follows the cwd: when the effective project MCP
85
+ // set changes, reconnect in the background (never await — this stays sync,
86
+ // and the session is preserved). Guarded so a no-op cwd change does not
87
+ // churn connections.
88
+ if (changed) {
89
+ try {
90
+ const nextKey = resolved + '\u0000' + JSON.stringify(readProjectMcpServers(resolved));
91
+ if (nextKey !== getLastProjectMcpKey()) {
92
+ setLastProjectMcpKey(nextKey);
93
+ void connectConfiguredMcp({ reset: true })
94
+ .then(() => invalidatePreSessionToolSurface())
95
+ .catch(() => {});
96
+ }
97
+ } catch {}
98
+ }
99
+ // CwdChanged: bridge an effective cwd switch to the standard hook bus.
100
+ // No matcher event — payload is minimal { cwd }. Fire-and-forget.
101
+ if (changed) {
102
+ try { void hooks.dispatch('CwdChanged', hookCommonPayload({ cwd: currentCwd })); } catch {}
103
+ }
104
+ return currentCwd;
105
+ }
106
+
107
+ async function refreshSessionForCwdIfNeeded(reason = 'cwd-change') {
108
+ // No-op: cwd changes are applied in place by applyResolvedCwd and never
109
+ // tear down the session. Retained as a stable hook for ask()'s pre-turn
110
+ // call so the surrounding turn flow is unchanged.
111
+ void reason;
112
+ setSessionNeedsCwdRefresh(false);
113
+ return getSession();
114
+ }
115
+
116
+ function pluginsStatus() {
117
+ const config = getConfig();
118
+ const dataDir = cfgMod.getPluginData?.();
119
+ const configuredMcp = config?.mcpServers && typeof config.mcpServers === 'object'
120
+ ? config.mcpServers
121
+ : {};
122
+ const plugins = [];
123
+ const addRegisteredPlugin = (entry) => {
124
+ const root = clean(entry.root);
125
+ if (!root || !existsSync(root)) return;
126
+ const manifest = pluginManifest(root);
127
+ const name = clean(manifest.name) || clean(manifest.id) || clean(entry.name) || root.split(/[\\/]/).pop() || root;
128
+ const plugin = {
129
+ id: clean(entry.id) || name,
130
+ name,
131
+ title: clean(manifest.title) || clean(manifest.displayName) || clean(entry.title) || name,
132
+ version: clean(manifest.version) || clean(entry.version) || null,
133
+ description: clean(manifest.description) || clean(entry.description),
134
+ marketplace: null,
135
+ source: clean(entry.sourceType) === 'local' ? 'local' : 'registry',
136
+ sourceUrl: clean(entry.source),
137
+ sourceType: clean(entry.sourceType) || 'git',
138
+ managed: entry.managed !== false,
139
+ root,
140
+ installedAt: entry.installedAt || null,
141
+ updatedAt: entry.updatedAt || null,
142
+ skillCount: countSkillFiles(root),
143
+ mcpScript: mcpScriptForPlugin(root),
144
+ };
145
+ plugin.mcpServerName = pluginMcpServerName(plugin);
146
+ plugin.mcpEnabled = Object.prototype.hasOwnProperty.call(configuredMcp, plugin.mcpServerName)
147
+ || Object.keys(configuredMcp).some((k) => k.startsWith(`${plugin.mcpServerName}--`));
148
+ plugins.push(plugin);
149
+ };
150
+
151
+ for (const entry of listRegisteredPlugins({ dataDir })) addRegisteredPlugin(entry);
152
+
153
+ plugins.sort((a, b) => {
154
+ if (a.source !== b.source) return a.source.localeCompare(b.source);
155
+ return a.name.localeCompare(b.name);
156
+ });
157
+ const admin = pluginAdminStatus({ dataDir });
158
+ return {
159
+ count: plugins.length,
160
+ plugins,
161
+ roots: {
162
+ registry: admin.registryPath,
163
+ installed: admin.installRoot,
164
+ },
165
+ };
166
+ }
167
+
168
+ function formatCoreMemoryLines(payload = {}) {
169
+ const seen = new Set();
170
+ const lines = [];
171
+ for (const value of [
172
+ ...(Array.isArray(payload.userLines) ? payload.userLines : []),
173
+ ...(Array.isArray(payload.dbLines) ? payload.dbLines : []),
174
+ ]) {
175
+ const text = clean(value).replace(/\s+/g, ' ');
176
+ if (!text) continue;
177
+ const key = text.toLowerCase();
178
+ if (seen.has(key)) continue;
179
+ seen.add(key);
180
+ lines.push(`- ${text}`);
181
+ if (lines.length >= 40) break;
182
+ }
183
+ const out = lines.join('\n');
184
+ const maxChars = 6000;
185
+ return out.length > maxChars ? `${out.slice(0, maxChars).replace(/\s+\S*$/, '')}\n- ...` : out;
186
+ }
187
+
188
+ async function loadCoreMemoryContext() {
189
+ // Boot should not pay for memory/PG startup unless explicitly requested.
190
+ // Recall and memory tools still initialize the memory service on first use.
191
+ if (process.env.MIXDOG_BOOT_CORE_MEMORY !== '1') {
192
+ bootProfile('core-memory:skipped');
193
+ return '';
194
+ }
195
+ const startedAt = performance.now();
196
+ let timer = null;
197
+ const timeout = new Promise((resolveTimeout) => {
198
+ timer = setTimeout(() => resolveTimeout(''), 2000);
199
+ timer.unref?.();
200
+ });
201
+ try {
202
+ return await Promise.race([
203
+ (async () => {
204
+ const memoryMod = await getMemoryModule();
205
+ if (typeof memoryMod?.buildSessionCoreMemoryPayload !== 'function') return '';
206
+ return formatCoreMemoryLines(await memoryMod.buildSessionCoreMemoryPayload(getCurrentCwd()));
207
+ })(),
208
+ timeout,
209
+ ]);
210
+ } catch {
211
+ return '';
212
+ } finally {
213
+ if (timer) clearTimeout(timer);
214
+ bootProfile('core-memory:done', { ms: (performance.now() - startedAt).toFixed(1) });
215
+ }
216
+ }
217
+
218
+ return {
219
+ resolveCwdPath,
220
+ applyResolvedCwd,
221
+ refreshSessionForCwdIfNeeded,
222
+ pluginsStatus,
223
+ formatCoreMemoryLines,
224
+ loadCoreMemoryContext,
225
+ };
226
+ }
@@ -0,0 +1,128 @@
1
+ // Reasoning-effort catalogs and coercion. Pure helpers.
2
+ import { clean } from './session-text.mjs';
3
+
4
+ export const TOOL_MODES = new Set(['full', 'readonly', 'lead']);
5
+ export const ALL_EFFORT_LEVELS = new Set(['none', 'low', 'medium', 'high', 'xhigh', 'max']);
6
+ export const EFFORT_LABELS = {
7
+ none: 'None',
8
+ low: 'Low',
9
+ medium: 'Medium',
10
+ high: 'High',
11
+ xhigh: 'Extra High',
12
+ max: 'Max',
13
+ };
14
+
15
+ export const EFFORT_OPTIONS_BY_PROVIDER = {
16
+ openai: ['none', 'low', 'medium', 'high', 'xhigh'],
17
+ 'openai-oauth': ['none', 'low', 'medium', 'high', 'xhigh'],
18
+ anthropic: ['low', 'medium', 'high', 'xhigh', 'max'],
19
+ 'anthropic-oauth': ['low', 'medium', 'high', 'xhigh', 'max'],
20
+ xai: ['none', 'low', 'medium', 'high'],
21
+ 'grok-oauth': ['none', 'low', 'medium', 'high'],
22
+ 'opencode-go': ['high', 'max'],
23
+ };
24
+ export const EFFORT_BY_FAMILY = {
25
+ opus: ['low', 'medium', 'high', 'xhigh', 'max'],
26
+ sonnet: ['low', 'medium', 'high'],
27
+ haiku: [],
28
+ 'gpt-5.5': ['none', 'low', 'medium', 'high', 'xhigh'],
29
+ 'gpt-5.4': ['none', 'low', 'medium', 'high', 'xhigh'],
30
+ 'gpt-5.2': ['none', 'low', 'medium', 'high', 'xhigh'],
31
+ 'gpt-5': ['none', 'low', 'medium', 'high', 'xhigh'],
32
+ 'gpt-mini': ['none', 'low', 'medium', 'high', 'xhigh'],
33
+ 'gpt-nano': ['none', 'low', 'medium', 'high'],
34
+ 'gpt-codex': ['none', 'low', 'medium', 'high'],
35
+ grok: ['none', 'low', 'medium', 'high'],
36
+ };
37
+ export const EFFORT_FALLBACKS = {
38
+ max: ['max', 'xhigh', 'high', 'medium', 'low'],
39
+ xhigh: ['xhigh', 'high', 'medium', 'low'],
40
+ high: ['high', 'medium', 'low'],
41
+ medium: ['medium', 'low'],
42
+ low: ['low'],
43
+ none: ['none'],
44
+ };
45
+
46
+ export function normalizeToolMode(mode) {
47
+ const value = String(mode || '').trim().toLowerCase();
48
+ return TOOL_MODES.has(value) ? value : 'full';
49
+ }
50
+
51
+ export function normalizeEffortInput(value) {
52
+ const v = clean(value).toLowerCase();
53
+ if (!v || v === 'auto') return null;
54
+ if (!ALL_EFFORT_LEVELS.has(v)) {
55
+ throw new Error(`effort must be one of auto, ${[...ALL_EFFORT_LEVELS].join(', ')}`);
56
+ }
57
+ return v;
58
+ }
59
+
60
+ export function effortOptionsFor(provider, model) {
61
+ const providerAllowed = EFFORT_OPTIONS_BY_PROVIDER[provider] || null;
62
+ const filterProvider = (values) => {
63
+ const unique = [...new Set((values || []).map(clean).filter(Boolean))];
64
+ return providerAllowed ? unique.filter((v) => providerAllowed.includes(v)) : unique;
65
+ };
66
+ const declared = Array.isArray(model?.reasoningLevels)
67
+ ? model.reasoningLevels.map(clean).filter(Boolean)
68
+ : [];
69
+ const family = clean(model?.family).toLowerCase();
70
+ if (Array.isArray(model?.reasoningLevels)) {
71
+ if (declared.length) return filterProvider(declared);
72
+ if (Object.prototype.hasOwnProperty.call(EFFORT_BY_FAMILY, family)) {
73
+ return filterProvider(EFFORT_BY_FAMILY[family]);
74
+ }
75
+ return [];
76
+ }
77
+ const reasoningOptionEffort = Array.isArray(model?.reasoningOptions)
78
+ ? model.reasoningOptions.find((option) => clean(option?.type).toLowerCase() === 'effort')
79
+ : null;
80
+ const reasoningOptionValues = Array.isArray(reasoningOptionEffort?.values)
81
+ ? reasoningOptionEffort.values.map(clean).filter(Boolean)
82
+ : [];
83
+ if (reasoningOptionValues.length) return filterProvider(reasoningOptionValues);
84
+ if (Object.prototype.hasOwnProperty.call(EFFORT_BY_FAMILY, family)) {
85
+ return filterProvider(EFFORT_BY_FAMILY[family]);
86
+ }
87
+ return providerAllowed || [];
88
+ }
89
+
90
+ export function coerceEffortFor(provider, model, effort) {
91
+ if (!effort) return null;
92
+ const allowed = effortOptionsFor(provider, model);
93
+ if (!allowed || allowed.length === 0) return null;
94
+ if (allowed.includes(effort)) return effort;
95
+ for (const candidate of EFFORT_FALLBACKS[effort] || []) {
96
+ if (allowed.includes(candidate)) return candidate;
97
+ }
98
+ return null;
99
+ }
100
+
101
+ export function normalizeSavedEffort(value) {
102
+ try {
103
+ return normalizeEffortInput(value);
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
109
+ export function effortItemsFor(provider, model, activeEffort) {
110
+ const allowed = effortOptionsFor(provider, model);
111
+ const items = [];
112
+ for (const value of allowed || []) {
113
+ items.push({
114
+ value,
115
+ label: EFFORT_LABELS[value] || value,
116
+ description: value === activeEffort ? 'current' : '',
117
+ });
118
+ }
119
+ return items;
120
+ }
121
+
122
+ export function toolSpecForMode(mode) {
123
+ return mode === 'readonly' ? ['tools:readonly'] : 'full';
124
+ }
125
+
126
+ export function deferredSurfaceModeForLead(mode) {
127
+ return mode === 'readonly' ? 'readonly' : 'lead';
128
+ }
@@ -0,0 +1,10 @@
1
+ // Small filesystem read helpers used by session-runtime submodules.
2
+ import { readFileSync } from 'node:fs';
3
+
4
+ export function readJsonSafe(path) {
5
+ try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return null; }
6
+ }
7
+
8
+ export function readTextSafe(path) {
9
+ try { return readFileSync(path, 'utf8').trim(); } catch { return ''; }
10
+ }
@@ -0,0 +1,177 @@
1
+ // MCP config/status/connect glue, extracted from mixdog-session-runtime.mjs.
2
+ // Dependency-injected factory: all live state (config, currentCwd, connect
3
+ // generation/in-flight/failures) is threaded through accessors + a caller-owned
4
+ // `state` object so the facade's teardown/reconnect paths still observe it.
5
+ // Method behavior is byte-for-byte identical; only grouping changes.
6
+ import { resolve } from 'node:path';
7
+ import { clean } from './session-text.mjs';
8
+ import { readProjectMcpServers } from './plugin-mcp.mjs';
9
+
10
+ export function createMcpGlue({
11
+ mcpClient,
12
+ getConfig,
13
+ getCurrentCwd,
14
+ state,
15
+ }) {
16
+ function mcpTransportLabel(cfg = {}) {
17
+ if (cfg.autoDetect) return `autoDetect:${cfg.autoDetect}`;
18
+ try {
19
+ return mcpClient.resolveMcpTransportKind(cfg);
20
+ } catch {
21
+ return 'unknown';
22
+ }
23
+ }
24
+
25
+ // Merge mixdog-config `agent.mcpServers` with project-local `.mcp.json`.
26
+ // On name collision the project-local `.mcp.json` entry WINS (Claude Code
27
+ // precedence: project > user config). `sources[name]` records each server's
28
+ // origin ('config' | 'project') for status reporting.
29
+ function resolveEffectiveMcpServers() {
30
+ const config = getConfig();
31
+ const configured = config?.mcpServers && typeof config.mcpServers === 'object'
32
+ ? config.mcpServers
33
+ : {};
34
+ const project = readProjectMcpServers(getCurrentCwd());
35
+ const servers = { ...configured, ...project };
36
+ const sources = {};
37
+ for (const name of Object.keys(configured)) sources[name] = 'config';
38
+ for (const name of Object.keys(project)) sources[name] = 'project';
39
+ return { servers, sources };
40
+ }
41
+
42
+ function mcpStatus() {
43
+ const { servers: configured, sources } = resolveEffectiveMcpServers();
44
+ const connected = new Map((mcpClient.getMcpServerStatus?.() || []).map((row) => [row.name, row]));
45
+ const failures = new Map((state.mcpFailures || []).map((row) => [row.name, row]));
46
+ const servers = [];
47
+ for (const [name, cfg] of Object.entries(configured)) {
48
+ const live = connected.get(name);
49
+ const fail = failures.get(name);
50
+ servers.push({
51
+ name,
52
+ configured: true,
53
+ enabled: cfg?.enabled !== false,
54
+ connected: Boolean(live),
55
+ status: cfg?.enabled === false ? 'disabled' : live ? 'connected' : fail ? 'failed' : 'disconnected',
56
+ transport: mcpTransportLabel(cfg),
57
+ toolCount: live?.toolCount || 0,
58
+ tools: live?.tools || [],
59
+ error: fail?.msg || null,
60
+ source: sources[name] || 'config',
61
+ });
62
+ connected.delete(name);
63
+ }
64
+ for (const live of connected.values()) {
65
+ servers.push({ ...live, configured: false, status: 'connected' });
66
+ }
67
+ servers.sort((a, b) => String(a.name).localeCompare(String(b.name)));
68
+ return {
69
+ servers,
70
+ configuredCount: Object.keys(configured).length,
71
+ connectedCount: servers.filter((row) => row.connected).length,
72
+ failedCount: servers.filter((row) => row.status === 'failed').length,
73
+ };
74
+ }
75
+
76
+ async function connectConfiguredMcp({ reset = false } = {}) {
77
+ // Serialize reconnects: boot connect, cwd-change reset, and rapid cwd
78
+ // switches must never interleave their disconnect/connect phases, or an
79
+ // older run finishing after a newer reset could re-add stale servers into
80
+ // the shared client registry. Approach: a generation token + a single
81
+ // in-flight promise. Each call bumps the generation, waits for any prior
82
+ // run to finish, then bails if a newer call has superseded it — leaving the
83
+ // latest requested effective-server-set in the registry.
84
+ const gen = ++state.mcpConnectGeneration;
85
+ if (state.mcpConnectInFlight) {
86
+ try { await state.mcpConnectInFlight; } catch { /* prior run's failures already captured */ }
87
+ }
88
+ if (gen !== state.mcpConnectGeneration) return mcpStatus();
89
+ const run = (async () => {
90
+ if (reset) await mcpClient.disconnectAll?.();
91
+ state.mcpFailures = [];
92
+ const { servers } = resolveEffectiveMcpServers();
93
+ if (Object.keys(servers).length === 0) return;
94
+ try {
95
+ await mcpClient.connectMcpServers(servers);
96
+ } catch (error) {
97
+ state.mcpFailures = Array.isArray(error?.failures)
98
+ ? error.failures
99
+ : [{ name: 'mcp', msg: error?.message || String(error) }];
100
+ }
101
+ })();
102
+ state.mcpConnectInFlight = run;
103
+ try {
104
+ await run;
105
+ } finally {
106
+ if (state.mcpConnectInFlight === run) state.mcpConnectInFlight = null;
107
+ }
108
+ return mcpStatus();
109
+ }
110
+
111
+ function normalizeMcpServerInput(input = {}) {
112
+ const currentCwd = getCurrentCwd();
113
+ const name = clean(input.name).toLowerCase().replace(/[^a-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '');
114
+ if (!name) throw new Error('MCP server name is required');
115
+ const coerceStringRecord = (value) => {
116
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
117
+ const out = {};
118
+ for (const [key, val] of Object.entries(value)) {
119
+ if (val === undefined || val === null) continue;
120
+ out[String(key)] = String(val);
121
+ }
122
+ return Object.keys(out).length > 0 ? out : null;
123
+ };
124
+ const withOptionalHeaders = (config) => {
125
+ const headers = coerceStringRecord(input.headers);
126
+ if (headers) config.headers = headers;
127
+ return config;
128
+ };
129
+ const url = clean(input.url);
130
+ const type = clean(input.type).toLowerCase();
131
+ if (url) {
132
+ if (type === 'sse') {
133
+ if (!/^https?:\/\//i.test(url)) throw new Error('MCP URL must start with http:// or https://');
134
+ return { name, config: withOptionalHeaders({ type: 'sse', url }) };
135
+ }
136
+ if (type === 'ws') {
137
+ if (!/^(?:wss?|https?):\/\//i.test(url)) {
138
+ throw new Error('MCP WebSocket URL must start with ws://, wss://, http://, or https://');
139
+ }
140
+ return { name, config: withOptionalHeaders({ type: 'ws', url }) };
141
+ }
142
+ if (type === 'http' || type === 'streamable-http') {
143
+ if (!/^https?:\/\//i.test(url)) throw new Error('MCP URL must start with http:// or https://');
144
+ return { name, config: withOptionalHeaders({ type: 'http', url }) };
145
+ }
146
+ if (/^wss?:\/\//i.test(url)) {
147
+ return { name, config: withOptionalHeaders({ type: 'ws', url }) };
148
+ }
149
+ if (!/^https?:\/\//i.test(url)) throw new Error('MCP URL must start with http:// or https://');
150
+ return { name, config: withOptionalHeaders({ type: 'http', url }) };
151
+ }
152
+ const command = clean(input.command);
153
+ if (!command) throw new Error('MCP server command or URL is required');
154
+ const args = Array.isArray(input.args)
155
+ ? input.args.map((v) => String(v)).filter(Boolean)
156
+ : clean(input.args).split(/\s+/).filter(Boolean);
157
+ const requestedCwd = clean(input.cwd);
158
+ const cwdForServer = requestedCwd ? resolve(currentCwd, requestedCwd) : currentCwd;
159
+ const root = resolve(currentCwd);
160
+ const resolvedCwd = resolve(cwdForServer);
161
+ if (resolvedCwd !== root && !resolvedCwd.startsWith(`${root}\\`) && !resolvedCwd.startsWith(`${root}/`)) {
162
+ throw new Error('MCP server cwd must stay under the current project');
163
+ }
164
+ const config = { type: 'stdio', command, args, cwd: resolvedCwd };
165
+ const env = coerceStringRecord(input.env);
166
+ if (env) config.env = env;
167
+ return { name, config };
168
+ }
169
+
170
+ return {
171
+ mcpTransportLabel,
172
+ resolveEffectiveMcpServers,
173
+ mcpStatus,
174
+ connectConfiguredMcp,
175
+ normalizeMcpServerInput,
176
+ };
177
+ }
@@ -0,0 +1,130 @@
1
+ // Provider/model capability probes: fast-tier + hosted web-search support, and
2
+ // model-settings persistence. Pure except saveModelSettings (takes cfgMod).
3
+ import { clean, hasOwn } from './session-text.mjs';
4
+
5
+ const FAST_CAPABLE_PROVIDERS = new Set(['anthropic', 'anthropic-oauth', 'openai', 'openai-oauth']);
6
+ export const LAZY_SECRET_PROVIDERS = new Set(['openai-oauth', 'anthropic-oauth', 'grok-oauth', 'ollama', 'lmstudio']);
7
+
8
+ export function routeFastKey(provider, model) {
9
+ const p = clean(provider);
10
+ const m = clean(model);
11
+ return p && m ? `${p}/${m}` : '';
12
+ }
13
+
14
+ function openAiModelMetaSupportsFast(model) {
15
+ const tiers = Array.isArray(model?.serviceTiers) ? model.serviceTiers : [];
16
+ const speedTiers = Array.isArray(model?.additionalSpeedTiers) ? model.additionalSpeedTiers : [];
17
+ if (tiers.length || speedTiers.length || model?.defaultServiceTier) {
18
+ return tiers.some((tier) => tier?.id === 'priority')
19
+ || speedTiers.includes('priority')
20
+ || model?.defaultServiceTier === 'priority';
21
+ }
22
+ const id = clean(model?.id || model).toLowerCase();
23
+ if (id.includes('mini') || id.includes('nano') || id.includes('codex')) return false;
24
+ return /^gpt-5(\.|-|$)/.test(id);
25
+ }
26
+
27
+ function openAiDirectModelSupportsFast(model) {
28
+ const id = clean(model?.id || model);
29
+ return /^gpt-5\.5(?:-\d{4}|$)/.test(id)
30
+ || /^gpt-5\.4(?:-\d{4}|$)/.test(id)
31
+ || /^gpt-5\.4-mini(?:-\d{4}|$)/.test(id);
32
+ }
33
+
34
+ function openAiModelSupportsHostedWebSearch(model) {
35
+ const id = clean(model?.id || model).toLowerCase();
36
+ if (!id) return false;
37
+ if (model?.supportsWebSearch === true) return true;
38
+ const tools = [
39
+ ...(Array.isArray(model?.supportedTools) ? model.supportedTools : []),
40
+ ...(Array.isArray(model?.tools) ? model.tools : []),
41
+ ...(Array.isArray(model?.capabilities?.tools) ? model.capabilities.tools : []),
42
+ ].map((tool) => clean(tool?.type || tool?.name || tool).toLowerCase());
43
+ if (tools.some((tool) => tool === 'web_search' || tool === 'web_search_preview')) return true;
44
+ if (/codex|image|audio|tts|stt|embedding|rerank|moderation|search-preview/.test(id)) return false;
45
+ return /^gpt-(5(?:\.|$|-)|4\.1(?:-|$)|4o(?:-|$)|4\.5(?:-|$))/.test(id)
46
+ || /^o[34](?:-|$)/.test(id);
47
+ }
48
+
49
+ function grokModelSupportsHostedWebSearch(model) {
50
+ const id = clean(model?.id || model).toLowerCase();
51
+ if (!id || /imagine|image|video|composer/.test(id)) return false;
52
+ if (id === 'grok-build') return false;
53
+ return /^grok-/.test(id);
54
+ }
55
+
56
+ function geminiModelSupportsHostedWebSearch(model) {
57
+ const id = clean(model?.id || model).toLowerCase();
58
+ if (!id || /embedding|aqa|imagen|veo|tts|image|computer-use|customtools/.test(id)) return false;
59
+ return /^gemini-(3(?:\.|-|$)|2\.5-|2\.0-flash)/.test(id);
60
+ }
61
+
62
+ function anthropicModelSupportsHostedWebSearch(model) {
63
+ const id = clean(model?.id || model).toLowerCase();
64
+ if (!id) return false;
65
+ const match = id.match(/^claude-(opus|sonnet|haiku)-(\d+)(?:[-.](\d+))?/);
66
+ if (!match) return false;
67
+ const major = Number(match[2]) || 0;
68
+ const minor = Number(match[3]) || 0;
69
+ return major > 4 || (major === 4 && minor >= 0);
70
+ }
71
+
72
+ function anthropicModelMetaSupportsFast(model) {
73
+ const id = clean(model?.id || model).toLowerCase();
74
+ return /^claude-(opus|sonnet)/.test(id);
75
+ }
76
+
77
+ export function fastCapableFor(provider, model) {
78
+ const p = clean(provider);
79
+ if (!FAST_CAPABLE_PROVIDERS.has(p)) return false;
80
+ if (p === 'openai') return openAiDirectModelSupportsFast(model);
81
+ if (p === 'openai-oauth') return openAiModelMetaSupportsFast(model);
82
+ if (p === 'anthropic' || p === 'anthropic-oauth') return anthropicModelMetaSupportsFast(model);
83
+ return false;
84
+ }
85
+
86
+ // searchCapableFor needs the search-route normalizers, which live in
87
+ // search-routes.mjs and themselves are pure. Wire them in via a factory to keep
88
+ // this module free of a circular import at load time.
89
+ export function makeSearchCapableFor(normalizeSearchProviderId, isSearchCapableProvider) {
90
+ return function searchCapableFor(provider, model) {
91
+ const p = normalizeSearchProviderId(provider);
92
+ if (!isSearchCapableProvider(p)) return false;
93
+ if (p === 'openai' || p === 'openai-oauth') return openAiModelSupportsHostedWebSearch(model);
94
+ if (p === 'grok-oauth' || p === 'xai') return grokModelSupportsHostedWebSearch(model);
95
+ if (p === 'gemini') return geminiModelSupportsHostedWebSearch(model);
96
+ if (p === 'anthropic' || p === 'anthropic-oauth') return anthropicModelSupportsHostedWebSearch(model);
97
+ return model?.supportsWebSearch === true;
98
+ };
99
+ }
100
+
101
+ export function fastPreferenceFor(config, provider, model) {
102
+ const key = routeFastKey(provider, model);
103
+ if (!key) return false;
104
+ const saved = config?.modelSettings?.[key];
105
+ if (saved && typeof saved === 'object' && hasOwn(saved, 'fast')) return saved.fast === true;
106
+ return config?.fastModels?.[key] === true;
107
+ }
108
+
109
+ export function saveModelSettings(cfgMod, route, { fastCapable = true, baseConfig = null } = {}) {
110
+ const key = routeFastKey(route?.provider, route?.model);
111
+ if (!key) return baseConfig || cfgMod.loadConfig();
112
+ const nextConfig = baseConfig || cfgMod.loadConfig();
113
+ const modelSettings = { ...(nextConfig.modelSettings || {}) };
114
+ const nextSetting = { ...(modelSettings[key] || {}) };
115
+ if (hasOwn(route, 'effort') && route.effort) nextSetting.effort = route.effort;
116
+ else delete nextSetting.effort;
117
+ if (fastCapable) nextSetting.fast = route.fast === true;
118
+ else nextSetting.fast = false;
119
+ modelSettings[key] = nextSetting;
120
+
121
+ // Legacy compatibility: keep fastModels true entries for old readers, but
122
+ // let modelSettings.fast=false override them in new readers.
123
+ const fastModels = { ...(nextConfig.fastModels || {}) };
124
+ if (nextSetting.fast === true) fastModels[key] = true;
125
+ else delete fastModels[key];
126
+
127
+ const savedConfig = { ...nextConfig, modelSettings, fastModels };
128
+ cfgMod.saveConfig(savedConfig);
129
+ return savedConfig;
130
+ }