mixdog 0.9.1 → 0.9.3

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 (301) hide show
  1. package/package.json +9 -1
  2. package/scripts/_bench-cwc.json +20 -0
  3. package/scripts/agent-loop-policy-test.mjs +37 -0
  4. package/scripts/agent-parallel-smoke.mjs +54 -10
  5. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  6. package/scripts/background-task-meta-smoke.mjs +1 -1
  7. package/scripts/bench-run.mjs +262 -0
  8. package/scripts/build-tui.mjs +13 -1
  9. package/scripts/compact-smoke.mjs +12 -0
  10. package/scripts/compact-trigger-migration-smoke.mjs +67 -1
  11. package/scripts/explore-bench.mjs +124 -0
  12. package/scripts/hook-bus-test.mjs +191 -0
  13. package/scripts/ingest-pure-conversation-smoke.mjs +148 -0
  14. package/scripts/internal-comms-bench.mjs +727 -0
  15. package/scripts/internal-comms-smoke.mjs +75 -0
  16. package/scripts/lead-workflow-smoke.mjs +4 -4
  17. package/scripts/live-worker-smoke.mjs +9 -9
  18. package/scripts/output-style-bench.mjs +285 -0
  19. package/scripts/output-style-smoke.mjs +13 -10
  20. package/scripts/patch-replay.mjs +90 -0
  21. package/scripts/path-suffix-test.mjs +57 -0
  22. package/scripts/provider-stream-stall-test.mjs +276 -0
  23. package/scripts/provider-toolcall-test.mjs +599 -1
  24. package/scripts/recall-bench.mjs +207 -0
  25. package/scripts/routing-corpus.mjs +281 -0
  26. package/scripts/session-bench.mjs +1526 -0
  27. package/scripts/session-diag.mjs +595 -0
  28. package/scripts/task-bench.mjs +207 -0
  29. package/scripts/tool-failures.mjs +6 -6
  30. package/scripts/tool-smoke.mjs +310 -67
  31. package/scripts/toolcall-args-test.mjs +81 -0
  32. package/src/agents/debugger/AGENT.md +4 -4
  33. package/src/agents/heavy-worker/AGENT.md +20 -9
  34. package/src/agents/reviewer/AGENT.md +4 -4
  35. package/src/agents/worker/AGENT.md +17 -9
  36. package/src/app.mjs +10 -6
  37. package/src/defaults/{hidden-roles.json → agents.json} +7 -7
  38. package/src/examples/schedules/SCHEDULE.example.md +32 -0
  39. package/src/examples/webhooks/WEBHOOK.example.md +40 -0
  40. package/src/headless-role.mjs +14 -14
  41. package/src/help.mjs +1 -0
  42. package/src/lib/rules-builder.cjs +32 -54
  43. package/src/mixdog-session-runtime.mjs +1040 -2036
  44. package/src/output-styles/default.md +12 -7
  45. package/src/output-styles/minimal.md +25 -0
  46. package/src/output-styles/oneline.md +21 -0
  47. package/src/output-styles/simple.md +10 -9
  48. package/src/repl.mjs +17 -7
  49. package/src/rules/agent/00-common.md +7 -5
  50. package/src/rules/agent/30-explorer.md +8 -12
  51. package/src/rules/lead/01-general.md +3 -1
  52. package/src/rules/lead/lead-tool.md +13 -2
  53. package/src/rules/shared/01-tool.md +23 -12
  54. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +90 -32
  55. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +32 -0
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +18 -6
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +23 -20
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +48 -14
  59. package/src/runtime/agent/orchestrator/agent-trace.mjs +87 -12
  60. package/src/runtime/agent/orchestrator/config.mjs +3 -0
  61. package/src/runtime/agent/orchestrator/context/collect.mjs +182 -67
  62. package/src/runtime/agent/orchestrator/{internal-roles.mjs → internal-agents.mjs} +72 -72
  63. package/src/runtime/agent/orchestrator/internal-tools.mjs +13 -26
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +100 -18
  65. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -0
  66. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +188 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +444 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +313 -84
  70. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +104 -38
  71. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +28 -33
  72. package/src/runtime/agent/orchestrator/providers/gemini.mjs +184 -17
  73. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +8 -1
  74. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  75. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +18 -8
  76. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +227 -31
  78. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +83 -6
  79. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +229 -115
  80. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +213 -33
  81. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +18 -0
  82. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +49 -17
  83. package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -1
  84. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +22 -17
  85. package/src/runtime/agent/orchestrator/session/compact.mjs +560 -51
  86. package/src/runtime/agent/orchestrator/session/context-utils.mjs +250 -3
  87. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  88. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +262 -0
  89. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  90. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  91. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  92. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +49 -0
  93. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  94. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  95. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  96. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  97. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  98. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  99. package/src/runtime/agent/orchestrator/session/loop.mjs +513 -1000
  100. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +227 -0
  101. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  102. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +137 -0
  103. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  104. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  105. package/src/runtime/agent/orchestrator/session/manager.mjs +232 -1152
  106. package/src/runtime/agent/orchestrator/session/store.mjs +4 -4
  107. package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +61 -0
  108. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +5 -0
  109. package/src/runtime/agent/orchestrator/stall-policy.mjs +63 -15
  110. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +194 -24
  111. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +143 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +34 -18
  113. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +162 -0
  115. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  116. package/src/runtime/agent/orchestrator/tools/builtin/list-formatting.mjs +10 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +5 -4
  118. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  119. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +15 -0
  120. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +9 -44
  121. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -1
  122. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +14 -5
  123. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  124. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +10 -17
  125. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +23 -2
  126. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +50 -39
  127. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +3 -2
  128. package/src/runtime/agent/orchestrator/tools/builtin/tool-output-limit.mjs +10 -0
  129. package/src/runtime/agent/orchestrator/tools/builtin.mjs +70 -1
  130. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  131. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  132. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  133. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +497 -0
  134. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  135. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  136. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  137. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  138. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  139. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1192 -0
  140. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  141. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  142. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  143. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  144. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +5 -5
  145. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +39 -4189
  146. package/src/runtime/agent/orchestrator/tools/patch.mjs +119 -5
  147. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -2
  148. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +1 -2
  149. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  150. package/src/runtime/channels/backends/discord.mjs +99 -9
  151. package/src/runtime/channels/backends/telegram.mjs +501 -0
  152. package/src/runtime/channels/index.mjs +437 -1439
  153. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  154. package/src/runtime/channels/lib/config.mjs +54 -2
  155. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  156. package/src/runtime/channels/lib/format.mjs +4 -2
  157. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  158. package/src/runtime/channels/lib/output-forwarder.mjs +88 -67
  159. package/src/runtime/channels/lib/runtime-paths.mjs +29 -0
  160. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  161. package/src/runtime/channels/lib/telegram-format.mjs +280 -0
  162. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  163. package/src/runtime/channels/lib/transcript-discovery.mjs +19 -1
  164. package/src/runtime/channels/lib/webhook.mjs +59 -31
  165. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  166. package/src/runtime/channels/tool-defs.mjs +1 -1
  167. package/src/runtime/memory/index.mjs +465 -345
  168. package/src/runtime/memory/lib/agent-ipc.mjs +2 -2
  169. package/src/runtime/memory/lib/core-memory-store.mjs +352 -2
  170. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  171. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  172. package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
  173. package/src/runtime/memory/lib/memory-cycle2.mjs +65 -9
  174. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
  175. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  176. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  177. package/src/runtime/memory/lib/memory.mjs +121 -4
  178. package/src/runtime/memory/lib/pg/adapter.mjs +139 -15
  179. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  180. package/src/runtime/memory/lib/recall-format.mjs +183 -0
  181. package/src/runtime/memory/lib/session-ingest.mjs +107 -0
  182. package/src/runtime/memory/lib/trace-store.mjs +69 -22
  183. package/src/runtime/memory/tool-defs.mjs +10 -7
  184. package/src/runtime/shared/abort-controller.mjs +1 -1
  185. package/src/runtime/shared/background-tasks.mjs +2 -3
  186. package/src/runtime/shared/buffered-appender.mjs +149 -0
  187. package/src/runtime/shared/channel-notification-routing.mjs +12 -0
  188. package/src/runtime/shared/channel-notification-routing.test.mjs +45 -0
  189. package/src/runtime/shared/config.mjs +9 -0
  190. package/src/runtime/shared/llm/http-agent.mjs +12 -5
  191. package/src/runtime/shared/schedules-store.mjs +21 -19
  192. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  193. package/src/runtime/shared/task-notification-envelope.test.mjs +107 -0
  194. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  195. package/src/runtime/shared/tool-surface.mjs +98 -13
  196. package/src/runtime/shared/transcript-writer.mjs +156 -0
  197. package/src/runtime/shared/update-checker.mjs +214 -0
  198. package/src/session-runtime/config-helpers.mjs +209 -0
  199. package/src/session-runtime/effort.mjs +128 -0
  200. package/src/session-runtime/fs-utils.mjs +10 -0
  201. package/src/session-runtime/model-capabilities.mjs +130 -0
  202. package/src/session-runtime/output-styles.mjs +124 -0
  203. package/src/session-runtime/plugin-mcp.mjs +114 -0
  204. package/src/session-runtime/session-text.mjs +100 -0
  205. package/src/session-runtime/statusline-route.mjs +35 -0
  206. package/src/session-runtime/tool-catalog.mjs +720 -0
  207. package/src/session-runtime/workflow.mjs +358 -0
  208. package/src/standalone/agent-tool.mjs +302 -117
  209. package/src/standalone/channel-admin.mjs +133 -40
  210. package/src/standalone/channel-worker.mjs +10 -292
  211. package/src/standalone/explore-tool.mjs +12 -5
  212. package/src/standalone/hook-bus.mjs +165 -8
  213. package/src/standalone/memory-runtime-proxy.mjs +3 -1
  214. package/src/standalone/opencode-go-login.mjs +121 -0
  215. package/src/standalone/provider-admin.mjs +25 -3
  216. package/src/standalone/usage-dashboard.mjs +1 -1
  217. package/src/tui/App.jsx +2883 -778
  218. package/src/tui/components/ConfirmBar.jsx +47 -0
  219. package/src/tui/components/ContextPanel.jsx +5 -3
  220. package/src/tui/components/ItemRightHintOverprint.jsx +54 -0
  221. package/src/tui/components/Markdown.jsx +22 -98
  222. package/src/tui/components/Message.jsx +14 -35
  223. package/src/tui/components/Picker.jsx +87 -12
  224. package/src/tui/components/PromptInput.jsx +355 -22
  225. package/src/tui/components/QueuedCommands.jsx +1 -1
  226. package/src/tui/components/SlashCommandPalette.jsx +8 -5
  227. package/src/tui/components/Spinner.jsx +7 -7
  228. package/src/tui/components/StatusLine.jsx +40 -21
  229. package/src/tui/components/TextEntryPanel.jsx +51 -7
  230. package/src/tui/components/ToolExecution.jsx +183 -101
  231. package/src/tui/components/TurnDone.jsx +4 -4
  232. package/src/tui/components/UsagePanel.jsx +1 -1
  233. package/src/tui/components/tool-output-format.mjs +161 -23
  234. package/src/tui/components/tool-output-format.test.mjs +87 -0
  235. package/src/tui/display-width.mjs +69 -0
  236. package/src/tui/display-width.test.mjs +35 -0
  237. package/src/tui/dist/index.mjs +6731 -2333
  238. package/src/tui/engine/agent-envelope.mjs +296 -0
  239. package/src/tui/engine/boot-profile.mjs +21 -0
  240. package/src/tui/engine/labels.mjs +67 -0
  241. package/src/tui/engine/notice-text.mjs +112 -0
  242. package/src/tui/engine/queue-helpers.mjs +161 -0
  243. package/src/tui/engine/session-stats.mjs +46 -0
  244. package/src/tui/engine/tool-call-fields.mjs +23 -0
  245. package/src/tui/engine/tool-result-text.mjs +126 -0
  246. package/src/tui/engine.mjs +569 -948
  247. package/src/tui/index.jsx +117 -7
  248. package/src/tui/input-editing.mjs +58 -8
  249. package/src/tui/input-editing.selection.test.mjs +75 -0
  250. package/src/tui/keyboard-protocol.mjs +42 -0
  251. package/src/tui/lib/voice-recorder.mjs +469 -0
  252. package/src/tui/markdown/format-token.mjs +128 -76
  253. package/src/tui/markdown/format-token.test.mjs +61 -19
  254. package/src/tui/markdown/measure-rendered-rows.mjs +85 -0
  255. package/src/tui/markdown/render-ansi.test.mjs +1 -1
  256. package/src/tui/markdown/streaming-markdown.mjs +167 -0
  257. package/src/tui/markdown/streaming-markdown.test.mjs +70 -0
  258. package/src/tui/markdown/table-layout.mjs +9 -9
  259. package/src/tui/paste-attachments.mjs +36 -9
  260. package/src/tui/paste-fix.test.mjs +119 -0
  261. package/src/tui/prompt-history-store.mjs +129 -0
  262. package/src/tui/prompt-history-store.test.mjs +52 -0
  263. package/src/tui/statusline-ansi-bridge.test.mjs +3 -3
  264. package/src/tui/theme.mjs +41 -657
  265. package/src/tui/themes/base.mjs +86 -0
  266. package/src/tui/themes/basic.mjs +85 -0
  267. package/src/tui/themes/catppuccin.mjs +72 -0
  268. package/src/tui/themes/dracula.mjs +70 -0
  269. package/src/tui/themes/everforest.mjs +71 -0
  270. package/src/tui/themes/gruvbox.mjs +71 -0
  271. package/src/tui/themes/index.mjs +71 -0
  272. package/src/tui/themes/indigo.mjs +78 -0
  273. package/src/tui/themes/kanagawa.mjs +80 -0
  274. package/src/tui/themes/light.mjs +81 -0
  275. package/src/tui/themes/nord.mjs +72 -0
  276. package/src/tui/themes/onedark.mjs +16 -0
  277. package/src/tui/themes/rosepine.mjs +70 -0
  278. package/src/tui/themes/teal.mjs +80 -0
  279. package/src/tui/themes/tokyonight.mjs +79 -0
  280. package/src/tui/themes/utils.mjs +106 -0
  281. package/src/tui/themes/warm.mjs +79 -0
  282. package/src/tui/transcript-tool-failures.mjs +13 -2
  283. package/src/ui/markdown.mjs +1 -1
  284. package/src/ui/model-display.mjs +2 -2
  285. package/src/ui/statusline.mjs +75 -27
  286. package/src/vendor/statusline/bin/statusline-route.mjs +5 -12
  287. package/src/vendor/statusline/src/gateway/claude-current.mjs +3 -3
  288. package/src/vendor/statusline/src/gateway/route-meta.mjs +30 -16
  289. package/src/workflows/default/WORKFLOW.md +46 -12
  290. package/src/workflows/sequential/WORKFLOW.md +51 -0
  291. package/src/workflows/solo/WORKFLOW.md +12 -1
  292. package/vendor/ink/build/display-width.js +62 -0
  293. package/vendor/ink/build/ink.js +100 -12
  294. package/vendor/ink/build/measure-text.js +4 -1
  295. package/vendor/ink/build/output.js +115 -9
  296. package/vendor/ink/build/render-node-to-output.js +4 -1
  297. package/vendor/ink/build/render.js +4 -0
  298. package/src/output-styles/extreme-simple.md +0 -20
  299. package/src/rules/lead/04-workflow.md +0 -51
  300. package/src/workflows/default/workflow.json +0 -13
  301. package/src/workflows/solo/workflow.json +0 -7
@@ -113,6 +113,9 @@ export function normalizeGrokModelId(id) {
113
113
  return (id && RETIRED_MODEL_ALIASES[id]) || id;
114
114
  }
115
115
  const MODEL_CACHE_TTL_MS = 24 * 60 * 60_000;
116
+ // Bump when the on-disk cache shape changes so stale-shape entries are
117
+ // discarded instead of misread.
118
+ const GROK_MODEL_CACHE_SCHEMA_VERSION = 1;
116
119
  const DISCOVERY_TIMEOUT_MS = 15_000;
117
120
  const TOKEN_TIMEOUT_MS = 30_000;
118
121
  const LOGIN_TIMEOUT_MS = 5 * 60_000;
@@ -362,7 +365,11 @@ async function refreshTokens(tokens) {
362
365
  }
363
366
 
364
367
  // --- Model catalog cache (24h disk TTL) ---
365
- const _modelCache = makeModelCache({ fileName: 'grok-oauth-models.json', ttlMs: MODEL_CACHE_TTL_MS });
368
+ const _modelCache = makeModelCache({
369
+ fileName: 'grok-oauth-models.json',
370
+ ttlMs: MODEL_CACHE_TTL_MS,
371
+ version: GROK_MODEL_CACHE_SCHEMA_VERSION,
372
+ });
366
373
  const PROXY_MODEL_METADATA = {
367
374
  'grok-build': { display: 'Grok Build', contextWindow: 512000 },
368
375
  'grok-composer-2.5-fast': { display: 'Composer 2.5 Fast', contextWindow: 200000 },
@@ -0,0 +1,32 @@
1
+ // Shared numeric/string primitives for provider usage-accounting modules.
2
+ //
3
+ // These are byte-for-byte identical helpers that were duplicated across
4
+ // api-usage.mjs, oauth-usage.mjs and opencode-go-usage.mjs. Extracting them
5
+ // here is behavior-preserving: each exported function reproduces the exact
6
+ // logic of the copies it replaces.
7
+ //
8
+ // NOTE ON `num`: api-usage.mjs and oauth-usage.mjs use the guarded variant
9
+ // below (treats null/undefined/'' as the fallback before Number()).
10
+ // opencode-go-usage.mjs uses an *unguarded* variant (Number('') === 0), which
11
+ // is observably different, so it keeps its own local `num` and is NOT wired to
12
+ // this module.
13
+
14
+ export function num(value, fallback = null) {
15
+ if (value === null || value === undefined || value === '') return fallback;
16
+ const n = Number(value);
17
+ return Number.isFinite(n) ? n : fallback;
18
+ }
19
+
20
+ export function round(value, digits = 4) {
21
+ const n = Number(value);
22
+ if (!Number.isFinite(n)) return null;
23
+ const scale = 10 ** digits;
24
+ return Math.round(n * scale) / scale;
25
+ }
26
+
27
+ // cleanString (oauth-usage.mjs) and clean (opencode-go-usage.mjs) were
28
+ // identical: trim strings, coerce empties/non-strings to null.
29
+ export function cleanString(value) {
30
+ const s = typeof value === 'string' ? value.trim() : '';
31
+ return s || null;
32
+ }
@@ -14,9 +14,10 @@
14
14
  * a stale number.
15
15
  */
16
16
 
17
- import { existsSync, readFileSync, writeFileSync } from 'fs';
17
+ import { existsSync, readFileSync } from 'fs';
18
18
  import { join } from 'path';
19
19
  import { getPluginData } from '../config.mjs';
20
+ import { writeJsonAtomicSync } from '../../../shared/atomic-file.mjs';
20
21
 
21
22
  const CATALOG_URL = 'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json';
22
23
  const CATALOG_CACHE_FILE = 'litellm-catalog.json';
@@ -187,7 +188,7 @@ async function _loadCatalogImpl() {
187
188
  if (!res.ok) throw new Error('HTTP ' + res.status);
188
189
  const data = await res.json();
189
190
  try {
190
- writeFileSync(cachePath(), JSON.stringify({ fetchedAt: Date.now(), data }));
191
+ writeJsonAtomicSync(cachePath(), { fetchedAt: Date.now(), data }, { lock: true, compact: true, fsyncDir: true, timeoutMs: 1000 });
191
192
  } catch { /* cache is best-effort */ }
192
193
  _memCache = data;
193
194
  _memCacheAt = Date.now();
@@ -239,7 +240,7 @@ async function _loadModelsDevImpl() {
239
240
  if (!res.ok) throw new Error('HTTP ' + res.status);
240
241
  const data = await res.json();
241
242
  try {
242
- writeFileSync(mdCachePath(), JSON.stringify({ fetchedAt: Date.now(), data }));
243
+ writeJsonAtomicSync(mdCachePath(), { fetchedAt: Date.now(), data }, { lock: true, compact: true, fsyncDir: true, timeoutMs: 1000 });
243
244
  } catch { /* cache is best-effort */ }
244
245
  _mdCache = data;
245
246
  _mdCacheAt = Date.now();
@@ -430,12 +431,12 @@ export async function enrichModels(models) {
430
431
  outputCostPerM: meta.outputCostPerM,
431
432
  cacheReadCostPerM: meta.cacheReadCostPerM,
432
433
  cacheWriteCostPerM: meta.cacheWriteCostPerM,
433
- supportsVision: meta.supportsVision,
434
- supportsFunctionCalling: meta.supportsFunctionCalling,
434
+ supportsVision: m.supportsVision === true || meta.supportsVision,
435
+ supportsFunctionCalling: m.supportsFunctionCalling === true || meta.supportsFunctionCalling,
435
436
  supportsWebSearch: meta.supportsWebSearch || m.supportsWebSearch === true,
436
- supportsPromptCaching: meta.supportsPromptCaching,
437
- supportsReasoning: meta.supportsReasoning,
438
- reasoningOptions: meta.reasoningOptions || m.reasoningOptions || [],
437
+ supportsPromptCaching: m.supportsPromptCaching === true || meta.supportsPromptCaching,
438
+ supportsReasoning: m.supportsReasoning === true || meta.supportsReasoning,
439
+ reasoningOptions: m.reasoningOptions?.length ? m.reasoningOptions : (meta.reasoningOptions || []),
439
440
  reasoningContentField: meta.reasoningContentField || m.reasoningContentField || null,
440
441
  mode: meta.mode || m.mode || null,
441
442
  };
@@ -472,3 +473,12 @@ export async function warmModelMetadataCatalogs() {
472
473
  const [litellm] = await Promise.all([loadCatalog(), loadModelsDevCatalog()]);
473
474
  return litellm;
474
475
  }
476
+
477
+ /** Fire-and-forget warm of both in-memory catalog caches (disk-first, then remote). */
478
+ export async function warmCatalogsInBackground() {
479
+ try {
480
+ await Promise.all([loadCatalog(), loadModelsDevCatalog()]);
481
+ } catch {
482
+ /* never throw — boot/statusline must not fail on catalog warm */
483
+ }
484
+ }
@@ -6,6 +6,7 @@ import { join } from 'path';
6
6
  import { updateJsonAtomicSync } from '../../../shared/atomic-file.mjs';
7
7
  import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
8
8
  import { getLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
9
+ import { num, round, cleanString } from './lib/usage-primitives.mjs';
9
10
 
10
11
  const CACHE_FILE = 'gateway-oauth-usage-cache.json';
11
12
  const LIVE_CACHE_TTL_MS = 60_000;
@@ -21,24 +22,6 @@ const lastWarnAt = new Map();
21
22
  let pendingDiskSnapshots = new Map();
22
23
  let pendingDiskFlushTimer = null;
23
24
 
24
- function num(value, fallback = null) {
25
- if (value === null || value === undefined || value === '') return fallback;
26
- const n = Number(value);
27
- return Number.isFinite(n) ? n : fallback;
28
- }
29
-
30
- function round(value, digits = 4) {
31
- const n = Number(value);
32
- if (!Number.isFinite(n)) return null;
33
- const scale = 10 ** digits;
34
- return Math.round(n * scale) / scale;
35
- }
36
-
37
- function cleanString(value) {
38
- const s = typeof value === 'string' ? value.trim() : '';
39
- return s || null;
40
- }
41
-
42
25
  function providerKey(routeInfo = {}) {
43
26
  return String(routeInfo?.provider || '').toLowerCase();
44
27
  }
@@ -301,7 +284,7 @@ function balanceFromCredits(credits, source) {
301
284
  }
302
285
 
303
286
  function balanceFromExtraUsage(extra) {
304
- if (!extra || typeof extra !== 'object' || extra.is_enabled !== true) return null;
287
+ if (!extra || typeof extra !== 'object') return null;
305
288
  const limit = num(extra.monthly_limit, null);
306
289
  const used = num(extra.used_credits, 0);
307
290
  if (limit === null) return null;
@@ -315,6 +298,57 @@ function balanceFromExtraUsage(extra) {
315
298
  };
316
299
  }
317
300
 
301
+ function balanceFromAnthropicSpend(spend) {
302
+ if (!spend || typeof spend !== 'object') return null;
303
+ const currency = cleanString(spend.used?.currency ?? spend.limit?.currency) || 'USD';
304
+ // spend.balance may be a plain number or a {amount_minor, exponent} money
305
+ // object like used/limit; support both shapes.
306
+ const directBalance = spend.balance && typeof spend.balance === 'object'
307
+ ? (() => {
308
+ const minor = num(spend.balance.amount_minor, null);
309
+ return minor === null ? null : minor / (10 ** num(spend.balance.exponent, 2));
310
+ })()
311
+ : num(spend.balance, null);
312
+ if (directBalance !== null) {
313
+ return {
314
+ source: 'anthropic-oauth-spend',
315
+ remainingUsd: round(directBalance, 4),
316
+ spentUsd: round(num(spend.used?.amount_minor, 0) / (10 ** num(spend.used?.exponent, 2)), 4),
317
+ currency,
318
+ };
319
+ }
320
+
321
+ const usedMinor = num(spend.used?.amount_minor, null);
322
+ const usedExponent = num(spend.used?.exponent, 2);
323
+ const usedUsd = usedMinor === null ? null : usedMinor / (10 ** usedExponent);
324
+
325
+ const capMinor = num(spend.cap?.credits?.amount_minor, null);
326
+ if (capMinor !== null && usedUsd !== null) {
327
+ const capExponent = num(spend.cap?.credits?.exponent, 2);
328
+ const capUsd = capMinor / (10 ** capExponent);
329
+ return {
330
+ source: 'anthropic-oauth-spend',
331
+ remainingUsd: round(Math.max(0, capUsd - usedUsd), 4),
332
+ spentUsd: round(usedUsd, 4),
333
+ currency,
334
+ };
335
+ }
336
+
337
+ const limitMinor = num(spend.limit?.amount_minor, null);
338
+ if (limitMinor !== null && limitMinor > 0 && usedUsd !== null) {
339
+ const limitExponent = num(spend.limit?.exponent, 2);
340
+ const limitUsd = limitMinor / (10 ** limitExponent);
341
+ return {
342
+ source: 'anthropic-oauth-spend',
343
+ remainingUsd: round(Math.max(0, limitUsd - usedUsd), 4),
344
+ spentUsd: round(usedUsd, 4),
345
+ currency,
346
+ };
347
+ }
348
+
349
+ return null;
350
+ }
351
+
318
352
  function normalizeOpenAIWhamUsage(data) {
319
353
  const rate = data?.rate_limit && typeof data.rate_limit === 'object' ? data.rate_limit : null;
320
354
  if (!rate) return null;
@@ -388,7 +422,7 @@ function normalizeAnthropicUsage(data, source = 'anthropic-oauth') {
388
422
  provider: 'anthropic-oauth',
389
423
  source,
390
424
  quotaWindows: windows,
391
- balance: balanceFromExtraUsage(data.extra_usage),
425
+ balance: balanceFromAnthropicSpend(data.spend) || balanceFromExtraUsage(data.extra_usage),
392
426
  rawKeys: Object.keys(data || {}).sort(),
393
427
  };
394
428
  }