mixdog 0.9.3 → 0.9.5

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 (382) hide show
  1. package/README.md +112 -38
  2. package/package.json +10 -3
  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/r5-orchestrated-task.json +7 -0
  7. package/scripts/bench/review-tasks.json +20 -0
  8. package/scripts/bench/round-codex.json +114 -0
  9. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  10. package/scripts/bench/round-mixdog-lead.json +269 -0
  11. package/scripts/bench/round-mixdog.json +126 -0
  12. package/scripts/bench/round-r10-bigsample.json +679 -0
  13. package/scripts/bench/round-r11-codexalign.json +257 -0
  14. package/scripts/bench/round-r13-clientmeta.json +464 -0
  15. package/scripts/bench/round-r14-betafeatures.json +466 -0
  16. package/scripts/bench/round-r15-fulldefault.json +462 -0
  17. package/scripts/bench/round-r16-sessionid.json +466 -0
  18. package/scripts/bench/round-r17-wirebytes.json +456 -0
  19. package/scripts/bench/round-r18-prewarm.json +468 -0
  20. package/scripts/bench/round-r19-clean.json +472 -0
  21. package/scripts/bench/round-r20-prewarm-clean.json +475 -0
  22. package/scripts/bench/round-r21-delta-retry.json +473 -0
  23. package/scripts/bench/round-r22-full-probe.json +693 -0
  24. package/scripts/bench/round-r23-itemprobe.json +701 -0
  25. package/scripts/bench/round-r24-shapefix.json +677 -0
  26. package/scripts/bench/round-r25-serial.json +464 -0
  27. package/scripts/bench/round-r26-parallel3.json +671 -0
  28. package/scripts/bench/round-r27-parallel10.json +894 -0
  29. package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
  30. package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
  31. package/scripts/bench/round-r30-instid.json +253 -0
  32. package/scripts/bench/round-r31-upgradeprobe.json +256 -0
  33. package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
  34. package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
  35. package/scripts/bench/round-r34-orchestrated.json +120 -0
  36. package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
  37. package/scripts/bench/round-r36-orchestrated-capped.json +128 -0
  38. package/scripts/bench/round-r4-codex.json +114 -0
  39. package/scripts/bench/round-r4-mixed.json +225 -0
  40. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  41. package/scripts/bench/round-r6-codex.json +114 -0
  42. package/scripts/bench/round-r6-solo.json +257 -0
  43. package/scripts/bench/round-r7-full.json +254 -0
  44. package/scripts/bench/round-r8-fulldefault.json +255 -0
  45. package/scripts/bench-run.mjs +251 -32
  46. package/scripts/freevar-smoke.mjs +95 -0
  47. package/scripts/internal-comms-bench.mjs +3 -4
  48. package/scripts/internal-comms-smoke.mjs +10 -9
  49. package/scripts/model-catalog-audit.mjs +209 -0
  50. package/scripts/model-list-sanitize-test.mjs +37 -0
  51. package/scripts/mouse-probe.mjs +45 -0
  52. package/scripts/output-style-bench.mjs +13 -6
  53. package/scripts/output-style-smoke.mjs +4 -4
  54. package/scripts/provider-toolcall-test.mjs +7 -3
  55. package/scripts/recall-bench.mjs +76 -13
  56. package/scripts/recall-quality-cases.json +12 -0
  57. package/scripts/recall-usecase-cases.json +18 -0
  58. package/scripts/session-bench.mjs +152 -6
  59. package/scripts/tool-smoke.mjs +25 -65
  60. package/scripts/tui-render-smoke.mjs +90 -0
  61. package/scripts/webhook-smoke.mjs +208 -0
  62. package/src/agents/debugger/AGENT.md +4 -1
  63. package/src/agents/heavy-worker/AGENT.md +9 -8
  64. package/src/agents/maintainer/AGENT.md +4 -0
  65. package/src/agents/reviewer/AGENT.md +2 -1
  66. package/src/agents/scheduler-task/AGENT.md +2 -3
  67. package/src/agents/webhook-handler/AGENT.md +2 -3
  68. package/src/agents/worker/AGENT.md +10 -7
  69. package/src/app.mjs +12 -1
  70. package/src/headless-role.mjs +7 -1
  71. package/src/lib/rules-builder.cjs +4 -0
  72. package/src/mixdog-session-runtime.mjs +647 -2056
  73. package/src/output-styles/default.md +30 -9
  74. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  75. package/src/output-styles/minimal.md +8 -6
  76. package/src/output-styles/simple.md +21 -7
  77. package/src/rules/agent/00-common.md +6 -3
  78. package/src/rules/agent/30-explorer.md +16 -5
  79. package/src/rules/lead/01-general.md +5 -5
  80. package/src/rules/lead/lead-brief.md +15 -0
  81. package/src/rules/lead/lead-tool.md +6 -15
  82. package/src/rules/shared/01-tool.md +17 -21
  83. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
  84. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  85. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  86. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  87. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  88. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  89. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  90. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  91. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
  92. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  93. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  94. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
  95. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  96. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
  97. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  99. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  100. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  101. package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
  102. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
  103. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +105 -11
  104. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +356 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  107. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  108. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  109. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
  110. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +740 -0
  111. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  112. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +349 -2131
  113. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +143 -1002
  114. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +229 -0
  115. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  116. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +465 -0
  117. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  118. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  119. package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +80 -0
  120. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  121. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  122. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  123. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  124. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  125. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  126. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  127. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  128. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
  129. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  130. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
  131. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +275 -0
  132. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  133. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  134. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  135. package/src/runtime/agent/orchestrator/session/loop.mjs +278 -402
  136. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  137. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
  138. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
  139. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  140. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  141. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  142. package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
  143. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  144. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  145. package/src/runtime/agent/orchestrator/stall-policy.mjs +20 -1
  146. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  147. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  148. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +35 -44
  149. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  152. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  155. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  156. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
  157. package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
  158. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
  159. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
  161. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  163. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  164. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  165. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  166. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  167. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  168. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  169. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  170. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  171. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  172. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  173. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
  174. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
  175. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  176. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  177. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  178. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  179. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  180. package/src/runtime/channels/backends/discord.mjs +27 -318
  181. package/src/runtime/channels/backends/telegram.mjs +8 -12
  182. package/src/runtime/channels/index.mjs +247 -701
  183. package/src/runtime/channels/lib/backend-dispatch.mjs +46 -0
  184. package/src/runtime/channels/lib/config.mjs +37 -149
  185. package/src/runtime/channels/lib/event-pipeline.mjs +22 -5
  186. package/src/runtime/channels/lib/event-queue.mjs +78 -13
  187. package/src/runtime/channels/lib/inbound-routing.mjs +74 -0
  188. package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
  189. package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
  190. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  191. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  192. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  193. package/src/runtime/channels/lib/scheduler.mjs +27 -113
  194. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  195. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  196. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  197. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  198. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  199. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  200. package/src/runtime/channels/lib/webhook/deliveries.mjs +313 -0
  201. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  202. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  203. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  204. package/src/runtime/channels/lib/webhook.mjs +43 -616
  205. package/src/runtime/channels/tool-defs.mjs +11 -130
  206. package/src/runtime/memory/index.mjs +210 -1948
  207. package/src/runtime/memory/lib/core-memory-store.mjs +5 -1
  208. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  209. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  210. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  211. package/src/runtime/memory/lib/ko-morph.mjs +195 -0
  212. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  213. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  214. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  215. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  216. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  217. package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
  218. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  219. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  220. package/src/runtime/memory/lib/memory-recall-store.mjs +69 -12
  221. package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
  222. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  223. package/src/runtime/memory/lib/query-handlers.mjs +802 -0
  224. package/src/runtime/memory/lib/recall-format.mjs +55 -0
  225. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  226. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  227. package/src/runtime/memory/tool-defs.mjs +5 -13
  228. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  229. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  230. package/src/runtime/search/lib/web-tools.mjs +24 -602
  231. package/src/runtime/shared/atomic-file.mjs +26 -1
  232. package/src/runtime/shared/config.mjs +14 -4
  233. package/src/runtime/shared/launcher-control.mjs +2 -2
  234. package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
  235. package/src/runtime/shared/schedules-store.mjs +13 -3
  236. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  237. package/src/runtime/shared/tool-primitives.mjs +308 -0
  238. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  239. package/src/runtime/shared/tool-surface.mjs +80 -898
  240. package/src/runtime/shared/transcript-writer.mjs +23 -0
  241. package/src/runtime/shared/update-checker.mjs +7 -4
  242. package/src/session-runtime/config-helpers.mjs +119 -2
  243. package/src/session-runtime/config-lifecycle.mjs +232 -0
  244. package/src/session-runtime/cwd-plugins.mjs +226 -0
  245. package/src/session-runtime/mcp-glue.mjs +177 -0
  246. package/src/session-runtime/model-recency.mjs +111 -0
  247. package/src/session-runtime/native-search.mjs +247 -0
  248. package/src/session-runtime/output-styles.mjs +11 -9
  249. package/src/session-runtime/prewarm.mjs +142 -0
  250. package/src/session-runtime/provider-models.mjs +278 -0
  251. package/src/session-runtime/provider-usage.mjs +120 -0
  252. package/src/session-runtime/quick-model-rows.mjs +205 -0
  253. package/src/session-runtime/quick-search-models.mjs +47 -0
  254. package/src/session-runtime/session-hooks.mjs +93 -0
  255. package/src/session-runtime/settings-api.mjs +352 -0
  256. package/src/session-runtime/tool-catalog.mjs +29 -29
  257. package/src/session-runtime/tool-defs.mjs +84 -0
  258. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  259. package/src/session-runtime/workflow.mjs +1 -1
  260. package/src/standalone/agent-tool/helpers.mjs +237 -0
  261. package/src/standalone/agent-tool/notify.mjs +107 -0
  262. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  263. package/src/standalone/agent-tool/render.mjs +152 -0
  264. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  265. package/src/standalone/agent-tool.mjs +138 -669
  266. package/src/standalone/channel-admin.mjs +102 -90
  267. package/src/standalone/channel-worker.mjs +4 -7
  268. package/src/standalone/explore-tool.mjs +64 -14
  269. package/src/standalone/hook-bus/config.mjs +207 -0
  270. package/src/standalone/hook-bus/constants.mjs +90 -0
  271. package/src/standalone/hook-bus/handlers.mjs +481 -0
  272. package/src/standalone/hook-bus/payload.mjs +31 -0
  273. package/src/standalone/hook-bus/rules.mjs +77 -0
  274. package/src/standalone/hook-bus.mjs +77 -870
  275. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  276. package/src/standalone/opencode-go-login.mjs +5 -1
  277. package/src/standalone/provider-admin.mjs +1 -16
  278. package/src/standalone/usage-dashboard.mjs +3 -1
  279. package/src/tui/App.jsx +1059 -8110
  280. package/src/tui/app/app-format.mjs +213 -0
  281. package/src/tui/app/channel-pickers.mjs +508 -0
  282. package/src/tui/app/clipboard.mjs +67 -0
  283. package/src/tui/app/core-memory-picker.mjs +210 -0
  284. package/src/tui/app/extension-pickers.mjs +506 -0
  285. package/src/tui/app/input-parsers.mjs +193 -0
  286. package/src/tui/app/maintenance-pickers.mjs +356 -0
  287. package/src/tui/app/model-options.mjs +334 -0
  288. package/src/tui/app/model-picker.mjs +365 -0
  289. package/src/tui/app/onboarding-steps.mjs +400 -0
  290. package/src/tui/app/project-picker.mjs +247 -0
  291. package/src/tui/app/provider-setup-picker.mjs +580 -0
  292. package/src/tui/app/resume-picker.mjs +55 -0
  293. package/src/tui/app/route-pickers.mjs +419 -0
  294. package/src/tui/app/settings-picker.mjs +489 -0
  295. package/src/tui/app/slash-commands.mjs +101 -0
  296. package/src/tui/app/slash-dispatch.mjs +427 -0
  297. package/src/tui/app/text-layout.mjs +46 -0
  298. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  299. package/src/tui/app/transcript-window.mjs +677 -0
  300. package/src/tui/app/use-mouse-input.mjs +460 -0
  301. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  302. package/src/tui/app/use-transcript-scroll.mjs +512 -0
  303. package/src/tui/app/use-transcript-window.mjs +607 -0
  304. package/src/tui/components/ConfirmBar.jsx +10 -7
  305. package/src/tui/components/Picker.jsx +64 -15
  306. package/src/tui/components/PromptInput.jsx +33 -102
  307. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  308. package/src/tui/components/StatusLine.jsx +69 -15
  309. package/src/tui/components/TextEntryPanel.jsx +11 -0
  310. package/src/tui/components/ToolExecution.jsx +52 -594
  311. package/src/tui/components/TranscriptItem.jsx +105 -0
  312. package/src/tui/components/UsagePanel.jsx +18 -4
  313. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  314. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  315. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  316. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  317. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  318. package/src/tui/display-width.mjs +20 -3
  319. package/src/tui/dist/index.mjs +13553 -12384
  320. package/src/tui/engine/agent-job-feed.mjs +133 -0
  321. package/src/tui/engine/notification-plan.mjs +76 -0
  322. package/src/tui/engine/render-timing.mjs +17 -0
  323. package/src/tui/engine/tool-approval.mjs +94 -0
  324. package/src/tui/engine/tool-card-results.mjs +234 -0
  325. package/src/tui/engine/tool-result-status.mjs +135 -0
  326. package/src/tui/engine.mjs +170 -574
  327. package/src/tui/figures.mjs +5 -0
  328. package/src/tui/index.jsx +65 -1
  329. package/src/tui/input-editing.mjs +2 -2
  330. package/src/tui/markdown/format-token.mjs +4 -1
  331. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  332. package/src/tui/theme.mjs +6 -0
  333. package/src/ui/statusline-agents.mjs +213 -0
  334. package/src/ui/statusline-format.mjs +146 -0
  335. package/src/ui/statusline-segments.mjs +148 -0
  336. package/src/ui/statusline.mjs +77 -501
  337. package/src/ui/tool-card.mjs +0 -1
  338. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  339. package/src/workflows/default/WORKFLOW.md +16 -18
  340. package/src/workflows/sequential/WORKFLOW.md +16 -18
  341. package/vendor/ink/build/display-width.js +19 -3
  342. package/vendor/ink/build/ink.js +112 -7
  343. package/vendor/ink/build/log-update.js +17 -3
  344. package/vendor/ink/build/wrap-text.js +125 -0
  345. package/scripts/_test-folder-dialog.mjs +0 -30
  346. package/scripts/fix-brief-fn.mjs +0 -35
  347. package/scripts/fix-format-tool-surface.mjs +0 -24
  348. package/scripts/fix-tool-exec-visible.mjs +0 -42
  349. package/scripts/patch-agent-brief.mjs +0 -48
  350. package/scripts/patch-app.mjs +0 -21
  351. package/scripts/patch-app2.mjs +0 -18
  352. package/scripts/patch-dist-brief.mjs +0 -96
  353. package/scripts/patch-tool-exec.mjs +0 -70
  354. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  355. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  356. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  357. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  358. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  359. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
  360. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  361. package/src/runtime/channels/lib/holidays.mjs +0 -138
  362. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  363. package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
  364. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  365. package/src/standalone/agent-task-status.test.mjs +0 -76
  366. package/src/tui/components/tool-output-format.test.mjs +0 -399
  367. package/src/tui/display-width.test.mjs +0 -35
  368. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  369. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  370. package/src/tui/input-editing.selection.test.mjs +0 -75
  371. package/src/tui/markdown/format-token.test.mjs +0 -354
  372. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  373. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  374. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  375. package/src/tui/paste-fix.test.mjs +0 -119
  376. package/src/tui/prompt-history-store.test.mjs +0 -52
  377. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  378. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  379. package/src/ui/markdown.test.mjs +0 -70
  380. package/src/ui/statusline-context-label.test.mjs +0 -15
  381. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  382. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -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,111 @@
1
+ // Provider-model version parsing, recency comparison, sorting, and cache-row
2
+ // construction. Extracted from mixdog-session-runtime.mjs. Pure except for the
3
+ // injected route provider (for sort priority) and searchCapableFor predicate.
4
+ import { clean } from './session-text.mjs';
5
+
6
+ export function parsedProviderModelVersion(id) {
7
+ const text = clean(id).toLowerCase();
8
+ const claude = text.match(/^claude-[a-z]+-(\d+)(?:[-.](\d+))?/);
9
+ if (claude) return [Number(claude[1]) || 0, Number(claude[2]) || 0];
10
+ const compact = text.match(/(?:^|[-_])(?:o|gpt|grok|qwen|llama|mistral|gemma|phi|glm)(\d+)(?:\.(\d+))?(?:\.(\d{1,3}))?/);
11
+ if (compact) return compact.slice(1).filter((v) => v != null).map((v) => Number(v) || 0);
12
+ const generic = text.match(/(?:^|[-_v])(\d+)(?:\.(\d+))?(?:\.(\d{1,3}))?/);
13
+ return generic ? generic.slice(1).filter((v) => v != null).map((v) => Number(v) || 0) : [];
14
+ }
15
+
16
+ export function compareProviderModelVersion(a, b) {
17
+ const va = parsedProviderModelVersion(a.id || a.display || a.name);
18
+ const vb = parsedProviderModelVersion(b.id || b.display || b.name);
19
+ if (va.length === 0 && vb.length === 0) return 0;
20
+ if (va.length === 0) return 1;
21
+ if (vb.length === 0) return -1;
22
+ for (let i = 0; i < Math.max(va.length, vb.length); i += 1) {
23
+ const delta = (vb[i] || 0) - (va[i] || 0);
24
+ if (delta) return delta;
25
+ }
26
+ return 0;
27
+ }
28
+
29
+ export function providerModelReleaseTime(model) {
30
+ if (model?.releaseDate) {
31
+ const t = Date.parse(model.releaseDate);
32
+ if (Number.isFinite(t)) return t;
33
+ }
34
+ const created = Number(model?.created);
35
+ if (Number.isFinite(created) && created > 0) {
36
+ return created < 1_000_000_000_000 ? created * 1000 : created;
37
+ }
38
+ const dated = clean(model?.id).match(/(?:^|-)(\d{4})(\d{2})(\d{2})(?:$|-)/);
39
+ return dated ? (Date.parse(`${dated[1]}-${dated[2]}-${dated[3]}`) || 0) : 0;
40
+ }
41
+
42
+ export function isClaudeProviderModel(model) {
43
+ return clean(model?.provider).toLowerCase().includes('anthropic')
44
+ && /^claude-[a-z]+-/.test(clean(model?.id).toLowerCase());
45
+ }
46
+
47
+ export function compareProviderModelRecency(a, b) {
48
+ if (isClaudeProviderModel(a) && isClaudeProviderModel(b)) {
49
+ if (a.latest !== b.latest) return a.latest ? -1 : 1;
50
+ const versionDelta = compareProviderModelVersion(a, b);
51
+ if (versionDelta) return versionDelta;
52
+ const ta = providerModelReleaseTime(a);
53
+ const tb = providerModelReleaseTime(b);
54
+ if (ta !== tb) return tb - ta;
55
+ return clean(a.display || a.id).localeCompare(clean(b.display || b.id));
56
+ }
57
+ const ta = providerModelReleaseTime(a);
58
+ const tb = providerModelReleaseTime(b);
59
+ if (ta !== tb) return tb - ta;
60
+ if (a.latest !== b.latest) return a.latest ? -1 : 1;
61
+ const versionDelta = compareProviderModelVersion(a, b);
62
+ if (versionDelta) return versionDelta;
63
+ return clean(a.display || a.id).localeCompare(clean(b.display || b.id));
64
+ }
65
+
66
+ export function sortProviderModels(models, primaryProvider = '') {
67
+ return (models || []).sort((a, b) => {
68
+ const ar = a.provider === primaryProvider ? 0 : 1;
69
+ const br = b.provider === primaryProvider ? 0 : 1;
70
+ if (ar !== br) return ar - br;
71
+ if (a.provider !== b.provider) return a.provider.localeCompare(b.provider);
72
+ return compareProviderModelRecency(a, b);
73
+ });
74
+ }
75
+
76
+ export function isSelectableLlmModel(model) {
77
+ const id = clean(model?.id).toLowerCase();
78
+ const display = clean(model?.display || model?.name).toLowerCase();
79
+ const mode = clean(model?.mode).toLowerCase();
80
+ const text = `${id} ${display}`;
81
+ if (!id) return false;
82
+ if (mode && !['chat', 'completion', 'responses', 'messages'].includes(mode)) return false;
83
+ if (/(^|[-_\s])(image|images|video|videos|audio|tts|stt|speech|embed|embedding|embeddings|rerank|reranker|realtime|moderation|imagine)([-_\s]|$)/i.test(text)) return false;
84
+ if (/(^|[-_\s])(dall[-_\s]?e|sora|imagen)([-_\s]|$)/i.test(text)) return false;
85
+ return true;
86
+ }
87
+
88
+ export function providerModelCacheRow(name, m, searchCapableFor) {
89
+ return {
90
+ id: m.id,
91
+ provider: name,
92
+ display: m.display || m.name || m.id,
93
+ created: typeof m.created === 'number' ? m.created : null,
94
+ releaseDate: m.releaseDate || null,
95
+ contextWindow: m.contextWindow,
96
+ outputTokens: m.outputTokens || null,
97
+ family: m.family || null,
98
+ tier: m.tier || null,
99
+ latest: m.latest === true,
100
+ description: m.description || '',
101
+ supportsVision: m.supportsVision === true,
102
+ supportsFunctionCalling: m.supportsFunctionCalling === true,
103
+ supportsWebSearch: searchCapableFor(name, m),
104
+ supportsPromptCaching: m.supportsPromptCaching === true,
105
+ supportsReasoning: m.supportsReasoning === true,
106
+ reasoningLevels: Array.isArray(m.reasoningLevels) ? m.reasoningLevels : undefined,
107
+ reasoningOptions: Array.isArray(m.reasoningOptions) ? m.reasoningOptions : [],
108
+ reasoningContentField: m.reasoningContentField || null,
109
+ mode: m.mode || null,
110
+ };
111
+ }
@@ -0,0 +1,247 @@
1
+ // Native (provider-hosted) web-search runtime, extracted from
2
+ // mixdog-session-runtime.mjs. Dependency-injected factory pattern: closes over
3
+ // route/searchRoute/config/reg/session accessors supplied by the facade.
4
+ import { clean } from './session-text.mjs';
5
+
6
+ export function createNativeSearch({
7
+ getRoute,
8
+ getSearchRoute,
9
+ setSearchRoute,
10
+ getConfig,
11
+ getSession,
12
+ getReg,
13
+ ensureFullConfig,
14
+ ensureProvidersReady,
15
+ ensureProviderEnabled,
16
+ normalizeSearchProviderId,
17
+ normalizeSearchRouteConfig,
18
+ isDefaultSearchRouteConfig,
19
+ isSearchCapableProvider,
20
+ searchCapableFor,
21
+ }) {
22
+ function normalizeSearchAllowedDomain(site) {
23
+ const raw = clean(site);
24
+ if (!raw) return '';
25
+ try {
26
+ return new URL(/^https?:\/\//i.test(raw) ? raw : `https://${raw}`).hostname.toLowerCase();
27
+ } catch {
28
+ return raw.replace(/^https?:\/\//i, '').split('/')[0].toLowerCase();
29
+ }
30
+ }
31
+
32
+ function nativeSearchUserLocation(locale) {
33
+ if (!locale || typeof locale !== 'object' || Array.isArray(locale)) return null;
34
+ const location = { type: 'approximate' };
35
+ for (const key of ['country', 'region', 'city', 'timezone']) {
36
+ const value = clean(locale[key]);
37
+ if (value) location[key] = value;
38
+ }
39
+ return Object.keys(location).length > 1 ? location : null;
40
+ }
41
+
42
+ function nativeSearchTool(args = {}, toolType = 'web_search', providerId = '') {
43
+ const providerName = normalizeSearchProviderId(providerId);
44
+ const domain = normalizeSearchAllowedDomain(args.site);
45
+ const type = clean(toolType) || 'web_search';
46
+ const location = nativeSearchUserLocation(args.locale);
47
+ if (providerName === 'gemini') {
48
+ return { type: type || 'google_search' };
49
+ }
50
+ if (providerName === 'anthropic' || providerName === 'anthropic-oauth') {
51
+ const tool = {
52
+ type: 'web_search_20250305',
53
+ name: 'web_search',
54
+ max_uses: Math.max(1, Math.min(10, Number(args.maxResults) || 5)),
55
+ };
56
+ if (domain) tool.allowed_domains = [domain];
57
+ if (location) tool.user_location = location;
58
+ return tool;
59
+ }
60
+ if (providerName === 'grok-oauth' || providerName === 'xai') {
61
+ const tool = { type };
62
+ if (domain) tool.filters = { allowed_domains: [domain] };
63
+ return tool;
64
+ }
65
+ const tool = {
66
+ type,
67
+ };
68
+ if (type === 'web_search') {
69
+ tool.search_context_size = clean(args.contextSize) || 'low';
70
+ if (domain) tool.filters = { allowed_domains: [domain] };
71
+ if (location) tool.user_location = location;
72
+ }
73
+ return tool;
74
+ }
75
+
76
+ function nativeSearchToolTypes(routeLike = {}) {
77
+ const envToolType = clean(process.env.MIXDOG_NATIVE_SEARCH_TOOL_TYPE);
78
+ if (envToolType) return [envToolType];
79
+ const configured = clean(routeLike.toolType);
80
+ if (configured) return [configured];
81
+ const providerName = normalizeSearchProviderId(routeLike.provider);
82
+ if (providerName === 'gemini') return ['google_search'];
83
+ if (providerName === 'anthropic' || providerName === 'anthropic-oauth') return ['web_search'];
84
+ if (providerName === 'grok-oauth' || providerName === 'xai') return ['web_search'];
85
+ return ['web_search', 'web_search_preview'];
86
+ }
87
+
88
+ function currentMainSearchModelMeta() {
89
+ const route = getRoute();
90
+ if (!route?.provider || !route?.model) return null;
91
+ return { ...route, id: route.model, display: route.model, name: route.model };
92
+ }
93
+
94
+ function nativeSearchRoutes() {
95
+ const route = getRoute();
96
+ const cfg = ensureFullConfig();
97
+ const searchRoute = normalizeSearchRouteConfig(cfg.searchRoute) || normalizeSearchRouteConfig(getSearchRoute());
98
+ setSearchRoute(searchRoute);
99
+ if (!searchRoute) return [];
100
+ if (isDefaultSearchRouteConfig(searchRoute)) {
101
+ const mainModel = currentMainSearchModelMeta();
102
+ if (!mainModel || !searchCapableFor(route.provider, mainModel)) return [];
103
+ return [{
104
+ key: `default\n${route.provider}\n${route.model}`,
105
+ provider: normalizeSearchProviderId(route.provider),
106
+ model: route.model,
107
+ source: 'default-search-route',
108
+ effort: route.effectiveEffort || route.effort || null,
109
+ fast: route.fast === true,
110
+ toolType: searchRoute.toolType || null,
111
+ }];
112
+ }
113
+ const providerName = normalizeSearchProviderId(searchRoute.provider);
114
+ if (!isSearchCapableProvider(providerName)) return [];
115
+ return [{
116
+ key: `${providerName}\n${searchRoute.model}`,
117
+ provider: providerName,
118
+ model: searchRoute.model,
119
+ source: 'search-route',
120
+ effort: searchRoute.effort || null,
121
+ fast: searchRoute.fast === true,
122
+ toolType: searchRoute.toolType || null,
123
+ }];
124
+ }
125
+
126
+ function nativeSearchMessages(searchArgs = {}) {
127
+ const prompt = searchArgs.prompt || '';
128
+ return [
129
+ {
130
+ role: 'system',
131
+ content: [
132
+ 'You are Mixdog native web search.',
133
+ 'Use the hosted web_search tool for current or external facts.',
134
+ 'Answer concisely, cite source URLs, and do not request local tools or file edits.',
135
+ ].join('\n'),
136
+ },
137
+ { role: 'user', content: prompt },
138
+ ];
139
+ }
140
+
141
+ function flattenNativeSearchSources(result = {}) {
142
+ const out = [];
143
+ const add = (source, fallbackTitle = '') => {
144
+ if (!source || typeof source !== 'object') return;
145
+ const url = clean(source.url || source.uri || source.href || source.source_url);
146
+ if (!url) return;
147
+ out.push({
148
+ title: clean(source.title || source.query || source.name || fallbackTitle || url),
149
+ url,
150
+ snippet: clean(source.snippet || source.text || source.description),
151
+ source: source.source || 'native-web-search',
152
+ provider: source.provider || 'native-web-search',
153
+ });
154
+ };
155
+ for (const citation of Array.isArray(result.citations) ? result.citations : []) add(citation);
156
+ for (const call of Array.isArray(result.webSearchCalls) ? result.webSearchCalls : []) {
157
+ const action = call?.action || {};
158
+ for (const source of Array.isArray(action.sources) ? action.sources : []) add(source, action.query || '');
159
+ if (action.url) add({ url: action.url, title: action.query || '' });
160
+ for (const url of Array.isArray(action.urls) ? action.urls : []) add({ url, title: action.query || '' });
161
+ }
162
+ const seen = new Set();
163
+ return out.filter((item) => {
164
+ const key = item.url || `${item.title}\n${item.snippet}`;
165
+ if (!key || seen.has(key)) return false;
166
+ seen.add(key);
167
+ return true;
168
+ });
169
+ }
170
+
171
+ async function runNativeWebSearch(searchArgs = {}, { signal } = {}) {
172
+ const route = getRoute();
173
+ const session = getSession();
174
+ const reg = getReg();
175
+ const candidates = nativeSearchRoutes();
176
+ if (!candidates.length) {
177
+ if (isDefaultSearchRouteConfig(getSearchRoute())) {
178
+ throw new Error(`default search route requires the current main model to support native web search (${route?.provider || 'unknown'}/${route?.model || 'unknown'})`);
179
+ }
180
+ throw new Error('search route is not configured; open /search to choose a search provider/model');
181
+ }
182
+ const errors = [];
183
+ for (const candidate of candidates) {
184
+ for (const toolType of nativeSearchToolTypes(candidate)) {
185
+ try {
186
+ // Read config lazily: nativeSearchRoutes() above may have run
187
+ // ensureFullConfig() and replaced the facade config with the
188
+ // secret-bearing one — an early snapshot would miss those secrets.
189
+ await ensureProvidersReady(ensureProviderEnabled(getConfig(), candidate.provider));
190
+ const providerImpl = reg.getProvider(candidate.provider);
191
+ if (!providerImpl || typeof providerImpl.send !== 'function') {
192
+ throw new Error(`provider "${candidate.provider}" is not ready`);
193
+ }
194
+ const model = candidate.model;
195
+ const searchTool = nativeSearchTool(searchArgs, toolType, candidate.provider);
196
+ const startedAt = Date.now();
197
+ const result = await providerImpl.send(
198
+ nativeSearchMessages(searchArgs),
199
+ model,
200
+ undefined,
201
+ {
202
+ signal,
203
+ role: 'web-search',
204
+ sessionId: `${session?.id || 'search'}:native-search:${Date.now().toString(36)}`,
205
+ sourceType: 'native-search',
206
+ sourceName: 'search',
207
+ nativeTools: [searchTool],
208
+ nativeInclude: candidate.provider === 'openai' || candidate.provider === 'openai-oauth'
209
+ ? ['web_search_call.action.sources']
210
+ : [],
211
+ toolChoice: candidate.provider === 'gemini' ? 'auto' : 'required',
212
+ ...(candidate.effort ? { effort: candidate.effort } : {}),
213
+ fast: candidate.fast === true,
214
+ onStageChange: () => {},
215
+ onStreamDelta: () => {},
216
+ },
217
+ );
218
+ const sources = flattenNativeSearchSources(result);
219
+ return {
220
+ content: String(result?.content || '').trim(),
221
+ provider: candidate.provider,
222
+ model: result?.model || candidate.model || null,
223
+ usage: result?.usage || null,
224
+ citations: sources,
225
+ webSearchCalls: result?.webSearchCalls || [],
226
+ durationMs: Date.now() - startedAt,
227
+ };
228
+ } catch (err) {
229
+ errors.push(`${candidate.provider}${candidate.model ? `/${candidate.model}` : ''}/${toolType}: ${err?.message || String(err)}`);
230
+ }
231
+ }
232
+ }
233
+ throw new Error(`native web search failed: ${errors.join(' | ')}`);
234
+ }
235
+
236
+ return {
237
+ normalizeSearchAllowedDomain,
238
+ nativeSearchUserLocation,
239
+ nativeSearchTool,
240
+ nativeSearchToolTypes,
241
+ currentMainSearchModelMeta,
242
+ nativeSearchRoutes,
243
+ nativeSearchMessages,
244
+ flattenNativeSearchSources,
245
+ runNativeWebSearch,
246
+ };
247
+ }
@@ -5,18 +5,20 @@ import { readFileSync, readdirSync } from 'node:fs';
5
5
  import { clean } from './session-text.mjs';
6
6
  import { readJsonSafe } from './fs-utils.mjs';
7
7
 
8
- const OUTPUT_STYLE_ORDER = ['default', 'simple', 'minimal', 'oneline'];
8
+ const OUTPUT_STYLE_ORDER = ['default', 'simple', 'minimal', 'extreme-minimal'];
9
9
  const OUTPUT_STYLE_ALIASES = new Map([
10
10
  ['compact', 'default'],
11
11
  ['normal', 'default'],
12
- ['extreme', 'minimal'],
13
- ['extremesimple', 'minimal'],
14
- ['extreme-simple', 'minimal'],
15
- ['extreme_simple', 'minimal'],
16
- ['mono', 'oneline'],
17
- ['oneline', 'oneline'],
18
- ['one-line', 'oneline'],
19
- ['one_line', 'oneline'],
12
+ ['extreme', 'extreme-minimal'],
13
+ ['extremesimple', 'extreme-minimal'],
14
+ ['extreme-simple', 'extreme-minimal'],
15
+ ['extreme_simple', 'extreme-minimal'],
16
+ ['extrememinimal', 'extreme-minimal'],
17
+ ['extreme_minimal', 'extreme-minimal'],
18
+ ['mono', 'extreme-minimal'],
19
+ ['oneline', 'extreme-minimal'],
20
+ ['one-line', 'extreme-minimal'],
21
+ ['one_line', 'extreme-minimal'],
20
22
  ]);
21
23
 
22
24
  export function normalizeOutputStyleId(value) {