mixdog 0.9.3 → 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 (329) hide show
  1. package/package.json +7 -3
  2. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  3. package/scripts/bench/lead-review-tasks.json +20 -0
  4. package/scripts/bench/r4-mixed-tasks.json +20 -0
  5. package/scripts/bench/review-tasks.json +20 -0
  6. package/scripts/bench/round-codex.json +114 -0
  7. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  8. package/scripts/bench/round-mixdog-lead.json +269 -0
  9. package/scripts/bench/round-mixdog.json +126 -0
  10. package/scripts/bench/round-r10-bigsample.json +679 -0
  11. package/scripts/bench/round-r11-codexalign.json +257 -0
  12. package/scripts/bench/round-r4-codex.json +114 -0
  13. package/scripts/bench/round-r4-mixed.json +225 -0
  14. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  15. package/scripts/bench/round-r6-codex.json +114 -0
  16. package/scripts/bench/round-r6-solo.json +257 -0
  17. package/scripts/bench/round-r7-full.json +254 -0
  18. package/scripts/bench/round-r8-fulldefault.json +255 -0
  19. package/scripts/bench-run.mjs +215 -29
  20. package/scripts/freevar-smoke.mjs +95 -0
  21. package/scripts/internal-comms-bench.mjs +1 -0
  22. package/scripts/internal-comms-smoke.mjs +10 -9
  23. package/scripts/mouse-probe.mjs +45 -0
  24. package/scripts/output-style-bench.mjs +13 -6
  25. package/scripts/output-style-smoke.mjs +4 -4
  26. package/scripts/provider-toolcall-test.mjs +7 -3
  27. package/scripts/recall-usecase-cases.json +18 -0
  28. package/scripts/recall-usecase-probe.json +6 -0
  29. package/scripts/session-bench.mjs +152 -6
  30. package/scripts/tool-smoke.mjs +23 -63
  31. package/scripts/tui-render-smoke.mjs +90 -0
  32. package/scripts/webhook-smoke.mjs +208 -0
  33. package/src/agents/debugger/AGENT.md +4 -1
  34. package/src/agents/heavy-worker/AGENT.md +6 -5
  35. package/src/agents/maintainer/AGENT.md +4 -0
  36. package/src/agents/reviewer/AGENT.md +2 -1
  37. package/src/agents/worker/AGENT.md +8 -4
  38. package/src/lib/rules-builder.cjs +4 -0
  39. package/src/mixdog-session-runtime.mjs +632 -2042
  40. package/src/output-styles/default.md +34 -9
  41. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  42. package/src/output-styles/minimal.md +4 -1
  43. package/src/output-styles/simple.md +22 -7
  44. package/src/rules/agent/00-common.md +2 -0
  45. package/src/rules/lead/lead-brief.md +12 -0
  46. package/src/rules/lead/lead-tool.md +0 -11
  47. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  48. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  49. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  50. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  51. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  52. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  53. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  54. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
  55. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
  58. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  59. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
  60. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  61. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  62. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  63. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  64. package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
  65. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
  66. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  67. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  68. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  69. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  70. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  72. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
  73. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +297 -2123
  76. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +130 -1002
  77. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  78. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  79. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  80. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  81. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  82. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  83. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  84. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  85. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  86. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  87. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  88. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  89. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  90. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
  91. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  92. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
  93. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  94. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  95. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  96. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  97. package/src/runtime/agent/orchestrator/session/loop.mjs +251 -397
  98. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  99. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
  100. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
  101. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  102. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  103. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  104. package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
  105. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  106. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  107. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  108. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  109. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +32 -41
  110. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  111. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  113. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
  115. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  116. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
  118. package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
  119. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
  120. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  121. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
  122. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  123. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  124. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  125. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  126. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  127. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  128. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  129. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  130. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  131. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  132. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  133. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  134. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
  135. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
  136. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  137. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  138. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  139. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  140. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  141. package/src/runtime/channels/backends/discord.mjs +12 -292
  142. package/src/runtime/channels/index.mjs +229 -663
  143. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  144. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  145. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  146. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  147. package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
  148. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  149. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  150. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  151. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  152. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  153. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  154. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  155. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  156. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  157. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  158. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  159. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  160. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  161. package/src/runtime/channels/lib/webhook.mjs +36 -570
  162. package/src/runtime/channels/tool-defs.mjs +11 -130
  163. package/src/runtime/memory/index.mjs +201 -1948
  164. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  165. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  166. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  167. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  168. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  169. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  170. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  171. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  172. package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
  173. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  174. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  175. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  176. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  177. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  178. package/src/runtime/memory/lib/recall-format.mjs +55 -0
  179. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  180. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  181. package/src/runtime/memory/tool-defs.mjs +5 -13
  182. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  183. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  184. package/src/runtime/search/lib/web-tools.mjs +24 -602
  185. package/src/runtime/shared/atomic-file.mjs +26 -1
  186. package/src/runtime/shared/launcher-control.mjs +2 -2
  187. package/src/runtime/shared/tool-primitives.mjs +308 -0
  188. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  189. package/src/runtime/shared/tool-surface.mjs +80 -898
  190. package/src/runtime/shared/transcript-writer.mjs +23 -0
  191. package/src/runtime/shared/update-checker.mjs +7 -4
  192. package/src/session-runtime/config-helpers.mjs +84 -2
  193. package/src/session-runtime/config-lifecycle.mjs +232 -0
  194. package/src/session-runtime/cwd-plugins.mjs +226 -0
  195. package/src/session-runtime/mcp-glue.mjs +177 -0
  196. package/src/session-runtime/model-recency.mjs +111 -0
  197. package/src/session-runtime/native-search.mjs +247 -0
  198. package/src/session-runtime/output-styles.mjs +11 -9
  199. package/src/session-runtime/prewarm.mjs +142 -0
  200. package/src/session-runtime/provider-models.mjs +278 -0
  201. package/src/session-runtime/provider-usage.mjs +120 -0
  202. package/src/session-runtime/quick-model-rows.mjs +170 -0
  203. package/src/session-runtime/quick-search-models.mjs +46 -0
  204. package/src/session-runtime/session-hooks.mjs +93 -0
  205. package/src/session-runtime/settings-api.mjs +319 -0
  206. package/src/session-runtime/tool-catalog.mjs +29 -29
  207. package/src/session-runtime/tool-defs.mjs +84 -0
  208. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  209. package/src/standalone/agent-tool/helpers.mjs +237 -0
  210. package/src/standalone/agent-tool/notify.mjs +107 -0
  211. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  212. package/src/standalone/agent-tool/render.mjs +152 -0
  213. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  214. package/src/standalone/agent-tool.mjs +110 -671
  215. package/src/standalone/channel-worker.mjs +4 -7
  216. package/src/standalone/explore-tool.mjs +30 -9
  217. package/src/standalone/hook-bus/config.mjs +207 -0
  218. package/src/standalone/hook-bus/constants.mjs +90 -0
  219. package/src/standalone/hook-bus/handlers.mjs +481 -0
  220. package/src/standalone/hook-bus/payload.mjs +31 -0
  221. package/src/standalone/hook-bus/rules.mjs +77 -0
  222. package/src/standalone/hook-bus.mjs +77 -870
  223. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  224. package/src/standalone/opencode-go-login.mjs +5 -1
  225. package/src/standalone/provider-admin.mjs +1 -16
  226. package/src/standalone/usage-dashboard.mjs +3 -1
  227. package/src/tui/App.jsx +945 -8094
  228. package/src/tui/app/app-format.mjs +206 -0
  229. package/src/tui/app/channel-pickers.mjs +510 -0
  230. package/src/tui/app/clipboard.mjs +67 -0
  231. package/src/tui/app/core-memory-picker.mjs +210 -0
  232. package/src/tui/app/extension-pickers.mjs +506 -0
  233. package/src/tui/app/input-parsers.mjs +193 -0
  234. package/src/tui/app/maintenance-pickers.mjs +324 -0
  235. package/src/tui/app/model-options.mjs +330 -0
  236. package/src/tui/app/model-picker.mjs +365 -0
  237. package/src/tui/app/onboarding-steps.mjs +400 -0
  238. package/src/tui/app/project-picker.mjs +247 -0
  239. package/src/tui/app/provider-setup-picker.mjs +580 -0
  240. package/src/tui/app/resume-picker.mjs +55 -0
  241. package/src/tui/app/route-pickers.mjs +419 -0
  242. package/src/tui/app/settings-picker.mjs +490 -0
  243. package/src/tui/app/slash-commands.mjs +101 -0
  244. package/src/tui/app/slash-dispatch.mjs +427 -0
  245. package/src/tui/app/text-layout.mjs +46 -0
  246. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  247. package/src/tui/app/transcript-window.mjs +671 -0
  248. package/src/tui/app/use-mouse-input.mjs +460 -0
  249. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  250. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  251. package/src/tui/app/use-transcript-window.mjs +589 -0
  252. package/src/tui/components/ConfirmBar.jsx +1 -1
  253. package/src/tui/components/Picker.jsx +32 -4
  254. package/src/tui/components/PromptInput.jsx +23 -101
  255. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  256. package/src/tui/components/StatusLine.jsx +63 -12
  257. package/src/tui/components/TextEntryPanel.jsx +11 -0
  258. package/src/tui/components/ToolExecution.jsx +52 -594
  259. package/src/tui/components/TranscriptItem.jsx +105 -0
  260. package/src/tui/components/UsagePanel.jsx +18 -4
  261. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  262. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  263. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  264. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  265. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  266. package/src/tui/display-width.mjs +20 -3
  267. package/src/tui/dist/index.mjs +19652 -18630
  268. package/src/tui/engine/agent-job-feed.mjs +133 -0
  269. package/src/tui/engine/notification-plan.mjs +76 -0
  270. package/src/tui/engine/render-timing.mjs +17 -0
  271. package/src/tui/engine/tool-approval.mjs +94 -0
  272. package/src/tui/engine/tool-card-results.mjs +234 -0
  273. package/src/tui/engine/tool-result-status.mjs +135 -0
  274. package/src/tui/engine.mjs +122 -562
  275. package/src/tui/figures.mjs +5 -0
  276. package/src/tui/index.jsx +105 -0
  277. package/src/tui/input-editing.mjs +2 -2
  278. package/src/tui/markdown/format-token.mjs +4 -1
  279. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  280. package/src/tui/theme.mjs +6 -0
  281. package/src/ui/statusline-agents.mjs +213 -0
  282. package/src/ui/statusline-format.mjs +146 -0
  283. package/src/ui/statusline-segments.mjs +148 -0
  284. package/src/ui/statusline.mjs +67 -501
  285. package/src/ui/tool-card.mjs +0 -1
  286. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  287. package/src/workflows/default/WORKFLOW.md +1 -1
  288. package/src/workflows/sequential/WORKFLOW.md +1 -1
  289. package/vendor/ink/build/display-width.js +19 -3
  290. package/vendor/ink/build/ink.js +103 -6
  291. package/vendor/ink/build/log-update.js +17 -3
  292. package/vendor/ink/build/wrap-text.js +125 -0
  293. package/scripts/_test-folder-dialog.mjs +0 -30
  294. package/scripts/fix-brief-fn.mjs +0 -35
  295. package/scripts/fix-format-tool-surface.mjs +0 -24
  296. package/scripts/fix-tool-exec-visible.mjs +0 -42
  297. package/scripts/patch-agent-brief.mjs +0 -48
  298. package/scripts/patch-app.mjs +0 -21
  299. package/scripts/patch-app2.mjs +0 -18
  300. package/scripts/patch-dist-brief.mjs +0 -96
  301. package/scripts/patch-tool-exec.mjs +0 -70
  302. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  303. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  304. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  305. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  306. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  307. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
  308. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  309. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  310. package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
  311. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  312. package/src/standalone/agent-task-status.test.mjs +0 -76
  313. package/src/tui/components/tool-output-format.test.mjs +0 -399
  314. package/src/tui/display-width.test.mjs +0 -35
  315. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  316. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  317. package/src/tui/input-editing.selection.test.mjs +0 -75
  318. package/src/tui/markdown/format-token.test.mjs +0 -354
  319. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  320. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  321. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  322. package/src/tui/paste-fix.test.mjs +0 -119
  323. package/src/tui/prompt-history-store.test.mjs +0 -52
  324. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  325. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  326. package/src/ui/markdown.test.mjs +0 -70
  327. package/src/ui/statusline-context-label.test.mjs +0 -15
  328. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  329. 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) {