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,142 @@
1
+ // Background prewarm/start schedulers, extracted from
2
+ // mixdog-session-runtime.mjs. Dependency-injected factory: timer handles live
3
+ // in a caller-owned `timers` object (so the facade's clearTimeout teardown
4
+ // still sees them) and all state reads go through supplied accessors. Byte-for-
5
+ // byte identical behavior; only grouping changes.
6
+ import { performance } from 'node:perf_hooks';
7
+
8
+ export function createPrewarmSchedulers({
9
+ timers,
10
+ bootProfile,
11
+ getCurrentCwd,
12
+ isCloseRequested,
13
+ getActiveTurnCount,
14
+ getSessionCreatePromise,
15
+ getSession,
16
+ isRemoteEnabled,
17
+ channelsEnabled,
18
+ getCodeGraphModule,
19
+ createCurrentSession,
20
+ channels,
21
+ envFlag,
22
+ delays,
23
+ flags,
24
+ state,
25
+ }) {
26
+ const { codeGraphPrewarmDelayMs, channelStartDelayMs, sessionPrewarmDelayMs, backgroundBusyRetryMs } = delays;
27
+ const { codeGraphPrewarmEnabled, sessionPrewarmEnabled } = flags;
28
+
29
+ function scheduleCodeGraphPrewarm(delayMs = codeGraphPrewarmDelayMs, reason = 'cwd') {
30
+ if (!codeGraphPrewarmEnabled) {
31
+ bootProfile('code-graph:prewarm-skipped', { reason: 'disabled' });
32
+ return;
33
+ }
34
+ if (isCloseRequested()) return;
35
+ state.codeGraphPrewarmQueuedCwd = getCurrentCwd();
36
+ if (timers.codeGraphPrewarmTimer) return;
37
+ timers.codeGraphPrewarmTimer = setTimeout(() => {
38
+ timers.codeGraphPrewarmTimer = null;
39
+ if (isCloseRequested()) return;
40
+ if (getActiveTurnCount() > 0 || getSessionCreatePromise()) {
41
+ bootProfile('code-graph:prewarm-deferred', { reason: getActiveTurnCount() > 0 ? 'turn-active' : 'session-create' });
42
+ scheduleCodeGraphPrewarm(backgroundBusyRetryMs, 'busy');
43
+ return;
44
+ }
45
+ if (state.codeGraphPrewarmInFlight) {
46
+ bootProfile('code-graph:prewarm-deferred', { reason: 'in-flight' });
47
+ scheduleCodeGraphPrewarm(backgroundBusyRetryMs, 'in-flight');
48
+ return;
49
+ }
50
+ const prewarmCwd = state.codeGraphPrewarmQueuedCwd || getCurrentCwd();
51
+ state.codeGraphPrewarmQueuedCwd = '';
52
+ state.codeGraphPrewarmInFlight = true;
53
+ const startedAt = performance.now();
54
+ bootProfile('code-graph:prewarm:start', { cwd: prewarmCwd, reason });
55
+ void getCodeGraphModule()
56
+ .then((mod) => {
57
+ if (typeof mod?.prewarmCodeGraphIfProject !== 'function') return false;
58
+ return mod.prewarmCodeGraphIfProject(prewarmCwd);
59
+ })
60
+ .then((scheduled) => bootProfile(scheduled ? 'code-graph:prewarm:scheduled' : 'code-graph:prewarm:no-project', {
61
+ cwd: prewarmCwd,
62
+ ms: (performance.now() - startedAt).toFixed(1),
63
+ }))
64
+ .catch((error) => bootProfile('code-graph:prewarm:failed', {
65
+ cwd: prewarmCwd,
66
+ ms: (performance.now() - startedAt).toFixed(1),
67
+ error: error?.message || String(error),
68
+ }))
69
+ .finally(() => {
70
+ state.codeGraphPrewarmInFlight = false;
71
+ if (state.codeGraphPrewarmQueuedCwd && !isCloseRequested()) {
72
+ scheduleCodeGraphPrewarm(backgroundBusyRetryMs, 'queued');
73
+ }
74
+ });
75
+ }, delayMs);
76
+ timers.codeGraphPrewarmTimer.unref?.();
77
+ }
78
+
79
+ function scheduleLeadSessionPrewarm() {
80
+ if (!sessionPrewarmEnabled) {
81
+ bootProfile('session:prewarm-skipped');
82
+ return;
83
+ }
84
+ const timer = setTimeout(() => {
85
+ if (isCloseRequested() || getSession()?.id || getSessionCreatePromise() || getActiveTurnCount() > 0) return;
86
+ void createCurrentSession('prewarm')
87
+ .then(() => bootProfile('session:prewarm:ready'))
88
+ .catch((error) => bootProfile('session:prewarm:failed', { error: error?.message || String(error) }));
89
+ }, sessionPrewarmDelayMs);
90
+ timer.unref?.();
91
+ }
92
+
93
+ function invokeChannelStart() {
94
+ if (state.channelStartPromise) return state.channelStartPromise;
95
+ const startedAt = performance.now();
96
+ bootProfile('channels:start:begin');
97
+ state.channelStartPromise = channels.start()
98
+ .then(() => bootProfile('channels:start:ready', { ms: (performance.now() - startedAt).toFixed(1) }))
99
+ .catch((error) => bootProfile('channels:start:failed', {
100
+ ms: (performance.now() - startedAt).toFixed(1),
101
+ error: error?.message || String(error),
102
+ }))
103
+ .finally(() => {
104
+ state.channelStartPromise = null;
105
+ });
106
+ return state.channelStartPromise;
107
+ }
108
+
109
+ function scheduleChannelStart(delayMs = channelStartDelayMs) {
110
+ if (envFlag('MIXDOG_DISABLE_CHANNEL_START')) {
111
+ bootProfile('channels:start-skipped');
112
+ return;
113
+ }
114
+ if (!channelsEnabled()) {
115
+ bootProfile('channels:start-disabled');
116
+ return;
117
+ }
118
+ if (timers.channelStartTimer || state.channelStartPromise || isCloseRequested()) return;
119
+ bootProfile('channels:start-scheduled', { delayMs });
120
+ timers.channelStartTimer = setTimeout(() => {
121
+ timers.channelStartTimer = null;
122
+ if (isCloseRequested()) return;
123
+ // A deferred start may straddle a stopRemote(); re-check before booting so
124
+ // a turned-off session neither starts channels nor keeps rescheduling.
125
+ if (!isRemoteEnabled()) return;
126
+ if (getActiveTurnCount() > 0 || getSessionCreatePromise()) {
127
+ bootProfile('channels:start-deferred', { reason: getActiveTurnCount() > 0 ? 'turn-active' : 'session-create' });
128
+ scheduleChannelStart(backgroundBusyRetryMs);
129
+ return;
130
+ }
131
+ void invokeChannelStart();
132
+ }, delayMs);
133
+ timers.channelStartTimer.unref?.();
134
+ }
135
+
136
+ return {
137
+ scheduleCodeGraphPrewarm,
138
+ scheduleLeadSessionPrewarm,
139
+ invokeChannelStart,
140
+ scheduleChannelStart,
141
+ };
142
+ }
@@ -0,0 +1,278 @@
1
+ // Provider/search model catalog + cache glue, extracted from
2
+ // mixdog-session-runtime.mjs. Dependency-injected factory following the
3
+ // createWarmupSchedulers/createNativeSearch pattern: mutable cache state lives
4
+ // in a caller-owned `caches` object (so the facade's invalidateProviderCaches
5
+ // teardown still sees the same references) and all route/config/registry reads
6
+ // go through supplied accessors so live-binding is preserved (no stale
7
+ // snapshot of route/config/searchRoute).
8
+ import { clean } from './session-text.mjs';
9
+ import { effortItemsFor } from './effort.mjs';
10
+ import { fastCapableFor, fastPreferenceFor } from './model-capabilities.mjs';
11
+ import { modelSettingsFor } from './config-helpers.mjs';
12
+ import { isSelectableLlmModel } from './model-recency.mjs';
13
+
14
+ export function createProviderModels({
15
+ caches,
16
+ modelMetaByRoute,
17
+ getRoute,
18
+ getConfig,
19
+ getReg,
20
+ searchCapableFor,
21
+ sortProviderModelsRaw,
22
+ providerModelCacheRowRaw,
23
+ normalizeSearchProviderId,
24
+ isSearchCapableProvider,
25
+ ensureFullConfig,
26
+ ensureProvidersReady,
27
+ bootProfile,
28
+ scheduleProviderModelWarmup,
29
+ // Quick-row helpers wired in after createQuickModelRows resolves.
30
+ quickHelpers,
31
+ }) {
32
+ const config = () => getConfig();
33
+ const route = () => getRoute();
34
+ const reg = () => getReg();
35
+
36
+ function modelMetaKey(providerId, modelId) {
37
+ return `${clean(providerId)}\n${clean(modelId)}`;
38
+ }
39
+
40
+ async function lookupModelMeta(providerId, modelId, { allowFetch = false } = {}) {
41
+ const key = modelMetaKey(providerId, modelId);
42
+ if (modelMetaByRoute.has(key)) return modelMetaByRoute.get(key);
43
+ const providerImpl = reg().getProvider(providerId);
44
+ if (!providerImpl || typeof providerImpl.listModels !== 'function') {
45
+ const fallback = { id: modelId, provider: providerId };
46
+ modelMetaByRoute.set(key, fallback);
47
+ return fallback;
48
+ }
49
+ if (typeof providerImpl.getCachedModelInfo === 'function') {
50
+ const cached = providerImpl.getCachedModelInfo(modelId);
51
+ if (cached) {
52
+ const meta = { ...cached, id: cached.id || modelId, provider: providerId };
53
+ modelMetaByRoute.set(key, meta);
54
+ return meta;
55
+ }
56
+ }
57
+ if (!allowFetch) {
58
+ const fallback = { id: modelId, provider: providerId };
59
+ modelMetaByRoute.set(key, fallback);
60
+ scheduleProviderModelWarmup();
61
+ return fallback;
62
+ }
63
+ try {
64
+ const models = await providerImpl.listModels();
65
+ const found = Array.isArray(models) ? models.find((m) => m?.id === modelId) : null;
66
+ const meta = found || { id: modelId, provider: providerId };
67
+ modelMetaByRoute.set(key, meta);
68
+ return meta;
69
+ } catch {
70
+ const fallback = { id: modelId, provider: providerId };
71
+ modelMetaByRoute.set(key, fallback);
72
+ return fallback;
73
+ }
74
+ }
75
+
76
+ function hydrateProviderModelRow(row) {
77
+ const cfg = config();
78
+ return {
79
+ ...row,
80
+ effortOptions: effortItemsFor(row.provider, row, null),
81
+ fastCapable: fastCapableFor(row.provider, row),
82
+ fastPreferred: fastPreferenceFor(cfg, row.provider, row.id),
83
+ savedEffort: modelSettingsFor(cfg, row.provider, row.id).effort || null,
84
+ savedFast: modelSettingsFor(cfg, row.provider, row.id).fast === true,
85
+ };
86
+ }
87
+
88
+ const sortProviderModels = (models) => sortProviderModelsRaw(models, route().provider);
89
+ const providerModelCacheRow = (name, m) => providerModelCacheRowRaw(name, m, searchCapableFor);
90
+
91
+ function providerModelsFromCacheRows(rows) {
92
+ return sortProviderModels((rows || []).map(hydrateProviderModelRow));
93
+ }
94
+
95
+ function enabledSearchProviderConfig() {
96
+ ensureFullConfig();
97
+ const out = {};
98
+ for (const [name, providerConfig] of Object.entries(config().providers || {})) {
99
+ const providerName = normalizeSearchProviderId(name);
100
+ if (!providerConfig?.enabled || !isSearchCapableProvider(providerName)) continue;
101
+ out[providerName] = { ...providerConfig, enabled: true };
102
+ }
103
+ return out;
104
+ }
105
+
106
+ async function loadSearchProviderModelsFresh({ forceRefresh = false } = {}) {
107
+ const searchProviders = enabledSearchProviderConfig();
108
+ const providerNames = Object.keys(searchProviders);
109
+ if (!providerNames.length) return [];
110
+ await ensureProvidersReady(config().providers || {});
111
+ const providerResults = await Promise.all(providerNames.map(async (name) => {
112
+ const provider = reg().getProvider(name);
113
+ if (typeof provider?.listModels !== 'function') return [];
114
+ try {
115
+ let models = null;
116
+ if (forceRefresh && typeof provider._refreshModelCache === 'function') {
117
+ models = await provider._refreshModelCache();
118
+ }
119
+ if (!Array.isArray(models)) {
120
+ models = await provider.listModels();
121
+ }
122
+ if (!Array.isArray(models)) return [];
123
+ const rows = [];
124
+ for (const m of models) {
125
+ if (!m?.id || !isSelectableLlmModel(m)) continue;
126
+ const row = providerModelCacheRow(name, m);
127
+ if (row.supportsWebSearch !== true) continue;
128
+ rows.push({
129
+ ...row,
130
+ provider: normalizeSearchProviderId(row.provider),
131
+ searchCapable: true,
132
+ searchToolType: row.searchToolType || 'web_search',
133
+ });
134
+ modelMetaByRoute.set(modelMetaKey(name, m.id), row);
135
+ }
136
+ return rows;
137
+ } catch {
138
+ // Keep the picker responsive if one search-capable provider has a
139
+ // transient catalog/auth failure.
140
+ return [];
141
+ }
142
+ }));
143
+ const results = [];
144
+ const seen = new Set();
145
+ quickHelpers.addDefaultSearchModel(results, seen);
146
+ for (const row of providerResults.flat()) {
147
+ const key = `${normalizeSearchProviderId(row.provider)}:${row.id}`;
148
+ if (seen.has(key)) continue;
149
+ seen.add(key);
150
+ results.push(row);
151
+ }
152
+ return results;
153
+ }
154
+
155
+ async function loadProviderModelsFresh({ forceRefresh = false } = {}) {
156
+ ensureFullConfig();
157
+ await ensureProvidersReady(config().providers || {});
158
+ const allProviders = [...reg().getAllProviders()];
159
+ const providerResults = await Promise.all(allProviders.map(async ([name, provider]) => {
160
+ if (typeof provider?.listModels !== 'function') return [];
161
+ try {
162
+ let models = null;
163
+ if (forceRefresh && typeof provider._refreshModelCache === 'function') {
164
+ models = await provider._refreshModelCache();
165
+ }
166
+ if (!Array.isArray(models)) {
167
+ models = await provider.listModels();
168
+ }
169
+ if (!Array.isArray(models)) return [];
170
+ const rows = [];
171
+ for (const m of models) {
172
+ if (!m?.id) continue;
173
+ if (!isSelectableLlmModel(m)) continue;
174
+ rows.push(providerModelCacheRow(name, m));
175
+ }
176
+ return rows;
177
+ } catch {
178
+ // Ignore per-provider catalog failures so one bad credential or
179
+ // transient /models error does not hide other authenticated models.
180
+ return [];
181
+ }
182
+ }));
183
+ const results = [];
184
+ const seen = new Set();
185
+ for (const row of providerResults.flat()) {
186
+ const key = `${row.provider}:${row.id}`;
187
+ if (seen.has(key)) continue;
188
+ seen.add(key);
189
+ results.push(row);
190
+ modelMetaByRoute.set(modelMetaKey(row.provider, row.id), row);
191
+ }
192
+ return results;
193
+ }
194
+
195
+ async function collectSearchProviderModels({ force = false } = {}) {
196
+ if (!force && Array.isArray(caches.searchProviderModelsCache.models)) {
197
+ return providerModelsFromCacheRows(quickHelpers.searchRowsWithDefault(caches.searchProviderModelsCache.models));
198
+ }
199
+ if (!force && Array.isArray(caches.providerModelsCache.models)) {
200
+ const rows = quickHelpers.searchRowsWithDefault(quickHelpers.searchModelsFromRows(caches.providerModelsCache.models));
201
+ caches.searchProviderModelsCache = { models: rows, at: Date.now() };
202
+ return providerModelsFromCacheRows(rows);
203
+ }
204
+ if (!force) {
205
+ const rows = quickHelpers.searchRowsWithDefault(quickHelpers.quickSearchProviderModelRows());
206
+ caches.searchProviderModelsCache = { models: rows, at: Date.now() };
207
+ return providerModelsFromCacheRows(rows);
208
+ }
209
+ if (force) {
210
+ const models = await loadSearchProviderModelsFresh({ forceRefresh: true });
211
+ caches.searchProviderModelsCache = { models, at: Date.now() };
212
+ return providerModelsFromCacheRows(models);
213
+ }
214
+ }
215
+
216
+ async function collectProviderModels({ force = false, quick = false } = {}) {
217
+ if (!force && Array.isArray(caches.providerModelsCache.models)) {
218
+ return providerModelsFromCacheRows(caches.providerModelsCache.models);
219
+ }
220
+ if (!force && quick) {
221
+ warmProviderModelCache();
222
+ return quickHelpers.quickProviderModelRows();
223
+ }
224
+ if (force) {
225
+ const seq = ++caches.providerModelsLoadSeq;
226
+ const models = await loadProviderModelsFresh({ forceRefresh: true });
227
+ if (seq === caches.providerModelsLoadSeq) caches.providerModelsCache = { models, at: Date.now() };
228
+ return providerModelsFromCacheRows(models);
229
+ }
230
+ if (!caches.providerModelsPromise) {
231
+ const seq = ++caches.providerModelsLoadSeq;
232
+ caches.providerModelsPromise = loadProviderModelsFresh()
233
+ .then((models) => {
234
+ if (seq === caches.providerModelsLoadSeq) caches.providerModelsCache = { models, at: Date.now() };
235
+ return models;
236
+ })
237
+ .finally(() => {
238
+ caches.providerModelsPromise = null;
239
+ });
240
+ }
241
+ return providerModelsFromCacheRows(await caches.providerModelsPromise);
242
+ }
243
+
244
+ function warmProviderModelCache() {
245
+ if (Array.isArray(caches.providerModelsCache.models) || caches.providerModelsPromise) return caches.providerModelsPromise;
246
+ const seq = ++caches.providerModelsLoadSeq;
247
+ caches.providerModelsPromise = loadProviderModelsFresh()
248
+ .then((models) => {
249
+ if (seq === caches.providerModelsLoadSeq) caches.providerModelsCache = { models, at: Date.now() };
250
+ bootProfile('provider-models:warm-ready', { count: models.length });
251
+ return models;
252
+ })
253
+ .catch((err) => {
254
+ const msg = err instanceof Error ? err.message : String(err);
255
+ bootProfile('provider-models:warm-failed', { error: msg });
256
+ return [];
257
+ })
258
+ .finally(() => {
259
+ caches.providerModelsPromise = null;
260
+ });
261
+ return caches.providerModelsPromise;
262
+ }
263
+
264
+ return {
265
+ modelMetaKey,
266
+ lookupModelMeta,
267
+ hydrateProviderModelRow,
268
+ sortProviderModels,
269
+ providerModelCacheRow,
270
+ providerModelsFromCacheRows,
271
+ enabledSearchProviderConfig,
272
+ loadSearchProviderModelsFresh,
273
+ loadProviderModelsFresh,
274
+ collectSearchProviderModels,
275
+ collectProviderModels,
276
+ warmProviderModelCache,
277
+ };
278
+ }
@@ -0,0 +1,120 @@
1
+ // Provider-setup snapshot + usage-dashboard cache glue, extracted from
2
+ // mixdog-session-runtime.mjs. Dependency-injected factory following the same
3
+ // pattern as createProviderModels: mutable cache state lives in a caller-owned
4
+ // `caches` object (so invalidateProviderCaches still resets the same
5
+ // references) and all config/registry reads flow through supplied accessors so
6
+ // live-binding is preserved.
7
+ import { clean } from './session-text.mjs';
8
+
9
+ export function createProviderUsage({
10
+ caches,
11
+ getConfig,
12
+ getReg,
13
+ displayConfig,
14
+ providerSetup,
15
+ createUsageDashboard,
16
+ fetchOAuthUsageSnapshot,
17
+ isCloseRequested,
18
+ getProviderSetupWarmupTimer,
19
+ scheduleProviderSetupWarmup,
20
+ }) {
21
+ const reg = () => getReg();
22
+
23
+ function refreshStatuslineUsageSnapshot(routeLike = {}) {
24
+ const providerId = clean(routeLike.provider);
25
+ const modelId = clean(routeLike.model);
26
+ if (!providerId || !providerId.includes('oauth')) return;
27
+ const providerObj = reg().getProvider(providerId);
28
+ if (!providerObj) return;
29
+ void fetchOAuthUsageSnapshot({ provider: providerId, model: modelId }, providerObj, (message) => {
30
+ if (process.env.MIXDOG_STATUSLINE_TRACE) {
31
+ try { process.stderr.write(`[statusline] ${message}\n`); } catch {}
32
+ }
33
+ }).catch(() => {});
34
+ }
35
+
36
+ async function cachedProviderSetup({ force = false, quick = false } = {}) {
37
+ if (!force && caches.providerSetupCache.setup) {
38
+ return caches.providerSetupCache.setup;
39
+ }
40
+ if (quick) {
41
+ if (!force && caches.providerSetupQuickCache.setup) {
42
+ return caches.providerSetupQuickCache.setup;
43
+ }
44
+ const setup = await providerSetup(displayConfig(), { detectLocal: false, checkSecrets: false });
45
+ caches.providerSetupQuickCache = { setup, at: Date.now() };
46
+ if (!caches.providerSetupPromise && !getProviderSetupWarmupTimer() && !isCloseRequested()) {
47
+ scheduleProviderSetupWarmup(0);
48
+ }
49
+ return setup;
50
+ }
51
+ if (caches.providerSetupPromise) return await caches.providerSetupPromise;
52
+ caches.providerSetupPromise = providerSetup(displayConfig(), { detectLocal: true })
53
+ .then((setup) => {
54
+ caches.providerSetupCache = { setup, at: Date.now() };
55
+ return setup;
56
+ })
57
+ .finally(() => {
58
+ caches.providerSetupPromise = null;
59
+ });
60
+ return await caches.providerSetupPromise;
61
+ }
62
+
63
+ async function getUsageDashboard(options = {}) {
64
+ const forceSetup = options?.force === true || options?.refresh === true;
65
+ if (!forceSetup && caches.usageDashboardCache.dashboard) {
66
+ const cached = {
67
+ ...caches.usageDashboardCache.dashboard,
68
+ refresh: false,
69
+ checking: false,
70
+ cached: true,
71
+ cachedAt: caches.usageDashboardCache.at,
72
+ };
73
+ if (typeof options?.onUpdate === 'function') {
74
+ try { options.onUpdate(cached); } catch {}
75
+ }
76
+ return cached;
77
+ }
78
+ if (!forceSetup && caches.usageDashboardPromise) return await caches.usageDashboardPromise;
79
+ const quickSetup = options?.quickSetup !== false;
80
+ const getProvider = (providerId) => reg().getProvider(providerId);
81
+ const log = (message) => {
82
+ if (process.env.MIXDOG_USAGE_TRACE) {
83
+ try { process.stderr.write(`[usage] ${message}\n`); } catch {}
84
+ }
85
+ };
86
+ if (quickSetup && typeof options?.onUpdate === 'function') {
87
+ const previewConfig = displayConfig();
88
+ const previewSetup = await cachedProviderSetup({ force: false, quick: true });
89
+ await createUsageDashboard(previewConfig, {
90
+ ...(options || {}),
91
+ preview: true,
92
+ setup: previewSetup,
93
+ getProvider,
94
+ log,
95
+ });
96
+ }
97
+ const buildDashboard = async () => {
98
+ const dashboard = await createUsageDashboard(displayConfig(), {
99
+ ...(options || {}),
100
+ setup: await cachedProviderSetup({ force: forceSetup, quick: false }),
101
+ getProvider,
102
+ log,
103
+ });
104
+ caches.usageDashboardCache = { dashboard, at: Date.now() };
105
+ return dashboard;
106
+ };
107
+ if (forceSetup) return await buildDashboard();
108
+ caches.usageDashboardPromise = buildDashboard()
109
+ .finally(() => {
110
+ caches.usageDashboardPromise = null;
111
+ });
112
+ return await caches.usageDashboardPromise;
113
+ }
114
+
115
+ return {
116
+ refreshStatuslineUsageSnapshot,
117
+ cachedProviderSetup,
118
+ getUsageDashboard,
119
+ };
120
+ }