mixdog 0.8.0 → 0.9.0

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 (285) hide show
  1. package/README.md +47 -23
  2. package/package.json +33 -27
  3. package/scripts/_test-folder-dialog.mjs +30 -0
  4. package/scripts/agent-parallel-smoke.mjs +388 -0
  5. package/scripts/agent-tag-reuse-smoke.mjs +183 -0
  6. package/scripts/background-task-meta-smoke.mjs +38 -0
  7. package/scripts/boot-smoke.mjs +52 -9
  8. package/scripts/build-runtime-linux.sh +348 -0
  9. package/scripts/build-runtime-macos.sh +217 -0
  10. package/scripts/build-runtime-windows.ps1 +242 -0
  11. package/scripts/compact-active-turn-test.mjs +68 -0
  12. package/scripts/compact-smoke.mjs +859 -129
  13. package/scripts/compact-trigger-migration-smoke.mjs +187 -0
  14. package/scripts/fix-brief-fn.mjs +35 -0
  15. package/scripts/fix-format-tool-surface.mjs +24 -0
  16. package/scripts/fix-tool-exec-visible.mjs +42 -0
  17. package/scripts/generate-runtime-manifest.mjs +166 -0
  18. package/scripts/hook-bus-test.mjs +330 -0
  19. package/scripts/lead-workflow-smoke.mjs +33 -39
  20. package/scripts/live-worker-smoke.mjs +43 -37
  21. package/scripts/llm-trace-summary.mjs +315 -0
  22. package/scripts/memory-meta-concurrency-test.mjs +20 -0
  23. package/scripts/output-style-smoke.mjs +56 -15
  24. package/scripts/parent-abort-link-test.mjs +44 -0
  25. package/scripts/patch-agent-brief.mjs +48 -0
  26. package/scripts/patch-app.mjs +21 -0
  27. package/scripts/patch-app2.mjs +18 -0
  28. package/scripts/patch-dist-brief.mjs +96 -0
  29. package/scripts/patch-tool-exec.mjs +70 -0
  30. package/scripts/pretool-ask-runtime-test.mjs +54 -0
  31. package/scripts/provider-toolcall-test.mjs +376 -0
  32. package/scripts/reactive-compact-persist-smoke.mjs +124 -0
  33. package/scripts/sanitize-tool-pairs-test.mjs +260 -0
  34. package/scripts/session-context-bench.mjs +344 -0
  35. package/scripts/session-ingest-smoke.mjs +177 -0
  36. package/scripts/set-effort-config-test.mjs +41 -0
  37. package/scripts/smoke-runtime-negative.ps1 +106 -0
  38. package/scripts/smoke-runtime-negative.sh +97 -0
  39. package/scripts/smoke.mjs +25 -0
  40. package/scripts/tool-result-hook-test.mjs +48 -0
  41. package/scripts/tool-smoke.mjs +1223 -95
  42. package/scripts/toolcall-args-test.mjs +150 -0
  43. package/scripts/tui-background-failure-smoke.mjs +73 -0
  44. package/scripts/usage-metrics-epoch-smoke.mjs +114 -0
  45. package/src/agents/debugger/AGENT.md +8 -0
  46. package/src/agents/explore/AGENT.md +4 -0
  47. package/src/agents/heavy-worker/AGENT.md +9 -3
  48. package/src/agents/maintainer/AGENT.md +4 -0
  49. package/src/agents/reviewer/AGENT.md +8 -0
  50. package/src/agents/scheduler-task/AGENT.md +12 -0
  51. package/src/agents/scheduler-task/agent.json +6 -0
  52. package/src/agents/webhook-handler/AGENT.md +12 -0
  53. package/src/agents/webhook-handler/agent.json +6 -0
  54. package/src/agents/worker/AGENT.md +9 -3
  55. package/src/app.mjs +77 -3
  56. package/src/defaults/hidden-roles.json +17 -12
  57. package/src/headless-role.mjs +117 -0
  58. package/src/help.mjs +30 -0
  59. package/src/hooks/lib/permission-evaluator.cjs +11 -475
  60. package/src/lib/keychain-cjs.cjs +9 -1
  61. package/src/lib/mixdog-debug.cjs +0 -7
  62. package/src/lib/rules-builder.cjs +240 -96
  63. package/src/lib/text-utils.cjs +1 -1
  64. package/src/mixdog-session-runtime.mjs +2325 -450
  65. package/src/output-styles/default.md +12 -28
  66. package/src/output-styles/extreme-simple.md +9 -6
  67. package/src/output-styles/simple.md +22 -9
  68. package/src/repl.mjs +118 -59
  69. package/src/rules/agent/00-common.md +15 -0
  70. package/src/rules/{bridge → agent}/20-skip-protocol.md +1 -2
  71. package/src/rules/agent/30-explorer.md +22 -0
  72. package/src/rules/{bridge → agent}/40-cycle1-agent.md +7 -0
  73. package/src/rules/{bridge → agent}/41-cycle2-agent.md +7 -0
  74. package/src/rules/{bridge → agent}/42-cycle3-agent.md +7 -0
  75. package/src/rules/lead/01-general.md +9 -5
  76. package/src/rules/lead/04-workflow.md +51 -12
  77. package/src/rules/lead/lead-tool.md +6 -0
  78. package/src/rules/shared/01-tool.md +12 -1
  79. package/src/runtime/agent/orchestrator/activity-bus.mjs +7 -18
  80. package/src/runtime/agent/orchestrator/agent-owner.mjs +11 -0
  81. package/src/runtime/agent/orchestrator/{smart-bridge/bridge-llm.mjs → agent-runtime/agent-dispatch.mjs} +138 -111
  82. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +94 -0
  83. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/cache-strategy.mjs +32 -23
  84. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/session-builder.mjs +33 -27
  85. package/src/runtime/agent/orchestrator/{bridge-trace.mjs → agent-trace.mjs} +132 -81
  86. package/src/runtime/agent/orchestrator/cache-mtime.mjs +0 -21
  87. package/src/runtime/agent/orchestrator/config.mjs +174 -55
  88. package/src/runtime/agent/orchestrator/context/collect.mjs +195 -487
  89. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/internal-roles.mjs +77 -29
  91. package/src/runtime/agent/orchestrator/internal-tools.mjs +5 -6
  92. package/src/runtime/agent/orchestrator/mcp/client.mjs +15 -9
  93. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -1
  94. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +377 -243
  95. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +146 -93
  96. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +236 -4
  97. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +49 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini.mjs +58 -13
  99. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +67 -149
  100. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +132 -2
  101. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +4 -1
  102. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +45 -0
  103. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +61 -116
  104. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +25 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +79 -255
  106. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +221 -70
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +477 -147
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +343 -496
  109. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -6
  110. package/src/runtime/agent/orchestrator/providers/registry.mjs +88 -51
  111. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +31 -11
  112. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +41 -8
  113. package/src/runtime/agent/orchestrator/session/cache/post-edit-marks.mjs +4 -4
  114. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +1 -1
  115. package/src/runtime/agent/orchestrator/session/compact.mjs +1173 -267
  116. package/src/runtime/agent/orchestrator/session/context-utils.mjs +199 -36
  117. package/src/runtime/agent/orchestrator/session/loop.mjs +851 -674
  118. package/src/runtime/agent/orchestrator/session/manager.mjs +1593 -466
  119. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +107 -0
  120. package/src/runtime/agent/orchestrator/session/store.mjs +291 -46
  121. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  122. package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -16
  123. package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +3 -219
  124. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +34 -7
  125. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
  126. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +19 -0
  127. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +9 -9
  128. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +60 -37
  129. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +21 -2
  130. package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +1 -1
  131. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -7
  132. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -3
  133. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -12
  134. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -0
  135. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -2
  136. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +5 -12
  137. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +1 -1
  138. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -36
  139. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +2 -40
  140. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +148 -27
  141. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -2
  142. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +43 -75
  143. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +90 -20
  144. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +1 -1
  145. package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -5
  146. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +86 -0
  147. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +11 -11
  148. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4106 -4019
  149. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -4
  150. package/src/runtime/agent/orchestrator/tools/patch.mjs +90 -6
  151. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +6 -4
  152. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +4 -4
  153. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +8 -1
  154. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -4
  155. package/src/runtime/channels/index.mjs +152 -24
  156. package/src/runtime/channels/lib/scheduler.mjs +18 -14
  157. package/src/runtime/channels/lib/session-discovery.mjs +3 -2
  158. package/src/runtime/channels/lib/tool-format.mjs +0 -2
  159. package/src/runtime/channels/lib/transcript-discovery.mjs +3 -2
  160. package/src/runtime/channels/lib/webhook.mjs +1 -1
  161. package/src/runtime/channels/tool-defs.mjs +29 -29
  162. package/src/runtime/memory/index.mjs +635 -107
  163. package/src/runtime/memory/lib/agent-ipc.mjs +29 -12
  164. package/src/runtime/memory/lib/core-memory-store.mjs +2 -2
  165. package/src/runtime/memory/lib/embedding-model-config.mjs +55 -0
  166. package/src/runtime/memory/lib/embedding-provider.mjs +31 -4
  167. package/src/runtime/memory/lib/embedding-worker.mjs +19 -10
  168. package/src/runtime/memory/lib/memory-cycle1.mjs +38 -17
  169. package/src/runtime/memory/lib/memory-cycle2.mjs +6 -7
  170. package/src/runtime/memory/lib/memory-cycle3.mjs +4 -4
  171. package/src/runtime/memory/lib/memory-ops-policy.mjs +2 -1
  172. package/src/runtime/memory/lib/memory-session-merge.mjs +38 -0
  173. package/src/runtime/memory/lib/memory.mjs +88 -9
  174. package/src/runtime/memory/lib/model-profile.mjs +1 -1
  175. package/src/runtime/memory/lib/pg/adapter.mjs +15 -1
  176. package/src/runtime/memory/lib/pg/supervisor.mjs +12 -0
  177. package/src/runtime/memory/lib/runtime-fetcher.mjs +37 -3
  178. package/src/runtime/memory/lib/session-ingest.mjs +194 -0
  179. package/src/runtime/memory/lib/trace-store.mjs +96 -51
  180. package/src/runtime/memory/tool-defs.mjs +46 -37
  181. package/src/runtime/search/index.mjs +102 -466
  182. package/src/runtime/search/lib/web-tools.mjs +45 -25
  183. package/src/runtime/search/tool-defs.mjs +16 -23
  184. package/src/runtime/shared/abort-controller.mjs +1 -1
  185. package/src/runtime/shared/atomic-file.mjs +4 -3
  186. package/src/runtime/shared/background-tasks.mjs +122 -11
  187. package/src/runtime/shared/child-spawn-gate.mjs +145 -0
  188. package/src/runtime/shared/config.mjs +7 -4
  189. package/src/runtime/shared/err-text.mjs +131 -4
  190. package/src/runtime/shared/llm/cost.mjs +2 -2
  191. package/src/runtime/shared/llm/http-agent.mjs +23 -7
  192. package/src/runtime/shared/llm/index.mjs +34 -11
  193. package/src/runtime/shared/llm/usage-log.mjs +4 -4
  194. package/src/runtime/shared/markdown-frontmatter.mjs +56 -0
  195. package/src/runtime/shared/singleton-owner.mjs +104 -0
  196. package/src/runtime/shared/tool-execution-contract.mjs +199 -20
  197. package/src/runtime/shared/tool-execution-contract.test.mjs +183 -0
  198. package/src/runtime/shared/tool-surface.mjs +624 -98
  199. package/src/runtime/shared/user-data-guard.mjs +0 -2
  200. package/src/standalone/agent-task-status.mjs +203 -0
  201. package/src/standalone/agent-task-status.test.mjs +76 -0
  202. package/src/standalone/agent-tool.mjs +1913 -0
  203. package/src/standalone/channel-worker.mjs +370 -14
  204. package/src/standalone/explore-tool.mjs +165 -70
  205. package/src/standalone/folder-dialog.mjs +314 -0
  206. package/src/standalone/hook-bus.mjs +898 -22
  207. package/src/standalone/memory-runtime-proxy.mjs +320 -0
  208. package/src/standalone/projects.mjs +226 -0
  209. package/src/standalone/provider-admin.mjs +41 -24
  210. package/src/standalone/seeds.mjs +2 -69
  211. package/src/standalone/usage-dashboard.mjs +96 -8
  212. package/src/tui/App.jsx +4798 -2153
  213. package/src/tui/components/AnsiText.jsx +39 -28
  214. package/src/tui/components/ContextPanel.jsx +87 -29
  215. package/src/tui/components/Markdown.jsx +43 -77
  216. package/src/tui/components/MarkdownTable.jsx +9 -184
  217. package/src/tui/components/Message.jsx +28 -11
  218. package/src/tui/components/Picker.jsx +95 -56
  219. package/src/tui/components/PromptInput.jsx +367 -239
  220. package/src/tui/components/QueuedCommands.jsx +1 -1
  221. package/src/tui/components/SlashCommandPalette.jsx +27 -21
  222. package/src/tui/components/Spinner.jsx +67 -38
  223. package/src/tui/components/StatusLine.jsx +606 -38
  224. package/src/tui/components/TextEntryPanel.jsx +128 -9
  225. package/src/tui/components/ToolExecution.jsx +617 -368
  226. package/src/tui/components/TurnDone.jsx +3 -3
  227. package/src/tui/components/UsagePanel.jsx +3 -5
  228. package/src/tui/components/tool-output-format.mjs +365 -0
  229. package/src/tui/components/tool-output-format.test.mjs +220 -0
  230. package/src/tui/dist/index.mjs +8915 -2418
  231. package/src/tui/engine-runtime-notification.test.mjs +115 -0
  232. package/src/tui/engine-tool-result-text.test.mjs +75 -0
  233. package/src/tui/engine.mjs +1455 -279
  234. package/src/tui/figures.mjs +21 -40
  235. package/src/tui/index.jsx +75 -31
  236. package/src/tui/input-editing.mjs +25 -0
  237. package/src/tui/markdown/format-token.mjs +511 -68
  238. package/src/tui/markdown/format-token.test.mjs +216 -0
  239. package/src/tui/markdown/render-ansi.mjs +94 -0
  240. package/src/tui/markdown/render-ansi.test.mjs +108 -0
  241. package/src/tui/markdown/stream-fence.mjs +34 -0
  242. package/src/tui/markdown/stream-fence.test.mjs +26 -0
  243. package/src/tui/markdown/table-layout.mjs +250 -0
  244. package/src/tui/paste-attachments.mjs +0 -7
  245. package/src/tui/spinner-verbs.mjs +1 -2
  246. package/src/tui/statusline-ansi-bridge.mjs +172 -0
  247. package/src/tui/statusline-ansi-bridge.test.mjs +159 -0
  248. package/src/tui/theme.mjs +746 -24
  249. package/src/tui/time-format.mjs +1 -1
  250. package/src/tui/transcript-tool-failures.mjs +67 -0
  251. package/src/tui/transcript-tool-failures.test.mjs +111 -0
  252. package/src/ui/ansi.mjs +1 -2
  253. package/src/ui/markdown.mjs +85 -26
  254. package/src/ui/markdown.test.mjs +70 -0
  255. package/src/ui/model-display.mjs +121 -0
  256. package/src/ui/session-stats.mjs +44 -0
  257. package/src/ui/statusline-context-label.test.mjs +15 -0
  258. package/src/ui/statusline.mjs +386 -178
  259. package/src/ui/tool-card.mjs +3 -16
  260. package/src/vendor/statusline/bin/statusline-lib.mjs +8 -4
  261. package/src/vendor/statusline/bin/statusline-route.mjs +169 -37
  262. package/src/vendor/statusline/bin/statusline-route.test.mjs +80 -0
  263. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +3 -3
  264. package/src/vendor/statusline/src/gateway/claude-current.mjs +1 -1
  265. package/src/vendor/statusline/src/gateway/route-meta.mjs +44 -6
  266. package/src/vendor/statusline/src/gateway/session-routes.mjs +1 -1
  267. package/src/workflows/default/WORKFLOW.md +12 -5
  268. package/src/workflows/default/workflow.json +0 -1
  269. package/src/workflows/solo/WORKFLOW.md +15 -0
  270. package/src/workflows/solo/workflow.json +7 -0
  271. package/vendor/ink/build/output.js +6 -1
  272. package/src/agents/scheduler-task.md +0 -3
  273. package/src/agents/web-researcher/AGENT.md +0 -3
  274. package/src/agents/web-researcher/agent.json +0 -6
  275. package/src/agents/webhook-handler.md +0 -3
  276. package/src/rules/bridge/00-common.md +0 -5
  277. package/src/rules/bridge/30-explorer.md +0 -4
  278. package/src/rules/lead/00-tool-lead.md +0 -5
  279. package/src/rules/shared/00-language.md +0 -3
  280. package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
  281. package/src/runtime/agent/orchestrator/tools/mutation-content-cache.mjs +0 -67
  282. package/src/runtime/memory/lib/bridge-trace-queries.mjs +0 -120
  283. package/src/runtime/shared/llm/pid-cleanup.mjs +0 -27
  284. package/src/standalone/bridge-tool.mjs +0 -1414
  285. package/src/tui/runtime/shared/process-shutdown.mjs +0 -1
@@ -1,13 +1,7 @@
1
1
  import {
2
- closeSync,
3
2
  existsSync,
4
- openSync,
5
3
  readFileSync,
6
- readdirSync,
7
- readSync,
8
- statSync,
9
4
  } from 'fs';
10
- import { homedir } from 'os';
11
5
  import { join } from 'path';
12
6
  import { updateJsonAtomicSync } from '../../../shared/atomic-file.mjs';
13
7
  import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
@@ -16,12 +10,10 @@ import { getLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
16
10
  const CACHE_FILE = 'gateway-oauth-usage-cache.json';
17
11
  const LIVE_CACHE_TTL_MS = 60_000;
18
12
  const DISK_CACHE_TTL_MS = 10 * 60_000;
13
+ const STALE_DISK_CACHE_TTL_MS = 7 * 24 * 60 * 60_000;
19
14
  const NEGATIVE_CACHE_TTL_MS = 5 * 60_000;
20
15
  const FETCH_TIMEOUT_MS = 4500;
21
16
  const WARN_TTL_MS = 5 * 60_000;
22
- const MAX_ROLLOUT_FILES = 8;
23
- const MAX_ROLLOUT_SCAN_FILES = 2500;
24
- const MAX_TAIL_BYTES = 2 * 1024 * 1024;
25
17
 
26
18
  const memoryCache = new Map();
27
19
  const inflight = new Map();
@@ -55,6 +47,17 @@ function routeKey(routeInfo = {}) {
55
47
  return `${providerKey(routeInfo)}\u0001${String(routeInfo?.model || '')}`;
56
48
  }
57
49
 
50
+ function cacheModelId(value) {
51
+ const model = cleanString(value);
52
+ if (!model) return null;
53
+ // Route cache keys are model identifiers, not free-form display/user text.
54
+ // A malformed route once copied a user prompt into the provider-wide OAuth
55
+ // cache; keeping cache keys/display model IDs single-token prevents that
56
+ // poison from persisting or winning provider fallback lookups.
57
+ if (model.length > 160 || /\s/.test(model)) return null;
58
+ return model;
59
+ }
60
+
58
61
  function cachePath() {
59
62
  return join(resolvePluginData(), CACHE_FILE);
60
63
  }
@@ -134,17 +137,41 @@ function freshSnapshot(snapshot, ttlMs) {
134
137
  return snapshot;
135
138
  }
136
139
 
137
- export function readCachedOAuthUsageSnapshot(routeInfo) {
140
+ function newestProviderSnapshot(entries, provider, ttlMs) {
141
+ const providerOnly = String(provider || '').toLowerCase();
142
+ if (!providerOnly) return null;
143
+ const routePrefix = `${providerOnly}\u0001`;
144
+ let best = null;
145
+ let bestAt = 0;
146
+ const iterable = entries instanceof Map ? entries.entries() : Object.entries(entries || {});
147
+ for (const [key, snapshot] of iterable) {
148
+ if (key !== providerOnly && !String(key).startsWith(routePrefix)) continue;
149
+ const fresh = freshSnapshot(snapshot, ttlMs);
150
+ const at = num(fresh?.cachedAt, 0);
151
+ if (fresh && at >= bestAt) {
152
+ best = fresh;
153
+ bestAt = at;
154
+ }
155
+ }
156
+ return best;
157
+ }
158
+
159
+ export function readCachedOAuthUsageSnapshot(routeInfo, options = {}) {
138
160
  const key = routeKey(routeInfo);
139
161
  const providerOnlyKey = providerKey(routeInfo);
162
+ const diskTtlMs = options?.allowStale === true
163
+ ? STALE_DISK_CACHE_TTL_MS
164
+ : DISK_CACHE_TTL_MS;
140
165
  const mem = freshSnapshot(memoryCache.get(key), LIVE_CACHE_TTL_MS)
141
- || freshSnapshot(memoryCache.get(providerOnlyKey), LIVE_CACHE_TTL_MS);
166
+ || freshSnapshot(memoryCache.get(providerOnlyKey), LIVE_CACHE_TTL_MS)
167
+ || newestProviderSnapshot(memoryCache, providerOnlyKey, LIVE_CACHE_TTL_MS);
142
168
  if (mem) return mem;
143
169
 
144
170
  const raw = readJsonFile(cachePath());
145
171
  const routes = raw?.routes && typeof raw.routes === 'object' ? raw.routes : {};
146
- return freshSnapshot(routes[key], DISK_CACHE_TTL_MS)
147
- || freshSnapshot(routes[providerOnlyKey], DISK_CACHE_TTL_MS)
172
+ return freshSnapshot(routes[key], diskTtlMs)
173
+ || freshSnapshot(routes[providerOnlyKey], diskTtlMs)
174
+ || newestProviderSnapshot(routes, providerOnlyKey, diskTtlMs)
148
175
  || null;
149
176
  }
150
177
 
@@ -366,105 +393,16 @@ function normalizeAnthropicUsage(data, source = 'anthropic-oauth') {
366
393
  };
367
394
  }
368
395
 
369
- function parseStatuslineRateLimits(raw, source) {
370
- if (!raw || typeof raw !== 'object') return null;
371
- const rateLimits = raw.rate_limits || raw.rateLimits;
372
- if (!rateLimits || typeof rateLimits !== 'object') return null;
373
- const windows = [
374
- windowFromPercent('5H', rateLimits.five_hour || rateLimits.primary, source),
375
- windowFromPercent('7D', rateLimits.seven_day || rateLimits.secondary, source),
376
- ].filter(Boolean);
377
- if (!windows.length) return null;
378
- return { quotaWindows: windows, source };
379
- }
380
-
381
- function tailFileText(file, maxBytes = MAX_TAIL_BYTES) {
382
- let fd = null;
383
- try {
384
- const st = statSync(file);
385
- const size = st.size || 0;
386
- const start = Math.max(0, size - maxBytes);
387
- const len = size - start;
388
- const buf = Buffer.alloc(len);
389
- fd = openSync(file, 'r');
390
- readSync(fd, buf, 0, len, start);
391
- return buf.toString('utf8');
392
- } catch {
393
- return '';
394
- } finally {
395
- if (fd != null) {
396
- try { closeSync(fd); } catch {}
397
- }
398
- }
399
- }
400
-
401
- function newestRolloutFiles(root) {
402
- const out = [];
403
- const stack = [root];
404
- let seen = 0;
405
- while (stack.length && seen < MAX_ROLLOUT_SCAN_FILES) {
406
- const dir = stack.pop();
407
- let entries = [];
408
- try { entries = readdirSync(dir, { withFileTypes: true }); } catch { continue; }
409
- for (const ent of entries) {
410
- if (seen++ >= MAX_ROLLOUT_SCAN_FILES) break;
411
- const full = join(dir, ent.name);
412
- if (ent.isDirectory()) {
413
- stack.push(full);
414
- } else if (ent.isFile() && /^rollout-.*\.jsonl$/i.test(ent.name)) {
415
- try {
416
- const st = statSync(full);
417
- out.push({ file: full, mtimeMs: st.mtimeMs || 0 });
418
- } catch {}
419
- }
420
- }
421
- }
422
- return out
423
- .sort((a, b) => b.mtimeMs - a.mtimeMs)
424
- .slice(0, MAX_ROLLOUT_FILES)
425
- .map(x => x.file);
426
- }
427
-
428
- function latestCodexRateLimitsFromRollout() {
429
- const root = join(homedir(), '.codex', 'sessions');
430
- if (!existsSync(root)) return null;
431
- for (const file of newestRolloutFiles(root)) {
432
- const lines = tailFileText(file).split(/\r?\n/).filter(Boolean);
433
- for (let i = lines.length - 1; i >= 0; i--) {
434
- try {
435
- const row = JSON.parse(lines[i]);
436
- const rateLimits = row?.payload?.rate_limits || row?.rate_limits;
437
- const snap = normalizeCodexRateLimits(rateLimits, 'openai-codex-rollout');
438
- if (snap && hasFutureWindow(snap)) return snap;
439
- } catch {}
440
- }
441
- }
442
- return null;
443
- }
444
-
445
396
  function latestClaudeStatuslineUsage() {
446
- // Standalone CLI must not borrow Claude Code's rendered statusline cache.
397
+ // Standalone CLI must not borrow a host-agent rendered statusline cache.
447
398
  // Anthropic quota should come from the OAuth usage endpoint above.
448
399
  return null;
449
400
  }
450
401
 
451
- function grokCliIdentity(accessToken = '') {
452
- const raw = readJsonFile(join(homedir(), '.grok', 'auth.json'));
453
- if (!raw || typeof raw !== 'object') return {};
454
- const entries = Object.values(raw).filter(v => v && typeof v === 'object');
455
- const exact = accessToken ? entries.find(v => v.key === accessToken || v.access_token === accessToken) : null;
456
- const entry = exact || entries.find(v => v.key || v.access_token) || null;
457
- if (!entry) return {};
458
- return {
459
- userId: entry.user_id || entry.userId || entry.principal_id || entry.principalId || '',
460
- token: entry.key || entry.access_token || entry.accessToken || '',
461
- };
462
- }
463
-
464
402
  async function fetchOpenAICodexUsage(providerObj) {
465
403
  const auth = await providerObj?.ensureAuth?.({ reason: 'usage' });
466
404
  const token = auth?.access_token || auth?.accessToken;
467
- if (!token) return latestCodexRateLimitsFromRollout();
405
+ if (!token) return null;
468
406
  const res = await fetch('https://chatgpt.com/backend-api/wham/usage', fetchOptions({
469
407
  Authorization: `Bearer ${token}`,
470
408
  originator: 'codex_cli_rs',
@@ -472,9 +410,9 @@ async function fetchOpenAICodexUsage(providerObj) {
472
410
  'OpenAI-Beta': 'responses=experimental',
473
411
  Accept: 'application/json',
474
412
  }));
475
- if (!res.ok) throw new Error(`codex usage ${res.status}`);
413
+ if (!res.ok) throw new Error(`openai-oauth usage ${res.status}`);
476
414
  const data = await res.json();
477
- return normalizeOpenAIWhamUsage(data) || latestCodexRateLimitsFromRollout();
415
+ return normalizeOpenAIWhamUsage(data);
478
416
  }
479
417
 
480
418
  async function fetchAnthropicUsage(providerObj) {
@@ -497,8 +435,7 @@ async function fetchGrokUsage(providerObj, routeInfo) {
497
435
  const auth = await providerObj?.ensureAuth?.({ reason: 'usage' });
498
436
  const token = auth?.access_token || auth?.accessToken || auth?.key;
499
437
  if (!token) return null;
500
- const cliIdentity = grokCliIdentity(token);
501
- const userId = auth?.user_id || auth?.userId || auth?.principal_id || auth?.principalId || cliIdentity.userId || '';
438
+ const userId = auth?.user_id || auth?.userId || auth?.principal_id || auth?.principalId || '';
502
439
  const cliHeaders = {
503
440
  Authorization: `Bearer ${token}`,
504
441
  'X-XAI-Token-Auth': 'xai-grok-cli',
@@ -583,7 +520,6 @@ export async function fetchOAuthUsageSnapshot(routeInfo, providerObj, log = () =
583
520
  snapshot = await fetchGrokUsage(providerObj, routeInfo);
584
521
  }
585
522
  } catch (err) {
586
- if (provider === 'openai-oauth') snapshot = latestCodexRateLimitsFromRollout();
587
523
  if (provider === 'anthropic-oauth') snapshot = latestClaudeStatuslineUsage();
588
524
  if (!snapshot) {
589
525
  warnThrottled(log, `oauth-usage:${provider}`, `gateway ${provider} usage fetch unavailable: ${err?.message || err}`);
@@ -596,17 +532,26 @@ export async function fetchOAuthUsageSnapshot(routeInfo, providerObj, log = () =
596
532
  return null;
597
533
  }
598
534
 
599
- const normalized = {
535
+ const model = cacheModelId(routeInfo?.model) || cacheModelId(snapshot.model);
536
+ const providerSnapshot = {
600
537
  ...snapshot,
601
538
  provider: routeInfo?.provider || snapshot.provider || provider,
602
- model: routeInfo?.model || snapshot.model || null,
603
539
  cachedAt: Date.now(),
604
540
  };
605
- memoryCache.set(key, normalized);
606
- memoryCache.set(providerOnly, normalized);
607
- writeSnapshotCache(key, normalized);
608
- writeSnapshotCache(providerOnly, normalized);
609
- return normalized;
541
+ delete providerSnapshot.model;
542
+
543
+ const routeSnapshot = model
544
+ ? { ...providerSnapshot, model }
545
+ : providerSnapshot;
546
+
547
+ if (model) {
548
+ const normalizedKey = `${provider}\u0001${model}`;
549
+ memoryCache.set(normalizedKey, routeSnapshot);
550
+ writeSnapshotCache(normalizedKey, routeSnapshot);
551
+ }
552
+ memoryCache.set(providerOnly, providerSnapshot);
553
+ writeSnapshotCache(providerOnly, providerSnapshot);
554
+ return routeSnapshot;
610
555
  })().finally(() => {
611
556
  inflight.delete(key);
612
557
  });
@@ -0,0 +1,25 @@
1
+ // Lightweight preset table shared by config/registry without importing the
2
+ // OpenAI SDK or compat runtime implementation.
3
+ export const OPENAI_COMPAT_PRESETS = {
4
+ deepseek: {
5
+ baseURL: 'https://api.deepseek.com',
6
+ defaultModel: 'deepseek-v4-pro',
7
+ },
8
+ xai: {
9
+ baseURL: 'https://api.x.ai/v1',
10
+ defaultModel: 'grok-4.3',
11
+ },
12
+ // OpenCode Go - low-cost coding-model subscription gateway.
13
+ 'opencode-go': {
14
+ baseURL: 'https://opencode.ai/zen/go/v1',
15
+ defaultModel: 'glm-5.2',
16
+ },
17
+ ollama: {
18
+ baseURL: 'http://localhost:11434/v1',
19
+ defaultModel: 'llama3.3:latest',
20
+ },
21
+ lmstudio: {
22
+ baseURL: 'http://localhost:1234/v1',
23
+ defaultModel: 'default',
24
+ },
25
+ };