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,365 @@
1
+ /*
2
+ * model-picker.mjs — the Model picker cluster (openModelPicker).
3
+ *
4
+ * Extracted from App.jsx behavior-preservingly as a dependency-injection
5
+ * factory. Every function body is the original App logic verbatim, with closure
6
+ * identifiers threaded through the factory argument. The nested effort helpers
7
+ * (preferredEffort/effortItemsFor/modelDefaultEffort/…) stay inside the opener.
8
+ * Deps pointing at a later-defined App fn (openProviderSetupPicker) thread as a
9
+ * lazy getter wrapper so it resolves the live opener at call time; live UI
10
+ * state (getState) is read through a getter so it always reflects the current
11
+ * render.
12
+ */
13
+ import { theme } from '../theme.mjs';
14
+ import {
15
+ normalizeModelOptions,
16
+ providerDisplayName,
17
+ effortDisplayLabel,
18
+ fastDisplayLabel,
19
+ buildModelProviderItems,
20
+ buildProviderModelItems,
21
+ } from './model-options.mjs';
22
+
23
+ export function createModelPicker({
24
+ store,
25
+ getState,
26
+ setPicker,
27
+ setProviderPrompt,
28
+ setChannelPrompt,
29
+ setHookPrompt,
30
+ setSettingsPrompt,
31
+ providerModelsCacheRef,
32
+ searchModelsCacheRef,
33
+ modelPickerRequestRef,
34
+ clearModelCaches,
35
+ modelSwitchNotice,
36
+ openProviderSetupPicker,
37
+ }) {
38
+ const openModelPicker = async (options = {}) => {
39
+ const state = getState();
40
+ setProviderPrompt(null);
41
+ setChannelPrompt(null);
42
+ setHookPrompt(null);
43
+ setSettingsPrompt(null);
44
+ const modelPickerRequest = ++modelPickerRequestRef.current;
45
+ let modelPickerClosed = false;
46
+ let activeModelProvider = null;
47
+ let providerListHighlightProvider = null;
48
+ const isActiveModelPicker = () => !modelPickerClosed && modelPickerRequestRef.current === modelPickerRequest;
49
+ const returnTo = typeof options.returnTo === 'function' ? options.returnTo : null;
50
+ const returnLabel = String(options.returnLabel || 'Agents');
51
+ const returnOnNestedCancel = options.returnOnNestedCancel === true;
52
+ const cancelModelPicker = () => {
53
+ modelPickerClosed = true;
54
+ if (returnTo) returnTo();
55
+ else setPicker(null);
56
+ };
57
+ const cacheRef = options.cacheRef === 'search' ? searchModelsCacheRef : providerModelsCacheRef;
58
+ const loadModels = typeof options.loadModels === 'function' ? options.loadModels : store.listProviderModels;
59
+ let providerModels = Array.isArray(cacheRef.current.models)
60
+ ? cacheRef.current.models
61
+ : [];
62
+ let refreshModelsPromise = null;
63
+ let renderedQuickModels = false;
64
+ if (!providerModels.length || options.refreshModels === true) {
65
+ setPicker({
66
+ title: options.title || 'Model',
67
+ description: options.loadingDescription || 'Loading models...',
68
+ help: returnTo ? `↑/↓ Select · Enter Open · Esc ${returnLabel}` : '↑/↓ Select · Enter Open · Esc Back',
69
+ items: [],
70
+ onCancel: cancelModelPicker,
71
+ });
72
+ await new Promise((resolve) => setTimeout(resolve, 0));
73
+ try {
74
+ if (options.refreshModels !== true && options.cacheRef !== 'search') {
75
+ refreshModelsPromise = Promise.resolve(loadModels({ force: false }));
76
+ providerModels = await loadModels({ quick: true });
77
+ renderedQuickModels = Array.isArray(providerModels) && providerModels.length > 0;
78
+ if (!renderedQuickModels) {
79
+ providerModels = await refreshModelsPromise;
80
+ }
81
+ } else {
82
+ providerModels = await loadModels({ force: options.refreshModels === true });
83
+ }
84
+ cacheRef.current = { models: providerModels, at: Date.now() };
85
+ } catch (e) {
86
+ store.pushNotice(`could not list models: ${e?.message || e}`, 'error');
87
+ return;
88
+ }
89
+ }
90
+
91
+ if (!providerModels || providerModels.length === 0) {
92
+ store.pushNotice(options.emptyNotice || 'no provider models available; open /providers to sign in', 'warn');
93
+ void openProviderSetupPicker({
94
+ title: 'Providers',
95
+ continueLabel: 'Back to model setup',
96
+ continueDescription: 'retry model list after provider auth',
97
+ onContinue: () => void openModelPicker(options),
98
+ });
99
+ return;
100
+ }
101
+
102
+ let models = normalizeModelOptions(providerModels);
103
+ const activeRoute = options.currentRoute || {
104
+ provider: state.provider,
105
+ model: state.model,
106
+ effort: state.effort,
107
+ fast: state.fast,
108
+ };
109
+ const renderModelPicker = (renderOptions = {}) => {
110
+ if (renderOptions.highlightProvider) {
111
+ providerListHighlightProvider = renderOptions.highlightProvider;
112
+ }
113
+ const highlightProvider = renderOptions.highlightProvider || providerListHighlightProvider || null;
114
+ activeModelProvider = null;
115
+ const openProviderModelsPicker = (provider) => {
116
+ if (!provider) return;
117
+ activeModelProvider = provider;
118
+ const providerModels = models.filter((model) => model.provider === provider);
119
+ const preferredEffort = (values = []) => {
120
+ const allowed = values.filter(Boolean);
121
+ for (const value of ['high', 'medium', 'low', 'none', 'xhigh', 'max']) {
122
+ if (allowed.includes(value)) return value;
123
+ }
124
+ return allowed[0] || null;
125
+ };
126
+ const effortItemsFor = (model) => Array.isArray(model?.effortOptions) && model.effortOptions.length > 0
127
+ ? model.effortOptions
128
+ : [];
129
+ const modelEffortValues = (model) => effortItemsFor(model).map((effort) => effort.value).filter(Boolean);
130
+ const modelDefaultEffort = (model) => {
131
+ const values = modelEffortValues(model);
132
+ if (!values.length) return null;
133
+ const currentRoute = options.currentRoute || null;
134
+ if (currentRoute?.provider === model.provider && currentRoute?.model === model.id && currentRoute.effort && values.includes(currentRoute.effort)) return currentRoute.effort;
135
+ if (model.provider === state.provider && model.id === state.model && state.effort && values.includes(state.effort)) return state.effort;
136
+ if (model.savedEffort && values.includes(model.savedEffort)) return model.savedEffort;
137
+ return preferredEffort(values);
138
+ };
139
+ const selectedEfforts = new Map();
140
+ const modelKey = (model) => `${model?.provider || ''}\n${model?.id || ''}`;
141
+ const getSelectedEffort = (model) => {
142
+ if (!model) return null;
143
+ const key = modelKey(model);
144
+ if (selectedEfforts.has(key)) return selectedEfforts.get(key);
145
+ const effort = modelDefaultEffort(model);
146
+ selectedEfforts.set(key, effort);
147
+ return effort;
148
+ };
149
+ const setSelectedEffort = (model, effort) => {
150
+ if (!model) return;
151
+ selectedEfforts.set(modelKey(model), effort || null);
152
+ };
153
+ const selectedFast = new Map();
154
+ const modelDefaultFast = (model) => {
155
+ if (!model?.fastCapable) return false;
156
+ const currentRoute = options.currentRoute || null;
157
+ if (currentRoute?.provider === model.provider && currentRoute?.model === model.id && typeof currentRoute.fast === 'boolean') return currentRoute.fast;
158
+ if (model.provider === state.provider && model.id === state.model && typeof state.fast === 'boolean') return state.fast;
159
+ if (typeof model.savedFast === 'boolean') return model.savedFast;
160
+ return model.fastPreferred === true;
161
+ };
162
+ const getSelectedFast = (model) => {
163
+ if (!model) return false;
164
+ const key = modelKey(model);
165
+ if (selectedFast.has(key)) return selectedFast.get(key) === true;
166
+ const fast = modelDefaultFast(model);
167
+ selectedFast.set(key, fast);
168
+ return fast;
169
+ };
170
+ const toggleFast = (model) => {
171
+ if (!model?.fastCapable) return;
172
+ selectedFast.set(modelKey(model), !getSelectedFast(model));
173
+ renderProviderModels();
174
+ };
175
+ const providerEffortItems = () => {
176
+ const seen = new Set();
177
+ const out = [];
178
+ for (const effort of providerModels.flatMap((model) => effortItemsFor(model))) {
179
+ if (!effort?.value || seen.has(effort.value)) continue;
180
+ seen.add(effort.value);
181
+ out.push(effort);
182
+ }
183
+ return out;
184
+ };
185
+ const effortLabel = (value) => {
186
+ const found = providerEffortItems().find((effort) => effort.value === value);
187
+ return effortDisplayLabel(found?.label || value || '');
188
+ };
189
+ const effortGlyph = (value) => {
190
+ if (value === 'none') return '○';
191
+ if (value === 'low') return '◔';
192
+ if (value === 'medium') return '◑';
193
+ if (value === 'high') return '◕';
194
+ if (value === 'max') return '◆';
195
+ return '●';
196
+ };
197
+ const effortColor = (value) => {
198
+ if (value === 'none') return theme.inactive;
199
+ if (value === 'low') return theme.warning;
200
+ if (value === 'medium') return theme.claude;
201
+ if (value === 'high') return theme.error;
202
+ if (value === 'max') return theme.permission;
203
+ return theme.error;
204
+ };
205
+ const modelFooter = (model = null) => {
206
+ const items = model ? effortItemsFor(model) : providerEffortItems();
207
+ const values = items.map((effort) => effort.value).filter(Boolean);
208
+ const fastCapable = model?.fastCapable === true;
209
+ const fastOn = fastCapable && getSelectedFast(model);
210
+ const fastLine = fastCapable
211
+ ? { glyph: fastOn ? '●' : '○', color: fastOn ? theme.fastMode : theme.inactive, text: `${fastDisplayLabel(fastOn)} · Tab Toggle` }
212
+ : null;
213
+ if (!values.length) {
214
+ return fastLine ? [fastLine] : '';
215
+ }
216
+ let selectedEffort = getSelectedEffort(model);
217
+ if (!values.includes(selectedEffort)) {
218
+ selectedEffort = modelDefaultEffort(model);
219
+ setSelectedEffort(model, selectedEffort);
220
+ }
221
+ const effortLine = {
222
+ glyph: effortGlyph(selectedEffort),
223
+ color: effortColor(selectedEffort),
224
+ text: `${effortLabel(selectedEffort)} Effort ←/→ To Adjust`,
225
+ };
226
+ return fastLine ? [effortLine, fastLine] : [effortLine];
227
+ };
228
+ const coerceEffort = (model) => {
229
+ const values = modelEffortValues(model);
230
+ if (!values.length) return null;
231
+ const selectedEffort = getSelectedEffort(model);
232
+ return values.includes(selectedEffort) ? selectedEffort : modelDefaultEffort(model);
233
+ };
234
+ const cycleEffort = (model, direction = 1) => {
235
+ const values = modelEffortValues(model);
236
+ if (values.length === 0) return;
237
+ const selectedEffort = getSelectedEffort(model);
238
+ const currentValue = values.includes(selectedEffort) ? selectedEffort : modelDefaultEffort(model);
239
+ const current = values.includes(currentValue) ? values.indexOf(currentValue) : 0;
240
+ setSelectedEffort(model, values[(current + direction + values.length) % values.length] || null);
241
+ renderProviderModels();
242
+ };
243
+ const applyModel = (item) => {
244
+ const selected = item?._model || models.find((m) => m.provider === item?._provider && m.id === item?._modelId);
245
+ if (!selected) return;
246
+ modelPickerClosed = true;
247
+ const effort = coerceEffort(selected);
248
+ const routeInput = {
249
+ provider: selected.provider,
250
+ model: selected.id,
251
+ ...(effort ? { effort } : {}),
252
+ ...(selected.fastCapable ? { fast: getSelectedFast(selected) } : {}),
253
+ };
254
+ if (typeof options.onSelectRoute === 'function') {
255
+ const savePromise = Promise.resolve(options.onSelectRoute(routeInput, selected, effort));
256
+ if (typeof options.onImmediateSelect === 'function') {
257
+ options.onImmediateSelect(routeInput, selected, effort);
258
+ } else {
259
+ setPicker(null);
260
+ }
261
+ void savePromise
262
+ .then((result) => {
263
+ if (result) clearModelCaches('all');
264
+ if (typeof options.onAfterSelect === 'function') options.onAfterSelect();
265
+ return result;
266
+ })
267
+ .catch((e) => store.pushNotice(`Couldn’t save model: ${e?.message || e}`, 'error'));
268
+ return;
269
+ }
270
+ setPicker(null);
271
+ void store.setRoute(routeInput)
272
+ .then((ok) => {
273
+ if (ok) clearModelCaches('provider');
274
+ store.pushNotice(
275
+ ok
276
+ ? modelSwitchNotice()
277
+ : 'Model switch is already running',
278
+ ok ? 'info' : 'warn',
279
+ );
280
+ if (ok && typeof options.onAfterSelect === 'function') options.onAfterSelect();
281
+ })
282
+ .catch((e) => store.pushNotice(`Couldn’t switch model: ${e?.message || e}`, 'error'));
283
+ };
284
+ const renderProviderModels = () => {
285
+ const providerModelItems = buildProviderModelItems(models, provider, activeRoute);
286
+ const providerModelInitialIndex = Math.max(
287
+ 0,
288
+ providerModelItems.findIndex(
289
+ (item) => item._provider === activeRoute?.provider && item._modelId === activeRoute?.model,
290
+ ),
291
+ );
292
+ setPicker({
293
+ title: providerDisplayName(provider),
294
+ description: options.modelDescription || 'Select a model. Adjust Effort with ←/→.',
295
+ footer: (item) => modelFooter(item?._model),
296
+ help: returnOnNestedCancel && returnTo
297
+ ? `↑/↓ Select · ←/→ Effort · Tab Fast · Enter Save · Esc ${returnLabel}`
298
+ : '↑/↓ Select · ←/→ Effort · Tab Fast · Enter Save · Esc Back',
299
+ indexMode: 'always',
300
+ initialIndex: providerModelInitialIndex,
301
+ pickerKey: `model-picker:provider-models:${provider}`,
302
+ items: providerModelItems,
303
+ onSelect: (_value, item) => applyModel(item),
304
+ onLeft: (item) => {
305
+ if (item?._model) cycleEffort(item._model, -1);
306
+ },
307
+ onRight: (item) => {
308
+ if (item?._model) cycleEffort(item._model, 1);
309
+ },
310
+ onTab: (item) => {
311
+ if (item?._model) toggleFast(item._model);
312
+ },
313
+ onCancel: () => {
314
+ if (returnOnNestedCancel && returnTo) cancelModelPicker();
315
+ else renderModelPicker({ highlightProvider: provider });
316
+ },
317
+ });
318
+ };
319
+ renderProviderModels();
320
+ };
321
+ const providerItems = buildModelProviderItems(models, activeRoute);
322
+ const providerHighlight = highlightProvider || activeRoute?.provider || null;
323
+ const providerInitialIndex = Math.max(
324
+ 0,
325
+ providerItems.findIndex((item) => item._provider === providerHighlight),
326
+ );
327
+ setPicker({
328
+ title: options.title || 'Model',
329
+ description: options.providerDescription || 'Choose a provider.',
330
+ help: returnTo ? `↑/↓ Select · Enter Open · Esc ${returnLabel}` : '↑/↓ Select · Enter Open · Esc Back',
331
+ indexMode: 'always',
332
+ labelWidth: 18,
333
+ metaWidth: 20,
334
+ initialIndex: providerInitialIndex,
335
+ pickerKey: `model-picker:providers:${providerHighlight || 'default'}`,
336
+ items: providerItems,
337
+ onSelect: (_value, item) => {
338
+ if (item?._provider) openProviderModelsPicker(item._provider);
339
+ },
340
+ onHighlight: (_value, item) => {
341
+ if (item?._provider) providerListHighlightProvider = item._provider;
342
+ },
343
+ onCancel: cancelModelPicker,
344
+ });
345
+ };
346
+
347
+ renderModelPicker();
348
+ if (renderedQuickModels && refreshModelsPromise) {
349
+ void refreshModelsPromise
350
+ .then((freshModels) => {
351
+ if (!isActiveModelPicker()) return;
352
+ if (!Array.isArray(freshModels) || freshModels.length === 0) return;
353
+ providerModels = freshModels;
354
+ models = normalizeModelOptions(providerModels);
355
+ cacheRef.current = { models: providerModels, at: Date.now() };
356
+ if (activeModelProvider === null) {
357
+ renderModelPicker();
358
+ }
359
+ })
360
+ .catch(() => {});
361
+ }
362
+ };
363
+
364
+ return { openModelPicker };
365
+ }