@yansigit/opencodex 2.31.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 (826) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/LICENSE +21 -0
  3. package/README.md +303 -0
  4. package/assets/architecture.png +0 -0
  5. package/assets/banner.png +0 -0
  6. package/assets/claude-code-models.gif +0 -0
  7. package/assets/codex-app-picker.png +0 -0
  8. package/bin/ocx.mjs +587 -0
  9. package/bin/package-main.mjs +9 -0
  10. package/gui/dist/assets/index-BNJ7r4Gd.js +102 -0
  11. package/gui/dist/assets/index-CGoDO3uO.css +1 -0
  12. package/gui/dist/favicon.png +0 -0
  13. package/gui/dist/icons.svg +24 -0
  14. package/gui/dist/index.html +25 -0
  15. package/gui/dist/logo.png +0 -0
  16. package/gui/dist/provider-icons/alibaba-color.svg +1 -0
  17. package/gui/dist/provider-icons/antigravity-color.svg +1 -0
  18. package/gui/dist/provider-icons/claude-color.svg +1 -0
  19. package/gui/dist/provider-icons/cline-color.svg +16 -0
  20. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
  21. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  22. package/gui/dist/provider-icons/copilot-color.svg +1 -0
  23. package/gui/dist/provider-icons/cursor-color.svg +2 -0
  24. package/gui/dist/provider-icons/deepseek-color.svg +1 -0
  25. package/gui/dist/provider-icons/discord.svg +1 -0
  26. package/gui/dist/provider-icons/firepass-color.svg +1 -0
  27. package/gui/dist/provider-icons/fireworks-color.svg +1 -0
  28. package/gui/dist/provider-icons/gemini-color.svg +1 -0
  29. package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
  30. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
  31. package/gui/dist/provider-icons/grok.svg +1 -0
  32. package/gui/dist/provider-icons/groq-color.svg +1 -0
  33. package/gui/dist/provider-icons/huggingface-color.svg +1 -0
  34. package/gui/dist/provider-icons/kimi-color.svg +1 -0
  35. package/gui/dist/provider-icons/kiro-color.svg +15 -0
  36. package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
  37. package/gui/dist/provider-icons/mistral-color.svg +1 -0
  38. package/gui/dist/provider-icons/moonshot-color.svg +1 -0
  39. package/gui/dist/provider-icons/nvidia-color.svg +1 -0
  40. package/gui/dist/provider-icons/ollama-color.svg +1 -0
  41. package/gui/dist/provider-icons/openai.svg +1 -0
  42. package/gui/dist/provider-icons/opencode.svg +2 -0
  43. package/gui/dist/provider-icons/openrouter-color.svg +1 -0
  44. package/gui/dist/provider-icons/pi.svg +21 -0
  45. package/gui/dist/provider-icons/qianfan-color.svg +1 -0
  46. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
  47. package/gui/dist/provider-icons/telegram.svg +1 -0
  48. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
  49. package/gui/dist/provider-icons/vllm-color.svg +1 -0
  50. package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
  51. package/package.json +108 -0
  52. package/src/AGENTS.md +28 -0
  53. package/src/adapters/anthropic-image-guard.ts +251 -0
  54. package/src/adapters/anthropic-image-normalize.ts +518 -0
  55. package/src/adapters/anthropic-output-schema.ts +137 -0
  56. package/src/adapters/anthropic.ts +1327 -0
  57. package/src/adapters/azure.ts +36 -0
  58. package/src/adapters/base.ts +121 -0
  59. package/src/adapters/client-fingerprint.ts +65 -0
  60. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  61. package/src/adapters/command-code.ts +601 -0
  62. package/src/adapters/cursor/arg-codec.ts +38 -0
  63. package/src/adapters/cursor/arg-normalize.ts +104 -0
  64. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  65. package/src/adapters/cursor/cursor-errors.ts +288 -0
  66. package/src/adapters/cursor/discovery.ts +333 -0
  67. package/src/adapters/cursor/effort-map.ts +151 -0
  68. package/src/adapters/cursor/exec-policy.ts +88 -0
  69. package/src/adapters/cursor/framing.ts +250 -0
  70. package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
  71. package/src/adapters/cursor/h2-pool.ts +123 -0
  72. package/src/adapters/cursor/http1-bidi.ts +361 -0
  73. package/src/adapters/cursor/images.ts +704 -0
  74. package/src/adapters/cursor/kv-store.ts +52 -0
  75. package/src/adapters/cursor/live-models.ts +269 -0
  76. package/src/adapters/cursor/live-smoke-gate.ts +41 -0
  77. package/src/adapters/cursor/live-transport.ts +1653 -0
  78. package/src/adapters/cursor/mcp-config.ts +42 -0
  79. package/src/adapters/cursor/mcp-manager.ts +333 -0
  80. package/src/adapters/cursor/message-mapper.ts +49 -0
  81. package/src/adapters/cursor/native-exec-common.ts +76 -0
  82. package/src/adapters/cursor/native-exec-desktop.ts +184 -0
  83. package/src/adapters/cursor/native-exec-fs.ts +332 -0
  84. package/src/adapters/cursor/native-exec-mcp.ts +153 -0
  85. package/src/adapters/cursor/native-exec-network.ts +43 -0
  86. package/src/adapters/cursor/native-exec-shell.ts +547 -0
  87. package/src/adapters/cursor/native-exec-tools.ts +118 -0
  88. package/src/adapters/cursor/native-exec.ts +663 -0
  89. package/src/adapters/cursor/protobuf-events.ts +1381 -0
  90. package/src/adapters/cursor/protobuf-request.ts +1032 -0
  91. package/src/adapters/cursor/request-builder.ts +461 -0
  92. package/src/adapters/cursor/thread-continuity.ts +67 -0
  93. package/src/adapters/cursor/tool-definitions.ts +735 -0
  94. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  95. package/src/adapters/cursor/transport-retry.ts +132 -0
  96. package/src/adapters/cursor/transport.ts +79 -0
  97. package/src/adapters/cursor/types.ts +79 -0
  98. package/src/adapters/cursor.ts +322 -0
  99. package/src/adapters/google-antigravity-hosts.ts +48 -0
  100. package/src/adapters/google-antigravity-replay.ts +827 -0
  101. package/src/adapters/google-antigravity-tools.ts +105 -0
  102. package/src/adapters/google-antigravity-wire.ts +141 -0
  103. package/src/adapters/google-errors.ts +92 -0
  104. package/src/adapters/google-http.ts +460 -0
  105. package/src/adapters/google-tool-schema.ts +238 -0
  106. package/src/adapters/google-truncation.ts +24 -0
  107. package/src/adapters/google-wire-compiler.ts +232 -0
  108. package/src/adapters/google.ts +1377 -0
  109. package/src/adapters/identity.ts +77 -0
  110. package/src/adapters/image.ts +23 -0
  111. package/src/adapters/kiro-constants.ts +16 -0
  112. package/src/adapters/kiro-errors.ts +208 -0
  113. package/src/adapters/kiro-events.ts +197 -0
  114. package/src/adapters/kiro-images.ts +129 -0
  115. package/src/adapters/kiro-retry.ts +312 -0
  116. package/src/adapters/kiro-thinking.ts +112 -0
  117. package/src/adapters/kiro-tool-fallback.ts +36 -0
  118. package/src/adapters/kiro-tools.ts +224 -0
  119. package/src/adapters/kiro-truncation.ts +33 -0
  120. package/src/adapters/kiro-wire.ts +129 -0
  121. package/src/adapters/kiro.ts +1936 -0
  122. package/src/adapters/mimo-free.ts +280 -0
  123. package/src/adapters/openai-chat-url.ts +11 -0
  124. package/src/adapters/openai-chat.ts +1980 -0
  125. package/src/adapters/openai-responses-url.ts +16 -0
  126. package/src/adapters/openai-responses.ts +1911 -0
  127. package/src/adapters/registry.ts +175 -0
  128. package/src/adapters/responses-tool-schema.ts +67 -0
  129. package/src/adapters/run-turn-queue.ts +114 -0
  130. package/src/adapters/tool-call-id.ts +119 -0
  131. package/src/adapters/tool-catalog-nudge.ts +154 -0
  132. package/src/adapters/upstream-http-error.ts +48 -0
  133. package/src/adapters/xai-web-search.ts +185 -0
  134. package/src/bridge.ts +1986 -0
  135. package/src/chat/inbound.ts +319 -0
  136. package/src/chat/outbound.ts +821 -0
  137. package/src/claude/agents-inject.ts +266 -0
  138. package/src/claude/alias.ts +149 -0
  139. package/src/claude/auth-detect.ts +229 -0
  140. package/src/claude/auth-mode-migration.ts +32 -0
  141. package/src/claude/auth-mode.ts +62 -0
  142. package/src/claude/context-windows.ts +205 -0
  143. package/src/claude/desktop-3p-guard.ts +35 -0
  144. package/src/claude/desktop-3p-paths.ts +84 -0
  145. package/src/claude/desktop-3p.ts +615 -0
  146. package/src/claude/desktop-health.ts +26 -0
  147. package/src/claude/desktop-profile.ts +263 -0
  148. package/src/claude/gateway-cache.ts +107 -0
  149. package/src/claude/inbound-debug.ts +163 -0
  150. package/src/claude/inbound.ts +578 -0
  151. package/src/claude/model-info.ts +174 -0
  152. package/src/claude/outbound.ts +926 -0
  153. package/src/cli/access.ts +108 -0
  154. package/src/cli/account-api.ts +302 -0
  155. package/src/cli/account-auth.ts +250 -0
  156. package/src/cli/account-catalog-refresh.ts +14 -0
  157. package/src/cli/account-extended.ts +737 -0
  158. package/src/cli/account-main.ts +317 -0
  159. package/src/cli/account.ts +299 -0
  160. package/src/cli/agent-driven.ts +70 -0
  161. package/src/cli/agent.ts +290 -0
  162. package/src/cli/catalog-prewarm.ts +27 -0
  163. package/src/cli/claude-agent-startup-sync.ts +73 -0
  164. package/src/cli/claude-desktop.ts +213 -0
  165. package/src/cli/claude.ts +355 -0
  166. package/src/cli/codex-log-guard-doctor.ts +103 -0
  167. package/src/cli/codex-shim-autorestore.ts +47 -0
  168. package/src/cli/codex-shim-readiness.ts +76 -0
  169. package/src/cli/combo.ts +127 -0
  170. package/src/cli/config-command.ts +209 -0
  171. package/src/cli/debug.ts +228 -0
  172. package/src/cli/dispatch.ts +585 -0
  173. package/src/cli/doctor.ts +1202 -0
  174. package/src/cli/ensure-desired-integrations.ts +152 -0
  175. package/src/cli/export-command.ts +213 -0
  176. package/src/cli/help.ts +101 -0
  177. package/src/cli/index.ts +973 -0
  178. package/src/cli/init.ts +211 -0
  179. package/src/cli/integrations.ts +260 -0
  180. package/src/cli/interactive-confirm.ts +133 -0
  181. package/src/cli/lab.ts +607 -0
  182. package/src/cli/launcher-context.ts +77 -0
  183. package/src/cli/minimax.ts +497 -0
  184. package/src/cli/models-runtime.ts +245 -0
  185. package/src/cli/models.ts +422 -0
  186. package/src/cli/observe.ts +206 -0
  187. package/src/cli/opencode.ts +588 -0
  188. package/src/cli/provider-replit.ts +232 -0
  189. package/src/cli/provider-runtime.ts +179 -0
  190. package/src/cli/provider.ts +492 -0
  191. package/src/cli/ready.ts +301 -0
  192. package/src/cli/registry.ts +422 -0
  193. package/src/cli/replit-gateway-key-input.ts +138 -0
  194. package/src/cli/root.ts +86 -0
  195. package/src/cli/route-policy.ts +92 -0
  196. package/src/cli/runtime-api.ts +328 -0
  197. package/src/cli/star-prompt.ts +211 -0
  198. package/src/cli/status-oauth.ts +78 -0
  199. package/src/cli/status.ts +328 -0
  200. package/src/cli/system-command.ts +112 -0
  201. package/src/cli/system-restart-client.ts +146 -0
  202. package/src/cli/tray-proxy.ts +199 -0
  203. package/src/cli/v2.ts +268 -0
  204. package/src/cli.ts +10 -0
  205. package/src/clients/config-export.ts +1704 -0
  206. package/src/codex/account-id.ts +34 -0
  207. package/src/codex/account-label.ts +47 -0
  208. package/src/codex/account-lifecycle.ts +172 -0
  209. package/src/codex/account-namespace-match.ts +63 -0
  210. package/src/codex/account-namespaces.ts +195 -0
  211. package/src/codex/account-pause.ts +20 -0
  212. package/src/codex/account-priority.ts +83 -0
  213. package/src/codex/account-runtime-state.ts +31 -0
  214. package/src/codex/account-store.ts +544 -0
  215. package/src/codex/account-usability.ts +43 -0
  216. package/src/codex/admission.ts +256 -0
  217. package/src/codex/affinity-debug.ts +162 -0
  218. package/src/codex/agent-roles-sync.ts +225 -0
  219. package/src/codex/agent-roles.ts +238 -0
  220. package/src/codex/app-server-processes.ts +1143 -0
  221. package/src/codex/app-server-restart-service.ts +232 -0
  222. package/src/codex/auth-api.ts +2147 -0
  223. package/src/codex/auth-collision.ts +109 -0
  224. package/src/codex/auth-context.ts +665 -0
  225. package/src/codex/autostart-health.ts +156 -0
  226. package/src/codex/catalog/account-models.ts +67 -0
  227. package/src/codex/catalog/aggregation.ts +436 -0
  228. package/src/codex/catalog/bundled.ts +549 -0
  229. package/src/codex/catalog/effort.ts +446 -0
  230. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  231. package/src/codex/catalog/kinds.ts +2 -0
  232. package/src/codex/catalog/metadata.ts +664 -0
  233. package/src/codex/catalog/native-models.ts +72 -0
  234. package/src/codex/catalog/parsing.ts +650 -0
  235. package/src/codex/catalog/provider-fetch.ts +2064 -0
  236. package/src/codex/catalog/sync.ts +1883 -0
  237. package/src/codex/catalog-admission.ts +199 -0
  238. package/src/codex/catalog-refresh-status.ts +105 -0
  239. package/src/codex/catalog-write-serialization.ts +242 -0
  240. package/src/codex/catalog.ts +14 -0
  241. package/src/codex/codex-write-lock.ts +384 -0
  242. package/src/codex/convergence-types.ts +614 -0
  243. package/src/codex/convergence.ts +651 -0
  244. package/src/codex/coordinator-doctor.ts +332 -0
  245. package/src/codex/custom-model-catalog-migration.ts +176 -0
  246. package/src/codex/data/upstream-models.json +830 -0
  247. package/src/codex/desired-state.ts +230 -0
  248. package/src/codex/exec-invocation.ts +22 -0
  249. package/src/codex/features.ts +1566 -0
  250. package/src/codex/generation.ts +202 -0
  251. package/src/codex/history-job.ts +407 -0
  252. package/src/codex/history-lock.ts +242 -0
  253. package/src/codex/history-migration-guardian.ts +108 -0
  254. package/src/codex/history-provider.ts +979 -0
  255. package/src/codex/history-transition.ts +105 -0
  256. package/src/codex/history-worker.ts +220 -0
  257. package/src/codex/home.ts +206 -0
  258. package/src/codex/inject-coordination.ts +290 -0
  259. package/src/codex/inject.ts +1733 -0
  260. package/src/codex/injected-marker.ts +106 -0
  261. package/src/codex/integration-record.ts +266 -0
  262. package/src/codex/internal/catalog-writer.ts +203 -0
  263. package/src/codex/internal/history-writer.ts +80 -0
  264. package/src/codex/journal.ts +225 -0
  265. package/src/codex/log-guard/inspect.ts +506 -0
  266. package/src/codex/log-guard/lock.ts +150 -0
  267. package/src/codex/log-guard/maintenance.ts +403 -0
  268. package/src/codex/log-guard/path-safety.ts +88 -0
  269. package/src/codex/log-guard/policy.ts +44 -0
  270. package/src/codex/log-guard/processes.ts +205 -0
  271. package/src/codex/log-guard/protection.ts +489 -0
  272. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  273. package/src/codex/main-account-cache.ts +56 -0
  274. package/src/codex/main-account.ts +68 -0
  275. package/src/codex/management-convergence.ts +167 -0
  276. package/src/codex/model-cache.ts +273 -0
  277. package/src/codex/model-entitlements.ts +353 -0
  278. package/src/codex/native-main-admission.ts +47 -0
  279. package/src/codex/native-main-auth-temp.ts +187 -0
  280. package/src/codex/native-main-claim.ts +178 -0
  281. package/src/codex/native-main-lock-file.ts +162 -0
  282. package/src/codex/native-main-owner.ts +329 -0
  283. package/src/codex/native-profile-api.ts +247 -0
  284. package/src/codex/native-profile-manager.ts +1531 -0
  285. package/src/codex/native-profile-processes.ts +121 -0
  286. package/src/codex/native-profile-recovery.ts +99 -0
  287. package/src/codex/native-profile-stage-store.ts +387 -0
  288. package/src/codex/native-profile-startup.ts +492 -0
  289. package/src/codex/native-profile-store.ts +855 -0
  290. package/src/codex/native-profile-types.ts +120 -0
  291. package/src/codex/native-residue.ts +682 -0
  292. package/src/codex/paths.ts +144 -0
  293. package/src/codex/plan-from-token.ts +140 -0
  294. package/src/codex/plan.ts +40 -0
  295. package/src/codex/plugins-doctor.ts +242 -0
  296. package/src/codex/pool-rotation.ts +295 -0
  297. package/src/codex/project-config-warnings.ts +425 -0
  298. package/src/codex/prompt-journal.ts +352 -0
  299. package/src/codex/prompt-layers.ts +967 -0
  300. package/src/codex/prompt-lock.ts +143 -0
  301. package/src/codex/quota-rejection.ts +298 -0
  302. package/src/codex/quota.ts +573 -0
  303. package/src/codex/refresh.ts +62 -0
  304. package/src/codex/reset-credit-recovery.ts +1044 -0
  305. package/src/codex/routing.ts +1888 -0
  306. package/src/codex/runtime.ts +659 -0
  307. package/src/codex/shim.ts +2170 -0
  308. package/src/codex/subagent-defaults.ts +550 -0
  309. package/src/codex/subagent-model-fallback.ts +784 -0
  310. package/src/codex/sync.ts +319 -0
  311. package/src/codex/transition-state.ts +612 -0
  312. package/src/codex/upstream-host-health.ts +368 -0
  313. package/src/codex/user-identity.ts +557 -0
  314. package/src/codex/warmup.ts +298 -0
  315. package/src/codex/websocket-registry.ts +100 -0
  316. package/src/codex/write-coordination.ts +114 -0
  317. package/src/combos/failover.ts +160 -0
  318. package/src/combos/index.ts +45 -0
  319. package/src/combos/request.ts +94 -0
  320. package/src/combos/resolve.ts +232 -0
  321. package/src/combos/types.ts +398 -0
  322. package/src/config/provider-name.ts +24 -0
  323. package/src/config.ts +4041 -0
  324. package/src/fork/register.ts +3 -0
  325. package/src/generated/compatibility-version.json +3116 -0
  326. package/src/generated/model-metadata.ts +106 -0
  327. package/src/github/star-state.ts +203 -0
  328. package/src/grok/inject.ts +530 -0
  329. package/src/grok/inspect.ts +45 -0
  330. package/src/grok/status.ts +121 -0
  331. package/src/grok/sync.ts +66 -0
  332. package/src/images/artifacts.ts +516 -0
  333. package/src/images/fulfill-video.ts +163 -0
  334. package/src/images/fulfill.ts +149 -0
  335. package/src/images/index.ts +4 -0
  336. package/src/images/loop.ts +955 -0
  337. package/src/images/plan.ts +143 -0
  338. package/src/images/synthetic-tool.ts +133 -0
  339. package/src/images/types.ts +41 -0
  340. package/src/images/xai-client.ts +141 -0
  341. package/src/images/xai-video-client.ts +163 -0
  342. package/src/index.ts +22 -0
  343. package/src/integrations/config-io.ts +269 -0
  344. package/src/integrations/journal.ts +315 -0
  345. package/src/integrations/merge.ts +135 -0
  346. package/src/integrations/mutation-flight.ts +71 -0
  347. package/src/integrations/native/ownership-preflight.ts +202 -0
  348. package/src/integrations/omp-yaml-source.ts +358 -0
  349. package/src/integrations/owned-refresh.ts +74 -0
  350. package/src/integrations/ownership.ts +111 -0
  351. package/src/integrations/registry.ts +159 -0
  352. package/src/integrations/serialize.ts +314 -0
  353. package/src/integrations/state.ts +361 -0
  354. package/src/integrations/store.ts +103 -0
  355. package/src/integrations/writer-lock.ts +98 -0
  356. package/src/integrations/writer.ts +691 -0
  357. package/src/lab/artifacts/sanitize.ts +586 -0
  358. package/src/lab/artifacts/secure-fs.ts +475 -0
  359. package/src/lab/artifacts/store.ts +310 -0
  360. package/src/lab/automation/budgets.ts +78 -0
  361. package/src/lab/automation/config-persistence.ts +256 -0
  362. package/src/lab/automation/constants.ts +39 -0
  363. package/src/lab/automation/cooldown.ts +103 -0
  364. package/src/lab/automation/dispatch.ts +211 -0
  365. package/src/lab/automation/index.ts +13 -0
  366. package/src/lab/automation/orchestrator.ts +499 -0
  367. package/src/lab/automation/persistence.ts +512 -0
  368. package/src/lab/automation/planner.ts +371 -0
  369. package/src/lab/automation/policy.ts +136 -0
  370. package/src/lab/automation/queue.ts +191 -0
  371. package/src/lab/automation/recovery.ts +24 -0
  372. package/src/lab/automation/route-context.ts +21 -0
  373. package/src/lab/automation/run-key.ts +44 -0
  374. package/src/lab/automation/runs-query.ts +34 -0
  375. package/src/lab/automation/types.ts +160 -0
  376. package/src/lab/conformance/assertion.ts +325 -0
  377. package/src/lab/conformance/digest.ts +22 -0
  378. package/src/lab/conformance/executor.ts +741 -0
  379. package/src/lab/conformance/fixture-provider.ts +27 -0
  380. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  381. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  382. package/src/lab/conformance/harness-budget.ts +47 -0
  383. package/src/lab/conformance/index.ts +5 -0
  384. package/src/lab/conformance/jcs.ts +64 -0
  385. package/src/lab/conformance/json-pointer.ts +39 -0
  386. package/src/lab/conformance/manifest.ts +180 -0
  387. package/src/lab/conformance/mcp-stub.ts +179 -0
  388. package/src/lab/conformance/negative-controls.ts +164 -0
  389. package/src/lab/conformance/observation.ts +355 -0
  390. package/src/lab/conformance/runner.ts +68 -0
  391. package/src/lab/conformance/sse-normalize.ts +59 -0
  392. package/src/lab/conformance/suite-manifest.ts +78 -0
  393. package/src/lab/conformance/types.ts +214 -0
  394. package/src/lab/constants.ts +126 -0
  395. package/src/lab/digest.ts +64 -0
  396. package/src/lab/events/errors.ts +9 -0
  397. package/src/lab/events/limits.ts +117 -0
  398. package/src/lab/events/types.ts +229 -0
  399. package/src/lab/events/validate.ts +781 -0
  400. package/src/lab/fabric/constants.ts +40 -0
  401. package/src/lab/fabric/executor.ts +492 -0
  402. package/src/lab/fabric/index.ts +80 -0
  403. package/src/lab/fabric/manifest.ts +222 -0
  404. package/src/lab/fabric/observe.ts +489 -0
  405. package/src/lab/fabric/patch.ts +79 -0
  406. package/src/lab/fabric/producer-child.ts +139 -0
  407. package/src/lab/fabric/producer-isolate.ts +276 -0
  408. package/src/lab/fabric/producer-protocol.ts +61 -0
  409. package/src/lab/fabric/scratch.ts +439 -0
  410. package/src/lab/fabric/subject.ts +106 -0
  411. package/src/lab/fabric/types.ts +134 -0
  412. package/src/lab/fabric/verifier.ts +98 -0
  413. package/src/lab/index.ts +54 -0
  414. package/src/lab/ledger/artifact-refs.ts +127 -0
  415. package/src/lab/ledger/invalidation.ts +136 -0
  416. package/src/lab/ledger/purge.ts +310 -0
  417. package/src/lab/ledger/store.ts +532 -0
  418. package/src/lab/live/credential-lease.ts +53 -0
  419. package/src/lab/live/destination.ts +155 -0
  420. package/src/lab/live/executor.ts +336 -0
  421. package/src/lab/live/inert-tools.ts +56 -0
  422. package/src/lab/live/manifest.ts +85 -0
  423. package/src/lab/live/mcp-loopback.ts +57 -0
  424. package/src/lab/live/runner.ts +19 -0
  425. package/src/lab/live/sandbox.ts +61 -0
  426. package/src/lab/live/suite-manifest.ts +41 -0
  427. package/src/lab/live/transport.ts +118 -0
  428. package/src/lab/live/types.ts +197 -0
  429. package/src/lab/observe/from-conformance.ts +301 -0
  430. package/src/lab/observe/from-live.ts +117 -0
  431. package/src/lab/paths.ts +153 -0
  432. package/src/lab/projection/rebuild.ts +495 -0
  433. package/src/lab/projection/schema.ts +135 -0
  434. package/src/lab/projection/verdicts.ts +474 -0
  435. package/src/lab/projection/verification.ts +412 -0
  436. package/src/lab/public/bundle.ts +217 -0
  437. package/src/lab/public/community-authority.ts +175 -0
  438. package/src/lab/public/community-files.ts +29 -0
  439. package/src/lab/public/community.ts +479 -0
  440. package/src/lab/public/file-safety.ts +155 -0
  441. package/src/lab/public/ids.ts +26 -0
  442. package/src/lab/public/index.ts +16 -0
  443. package/src/lab/public/mutation-lock.ts +424 -0
  444. package/src/lab/public/operator.ts +353 -0
  445. package/src/lab/public/origin-purge.ts +79 -0
  446. package/src/lab/public/origin.ts +203 -0
  447. package/src/lab/public/privacy.ts +143 -0
  448. package/src/lab/public/private-file.ts +261 -0
  449. package/src/lab/public/project.ts +124 -0
  450. package/src/lab/public/purge-test-fault.ts +21 -0
  451. package/src/lab/public/purge.ts +223 -0
  452. package/src/lab/public/registry.ts +44 -0
  453. package/src/lab/public/revocation.ts +252 -0
  454. package/src/lab/public/signature.ts +243 -0
  455. package/src/lab/public/storage.ts +105 -0
  456. package/src/lab/public/strict-json.ts +206 -0
  457. package/src/lab/public/time.ts +26 -0
  458. package/src/lab/public/types.ts +172 -0
  459. package/src/lab/public/validate.ts +391 -0
  460. package/src/lab/query/catalog.ts +101 -0
  461. package/src/lab/query/connection.ts +107 -0
  462. package/src/lab/query/constants.ts +4 -0
  463. package/src/lab/query/cursor.ts +132 -0
  464. package/src/lab/query/dto-map.ts +277 -0
  465. package/src/lab/query/errors.ts +22 -0
  466. package/src/lab/query/freshness.ts +53 -0
  467. package/src/lab/query/index.ts +45 -0
  468. package/src/lab/query/latest-observation.ts +59 -0
  469. package/src/lab/query/passive-production.ts +159 -0
  470. package/src/lab/query/queries.ts +444 -0
  471. package/src/lab/query/types.ts +266 -0
  472. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  473. package/src/lab/subject/installation-salt.ts +112 -0
  474. package/src/lab/subject/protocol-subject.ts +80 -0
  475. package/src/lab/subject/route-subject.ts +74 -0
  476. package/src/lib/abort.ts +146 -0
  477. package/src/lib/admin-secrets.ts +25 -0
  478. package/src/lib/admission.ts +83 -0
  479. package/src/lib/app-owned-memory-stores.ts +195 -0
  480. package/src/lib/app-owned-memory.ts +265 -0
  481. package/src/lib/bounded-body.ts +346 -0
  482. package/src/lib/bun-binary-validator.d.mts +3 -0
  483. package/src/lib/bun-binary-validator.mjs +18 -0
  484. package/src/lib/bun-runtime.ts +184 -0
  485. package/src/lib/bun-stream-caps.ts +130 -0
  486. package/src/lib/codex-restart-contract.ts +120 -0
  487. package/src/lib/config-ownership.ts +364 -0
  488. package/src/lib/crash-guard.ts +344 -0
  489. package/src/lib/debug-log-buffer.ts +83 -0
  490. package/src/lib/debug-settings.ts +108 -0
  491. package/src/lib/debug.ts +31 -0
  492. package/src/lib/destination-policy.ts +380 -0
  493. package/src/lib/errors.ts +406 -0
  494. package/src/lib/eventstream-decoder.ts +253 -0
  495. package/src/lib/fabric-task-execution-authority.ts +7 -0
  496. package/src/lib/fabric-task-host.ts +29 -0
  497. package/src/lib/gcp-adc.ts +341 -0
  498. package/src/lib/injection-debug-log.ts +58 -0
  499. package/src/lib/lab-activation.ts +223 -0
  500. package/src/lib/lab-live-execution-authority.ts +13 -0
  501. package/src/lib/lab-live-host.ts +30 -0
  502. package/src/lib/lab-live-pinned-sender.ts +56 -0
  503. package/src/lib/lab-live-route-production.ts +130 -0
  504. package/src/lib/lab-passive-linker-registration.ts +26 -0
  505. package/src/lib/local-management-attestation.ts +51 -0
  506. package/src/lib/local-management-capability.ts +100 -0
  507. package/src/lib/local-provider-reload-contract.ts +100 -0
  508. package/src/lib/open-url.ts +25 -0
  509. package/src/lib/optional-shutdown-hooks.ts +57 -0
  510. package/src/lib/pinned-http.ts +270 -0
  511. package/src/lib/privacy.ts +20 -0
  512. package/src/lib/process-control.ts +168 -0
  513. package/src/lib/provider-outbound.ts +210 -0
  514. package/src/lib/provider-url.ts +14 -0
  515. package/src/lib/proxy-env.ts +18 -0
  516. package/src/lib/redact.ts +521 -0
  517. package/src/lib/retry-after.ts +55 -0
  518. package/src/lib/self-launch-argv.ts +15 -0
  519. package/src/lib/server-resource-ownership.ts +71 -0
  520. package/src/lib/service-secrets.ts +25 -0
  521. package/src/lib/shadow-call.ts +61 -0
  522. package/src/lib/sidecar-tracker.ts +52 -0
  523. package/src/lib/sse-decoder.ts +364 -0
  524. package/src/lib/state-store-registrations.ts +119 -0
  525. package/src/lib/state-store-sweeper.ts +184 -0
  526. package/src/lib/system-restart-contract.ts +73 -0
  527. package/src/lib/test-home-guard.ts +90 -0
  528. package/src/lib/token-estimate.ts +86 -0
  529. package/src/lib/tool-argument-integers.ts +202 -0
  530. package/src/lib/translator-budget.ts +400 -0
  531. package/src/lib/upstream-http-version.ts +57 -0
  532. package/src/lib/upstream-reachability.ts +95 -0
  533. package/src/lib/upstream-retry.ts +392 -0
  534. package/src/lib/win-exec.ts +115 -0
  535. package/src/lib/win-paths.ts +68 -0
  536. package/src/lib/windows-atomic-replace.ts +156 -0
  537. package/src/lib/windows-elevation.ts +773 -0
  538. package/src/lib/windows-secret-acl.ts +854 -0
  539. package/src/lib/windows-service-wrappers.ts +72 -0
  540. package/src/lib/windows-text.ts +106 -0
  541. package/src/lib/windows-user-principal.ts +341 -0
  542. package/src/lib/winsw.ts +403 -0
  543. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  544. package/src/oauth/account-import/index.ts +15 -0
  545. package/src/oauth/account-import/parser.ts +83 -0
  546. package/src/oauth/account-import/registry.ts +18 -0
  547. package/src/oauth/account-import/service.ts +75 -0
  548. package/src/oauth/account-import/types.ts +91 -0
  549. package/src/oauth/anthropic-routing.ts +594 -0
  550. package/src/oauth/anthropic.ts +188 -0
  551. package/src/oauth/antigravity-routing.ts +151 -0
  552. package/src/oauth/callback-server.ts +300 -0
  553. package/src/oauth/chatgpt.ts +161 -0
  554. package/src/oauth/command-code.ts +239 -0
  555. package/src/oauth/cursor.ts +252 -0
  556. package/src/oauth/github-copilot.ts +428 -0
  557. package/src/oauth/google-antigravity.ts +262 -0
  558. package/src/oauth/health.ts +407 -0
  559. package/src/oauth/index.ts +1504 -0
  560. package/src/oauth/key-providers.ts +124 -0
  561. package/src/oauth/kimi.ts +227 -0
  562. package/src/oauth/kiro-credentials.ts +726 -0
  563. package/src/oauth/kiro.ts +621 -0
  564. package/src/oauth/local-token-detect.ts +130 -0
  565. package/src/oauth/log.ts +50 -0
  566. package/src/oauth/login-cli.ts +223 -0
  567. package/src/oauth/nous.ts +798 -0
  568. package/src/oauth/pkce.ts +15 -0
  569. package/src/oauth/store.ts +728 -0
  570. package/src/oauth/token-guardian.ts +309 -0
  571. package/src/oauth/types.ts +62 -0
  572. package/src/oauth/xai.ts +241 -0
  573. package/src/providers/alibaba-region-backup.ts +75 -0
  574. package/src/providers/alibaba-region-migration.ts +156 -0
  575. package/src/providers/alibaba-region-startup.ts +36 -0
  576. package/src/providers/antigravity-models.ts +695 -0
  577. package/src/providers/antigravity-quota.ts +216 -0
  578. package/src/providers/api-keys.ts +140 -0
  579. package/src/providers/base-url-choices.ts +74 -0
  580. package/src/providers/codex-capacity.ts +292 -0
  581. package/src/providers/command-code-efforts.ts +144 -0
  582. package/src/providers/context-cap.ts +82 -0
  583. package/src/providers/cursor-pool.ts +72 -0
  584. package/src/providers/derive.ts +586 -0
  585. package/src/providers/fastwire.ts +501 -0
  586. package/src/providers/free-directory.ts +187 -0
  587. package/src/providers/github-copilot-transport.ts +56 -0
  588. package/src/providers/google-vertex-location.ts +14 -0
  589. package/src/providers/key-failover.ts +271 -0
  590. package/src/providers/kiro-models.ts +67 -0
  591. package/src/providers/label.ts +19 -0
  592. package/src/providers/model-discovery-limits.ts +16 -0
  593. package/src/providers/model-discovery.ts +449 -0
  594. package/src/providers/model-rename-migration.ts +255 -0
  595. package/src/providers/model-rename-startup.ts +28 -0
  596. package/src/providers/openai-sidecar.ts +243 -0
  597. package/src/providers/openai-tier-startup.ts +56 -0
  598. package/src/providers/openai-tiers.ts +423 -0
  599. package/src/providers/openai-virtual-models.ts +83 -0
  600. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  601. package/src/providers/openrouter-routing.ts +102 -0
  602. package/src/providers/provider-id-rewrite.ts +185 -0
  603. package/src/providers/quota.ts +2345 -0
  604. package/src/providers/registry.ts +2918 -0
  605. package/src/providers/replit/constants.ts +27 -0
  606. package/src/providers/replit/derive.ts +85 -0
  607. package/src/providers/replit/headers.ts +28 -0
  608. package/src/providers/replit/origin.ts +55 -0
  609. package/src/providers/replit/pair-install-response.ts +72 -0
  610. package/src/providers/replit/probe.ts +199 -0
  611. package/src/providers/replit/setup.ts +350 -0
  612. package/src/providers/request-pacing.ts +310 -0
  613. package/src/providers/service-tier.ts +277 -0
  614. package/src/providers/slug-codec.ts +103 -0
  615. package/src/providers/static-model-discovery.ts +86 -0
  616. package/src/providers/xai-responses-opt-in.ts +15 -0
  617. package/src/providers/xai-transport.ts +148 -0
  618. package/src/reasoning-effort.ts +183 -0
  619. package/src/responses/compaction.ts +142 -0
  620. package/src/responses/custom-tool-compat.ts +266 -0
  621. package/src/responses/hosted-tool-policy.ts +9 -0
  622. package/src/responses/namespace-tool-compat.ts +355 -0
  623. package/src/responses/parser.ts +838 -0
  624. package/src/responses/provider-continuation.ts +98 -0
  625. package/src/responses/provider-opaque-metadata.ts +73 -0
  626. package/src/responses/reasoning-envelope.ts +60 -0
  627. package/src/responses/reasoning-replay-cache.ts +426 -0
  628. package/src/responses/schema.ts +165 -0
  629. package/src/responses/spill-store.ts +459 -0
  630. package/src/responses/state.ts +1433 -0
  631. package/src/responses/thought-signature-replay.ts +347 -0
  632. package/src/responses/tool-groups.ts +19 -0
  633. package/src/responses/tool-search-compat.ts +301 -0
  634. package/src/responses/truncated-stop-reason.ts +60 -0
  635. package/src/router.ts +761 -0
  636. package/src/routing/analytics.ts +378 -0
  637. package/src/routing/capability.ts +244 -0
  638. package/src/routing/compatibility/assemble.ts +73 -0
  639. package/src/routing/compatibility/behavior.ts +278 -0
  640. package/src/routing/compatibility/catalog.ts +99 -0
  641. package/src/routing/compatibility/endpoint.ts +52 -0
  642. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  643. package/src/routing/compatibility/policy.ts +181 -0
  644. package/src/routing/compatibility/provider-slot.ts +56 -0
  645. package/src/routing/compatibility/reader.ts +110 -0
  646. package/src/routing/compatibility/subject.ts +191 -0
  647. package/src/routing/compatibility/types.ts +64 -0
  648. package/src/routing/compatibility/version.ts +104 -0
  649. package/src/routing/cost.ts +77 -0
  650. package/src/routing/evaluator.ts +495 -0
  651. package/src/routing/health.ts +412 -0
  652. package/src/routing/history/cursor.ts +43 -0
  653. package/src/routing/history/indexer.ts +605 -0
  654. package/src/routing/history/schema.ts +72 -0
  655. package/src/routing/profile-namespace.ts +15 -0
  656. package/src/routing/profile.ts +547 -0
  657. package/src/routing/quota.ts +145 -0
  658. package/src/routing/request-evidence.ts +45 -0
  659. package/src/routing/trace.ts +776 -0
  660. package/src/server/adapter-resolve.ts +53 -0
  661. package/src/server/auth-cors.ts +751 -0
  662. package/src/server/background-lifecycle.ts +182 -0
  663. package/src/server/chat-completions.ts +442 -0
  664. package/src/server/chat-native-sse.ts +331 -0
  665. package/src/server/chat-native.ts +426 -0
  666. package/src/server/claude-messages.ts +1030 -0
  667. package/src/server/direct-local-http.ts +347 -0
  668. package/src/server/effort-policy.ts +190 -0
  669. package/src/server/github-copilot-responses-repair.ts +338 -0
  670. package/src/server/gui-static.ts +152 -0
  671. package/src/server/image-retry.ts +42 -0
  672. package/src/server/images.ts +568 -0
  673. package/src/server/index.ts +1813 -0
  674. package/src/server/lifecycle.ts +498 -0
  675. package/src/server/live.ts +717 -0
  676. package/src/server/local-management-read-client.ts +90 -0
  677. package/src/server/local-provider-reload-client.ts +137 -0
  678. package/src/server/management/agent-settings-routes.ts +1433 -0
  679. package/src/server/management/api-access.ts +141 -0
  680. package/src/server/management/api-key-usage.ts +193 -0
  681. package/src/server/management/body.ts +41 -0
  682. package/src/server/management/combo-routes.ts +263 -0
  683. package/src/server/management/config-routes.ts +835 -0
  684. package/src/server/management/context.ts +113 -0
  685. package/src/server/management/integration-routes.ts +498 -0
  686. package/src/server/management/lab-automation-routes.ts +206 -0
  687. package/src/server/management/lab-routes.ts +563 -0
  688. package/src/server/management/logs-usage-routes.ts +586 -0
  689. package/src/server/management/model-routes.ts +560 -0
  690. package/src/server/management/model-rows.ts +163 -0
  691. package/src/server/management/native-integration-routes.ts +769 -0
  692. package/src/server/management/oauth-account-routes.ts +637 -0
  693. package/src/server/management/provider-capability-config.ts +48 -0
  694. package/src/server/management/provider-routes.ts +1033 -0
  695. package/src/server/management/replit-provider-routes.ts +86 -0
  696. package/src/server/management/request-history-routes.ts +191 -0
  697. package/src/server/management/routing-analytics-routes.ts +74 -0
  698. package/src/server/management/routing-profile-routes.ts +385 -0
  699. package/src/server/management/shared.ts +286 -0
  700. package/src/server/management/sidebar-routes.ts +106 -0
  701. package/src/server/management/storage-log-guard-routes.ts +186 -0
  702. package/src/server/management/sync-response.ts +69 -0
  703. package/src/server/management/system-restart.ts +435 -0
  704. package/src/server/management/system-routes.ts +194 -0
  705. package/src/server/management/usage-summary-cache.ts +94 -0
  706. package/src/server/management/vision-sidecar-options.ts +167 -0
  707. package/src/server/management/web-search-sidecar-options.ts +120 -0
  708. package/src/server/management-api.ts +314 -0
  709. package/src/server/management-auth.ts +482 -0
  710. package/src/server/memory-watchdog.ts +156 -0
  711. package/src/server/passive-route-linker.ts +66 -0
  712. package/src/server/port-reclaim.ts +307 -0
  713. package/src/server/ports.ts +156 -0
  714. package/src/server/proxy-liveness.ts +328 -0
  715. package/src/server/readiness.ts +99 -0
  716. package/src/server/relay-eager.ts +353 -0
  717. package/src/server/relay.ts +1209 -0
  718. package/src/server/request-decompress.ts +239 -0
  719. package/src/server/request-log-conversation.ts +168 -0
  720. package/src/server/request-log.ts +1259 -0
  721. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  722. package/src/server/responses/agent-task-recovery.ts +465 -0
  723. package/src/server/responses/collaboration.ts +551 -0
  724. package/src/server/responses/compact.ts +771 -0
  725. package/src/server/responses/core.ts +5389 -0
  726. package/src/server/responses/empty-completion-guard.ts +276 -0
  727. package/src/server/responses/encrypted-payload.ts +331 -0
  728. package/src/server/responses/fetch-helpers.ts +232 -0
  729. package/src/server/responses/input-admission.ts +185 -0
  730. package/src/server/responses/pacing-overload.ts +13 -0
  731. package/src/server/responses/passthrough-error.ts +78 -0
  732. package/src/server/responses/policy-fallback.ts +178 -0
  733. package/src/server/responses/responses-field-backfill.ts +251 -0
  734. package/src/server/responses/terminal-guard.ts +251 -0
  735. package/src/server/responses/upstream-error.ts +53 -0
  736. package/src/server/responses/ws-upstream.ts +308 -0
  737. package/src/server/responses-custom-tool-repair.ts +282 -0
  738. package/src/server/responses-image-gen-repair.ts +132 -0
  739. package/src/server/responses-item-id-repair.ts +272 -0
  740. package/src/server/responses-json-events.ts +90 -0
  741. package/src/server/responses-model-rewrite.ts +29 -0
  742. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  743. package/src/server/responses-snapshot-repair.ts +621 -0
  744. package/src/server/responses-terminal-repair.ts +342 -0
  745. package/src/server/responses-tool-search-repair.ts +267 -0
  746. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  747. package/src/server/responses.ts +25 -0
  748. package/src/server/search.ts +201 -0
  749. package/src/server/sse-frame-buffer.ts +292 -0
  750. package/src/server/sse-payload-rewrite.ts +263 -0
  751. package/src/server/startup-action-control.ts +315 -0
  752. package/src/server/startup-health-cache.ts +131 -0
  753. package/src/server/system-env.ts +484 -0
  754. package/src/server/windows-tcp-drop.ts +184 -0
  755. package/src/server/windows-tray-control.ts +41 -0
  756. package/src/server/ws-bridge.ts +472 -0
  757. package/src/service-manager-probe.ts +892 -0
  758. package/src/service.ts +3575 -0
  759. package/src/sidecar/auth.ts +92 -0
  760. package/src/sidecar/candidates.ts +83 -0
  761. package/src/stall-timeout.ts +20 -0
  762. package/src/storage/cleanup-job.ts +57 -0
  763. package/src/storage/cleanup.ts +3085 -0
  764. package/src/storage/policy-job.ts +457 -0
  765. package/src/storage/policy-scheduler.ts +40 -0
  766. package/src/storage/policy-worker.ts +59 -0
  767. package/src/storage/policy.ts +527 -0
  768. package/src/storage/restore-job.ts +299 -0
  769. package/src/storage/restore-worker.ts +58 -0
  770. package/src/storage/scanner.ts +238 -0
  771. package/src/storage/storage-mutation-coordinator.ts +139 -0
  772. package/src/storage/worker-lifecycle.ts +215 -0
  773. package/src/tray/assets/opencodex-tray-offline.ico +0 -0
  774. package/src/tray/assets/opencodex-tray-online.ico +0 -0
  775. package/src/tray/assets/opencodex-tray-warning.ico +0 -0
  776. package/src/tray/assets/opencodex-tray.png +0 -0
  777. package/src/tray/windows-tray.ps1 +364 -0
  778. package/src/tray/windows.ts +757 -0
  779. package/src/types/accounts.ts +37 -0
  780. package/src/types/config.ts +876 -0
  781. package/src/types/provider.ts +545 -0
  782. package/src/types/request.ts +384 -0
  783. package/src/types/tools.ts +131 -0
  784. package/src/types/wire.ts +80 -0
  785. package/src/types.ts +106 -0
  786. package/src/update/badge.ts +72 -0
  787. package/src/update/index.ts +415 -0
  788. package/src/update/job.ts +1887 -0
  789. package/src/update/notify.ts +263 -0
  790. package/src/update/npm-cache-preflight.d.mts +47 -0
  791. package/src/update/npm-cache-preflight.mjs +201 -0
  792. package/src/update/npm-invocation.d.mts +23 -0
  793. package/src/update/npm-invocation.mjs +94 -0
  794. package/src/update/transactional-install.d.mts +22 -0
  795. package/src/update/transactional-install.mjs +259 -0
  796. package/src/update/tray-update-plan.d.mts +18 -0
  797. package/src/update/tray-update-plan.mjs +38 -0
  798. package/src/usage/cost.ts +625 -0
  799. package/src/usage/debug.ts +97 -0
  800. package/src/usage/expected-prices.ts +416 -0
  801. package/src/usage/log.ts +1223 -0
  802. package/src/usage/summary.ts +753 -0
  803. package/src/usage/totals.ts +14 -0
  804. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  805. package/src/usage/user-cost-overlays.ts +314 -0
  806. package/src/vision/anthropic-describe.ts +189 -0
  807. package/src/vision/backends.ts +97 -0
  808. package/src/vision/describe.ts +131 -0
  809. package/src/vision/eligibility.ts +250 -0
  810. package/src/vision/index.ts +681 -0
  811. package/src/vision/reasoning.ts +55 -0
  812. package/src/vision/routed-describe.ts +175 -0
  813. package/src/vision/timeout-bounds.ts +9 -0
  814. package/src/web-search/anthropic-executor.ts +195 -0
  815. package/src/web-search/backends.ts +108 -0
  816. package/src/web-search/exa-executor.ts +88 -0
  817. package/src/web-search/executor.ts +113 -0
  818. package/src/web-search/format-result.ts +89 -0
  819. package/src/web-search/gemini-executor.ts +141 -0
  820. package/src/web-search/index.ts +331 -0
  821. package/src/web-search/loop.ts +896 -0
  822. package/src/web-search/parse.ts +315 -0
  823. package/src/web-search/progress-stream.ts +342 -0
  824. package/src/web-search/sources.ts +60 -0
  825. package/src/web-search/synthetic-tool.ts +47 -0
  826. package/src/web-search/xai-executor.ts +219 -0
@@ -0,0 +1,573 @@
1
+ import { existsSync, readFileSync, unlinkSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { atomicWriteFile, getConfigDir } from "../config";
4
+ import { captureConfigGeneration, type GenerationContext } from "../lib/state-store-sweeper";
5
+ import { isThirtyDayOnlyCodexPlan } from "./plan";
6
+
7
+ export type StoredAccountQuota = {
8
+ weeklyPercent?: number;
9
+ monthlyPercent?: number;
10
+ weeklyResetAt?: number;
11
+ monthlyResetAt?: number;
12
+ /**
13
+ * A sub-day burst window, when upstream declares one (#1791).
14
+ *
15
+ * K12 and similar plans enforce a rolling 5-hour limit ALONGSIDE the weekly one.
16
+ * Not folding it into `weeklyPercent` stopped the mislabeling, but dropping it
17
+ * entirely hides a limit that genuinely blocks the account: a 429 at 100% here is
18
+ * real even while the weekly quota is untouched.
19
+ *
20
+ * `shortWindowSeconds` is retained because the duration is the only thing that makes
21
+ * this window self-describing; the slot it arrived in is not stable across plans.
22
+ */
23
+ shortPercent?: number;
24
+ shortResetAt?: number;
25
+ shortWindowSeconds?: number;
26
+ resetCredits?: number;
27
+ /**
28
+ * True when `monthlyPercent` came from an explicitly-monthly PRIMARY window —
29
+ * i.e. it is the account's governing quota reading, not a supplementary
30
+ * tertiary window. Tertiary-only monthly data lands in the same field but says
31
+ * nothing about the weekly quota that actually gates a non-Go/Free account,
32
+ * so recovery must be able to tell the two apart (#967 audit).
33
+ */
34
+ monthlyIsPrimaryWindow?: boolean;
35
+ updatedAt: number;
36
+ };
37
+
38
+ /** Disk snapshot under OPENCODEX_HOME — usage percents only (no emails/tokens). */
39
+ const QUOTA_CACHE_FILENAME = "codex-quota-cache.json";
40
+ /** Keep last-known bars across restarts; WHAM still refreshes on TTL in live/prime paths. */
41
+ const QUOTA_DISK_MAX_AGE_MS = 6 * 60 * 60_000;
42
+ const QUOTA_PERSIST_DEBOUNCE_MS = 250;
43
+
44
+ type QuotaDiskFile = {
45
+ version: 1;
46
+ quotas: Record<string, StoredAccountQuota>;
47
+ };
48
+
49
+ let diskHydrated = false;
50
+ let persistTimer: ReturnType<typeof setTimeout> | null = null;
51
+
52
+ export type WhamUsageResponse = {
53
+ email?: string | null;
54
+ plan_type?: unknown;
55
+ rate_limit?: {
56
+ // Live WHAM payloads send explicit nulls for absent windows (issue #315 repro).
57
+ primary_window?: WhamUsageWindow | null;
58
+ secondary_window?: WhamUsageWindow | null;
59
+ tertiary_window?: WhamUsageWindow | null;
60
+ };
61
+ rate_limit_reset_credits?: {
62
+ available_count: number;
63
+ } | null;
64
+ };
65
+
66
+ type WhamUsageWindow = {
67
+ used_percent?: number;
68
+ reset_at?: number;
69
+ limit_window_seconds?: number;
70
+ };
71
+
72
+ const MONTHLY_WINDOW_MIN_SECONDS = 28 * 24 * 60 * 60;
73
+ /**
74
+ * Shortest window still plausibly the WEEKLY quota (#1791).
75
+ *
76
+ * K12 and similar plans send a 5-hour primary window plus a 7-day secondary. Folding the
77
+ * primary into `weeklyPercent` reported the 5-hour bar as the weekly one and discarded the
78
+ * real weekly reading entirely, so the dashboard showed a window that reset every few hours
79
+ * and routing never saw the limit that actually gates the account.
80
+ *
81
+ * 24h is the discriminator: anything shorter is a burst window, not a weekly one. A window
82
+ * with no declared duration is unchanged, because older payloads omit `limit_window_seconds`
83
+ * and guessing there would break every legacy account.
84
+ */
85
+ const WEEKLY_WINDOW_MIN_SECONDS = 24 * 60 * 60;
86
+ const MONTHLY_WINDOW_MIN_MINUTES = MONTHLY_WINDOW_MIN_SECONDS / 60;
87
+
88
+ const accountQuota = new Map<string, StoredAccountQuota>();
89
+ let lastReconciledGeneration = 0;
90
+ let liveAccountIds = new Set<string>();
91
+
92
+ function mayCommitAccountQuota(accountId: string, writerGeneration: number): boolean {
93
+ return writerGeneration >= lastReconciledGeneration || liveAccountIds.has(accountId);
94
+ }
95
+
96
+ // Valid upstream percentages are normalized to 0..100. Keep "unknown" outside that domain so an
97
+ // actually exhausted account is still eligible for threshold rotation.
98
+ export const CODEX_UNKNOWN_USAGE_SCORE = 101;
99
+ export const CODEX_EXHAUSTED_USAGE_PERCENT = 100;
100
+
101
+ export function isCodexQuotaExhausted(
102
+ quota: Pick<StoredAccountQuota, "weeklyPercent" | "monthlyPercent" | "shortPercent"> | null,
103
+ plan?: unknown,
104
+ ): boolean {
105
+ if (!quota) return false;
106
+ // The burst window counts on EVERY plan. It is upstream-enforced independently, so an
107
+ // account at 100% there is blocked regardless of which longer window governs its plan;
108
+ // omitting it would route traffic straight into a 429 (#1791).
109
+ const values = codexQuotaWindowForPlan(plan) === "monthly"
110
+ ? [quota.monthlyPercent, quota.shortPercent]
111
+ : [quota.weeklyPercent, quota.monthlyPercent, quota.shortPercent];
112
+ return values.some(value => typeof value === "number"
113
+ && Number.isFinite(value)
114
+ && value >= CODEX_EXHAUSTED_USAGE_PERCENT);
115
+ }
116
+
117
+ /**
118
+ * Which usage window a plan reports in. This is the SINGLE rule shared by quota
119
+ * parsing, exhaustion, and recovery — they must not diverge.
120
+ *
121
+ * An allowlist of "known" plans was tried here and was wrong: the upstream model
122
+ * snapshot alone carries 21 distinct plan strings (`edu_plus`, `finserv`, `k12`,
123
+ * `quorum`, `self_serve_business_usage_based`, ...), and `CodexAccount.plan` is an
124
+ * unrestricted string, so any list is a list of the plans someone remembered.
125
+ * Twelve real plans would have been refused recovery and stayed cooled forever —
126
+ * the very defect this unit exists to fix, reintroduced as a typo-shaped hole.
127
+ *
128
+ * The honest rule is the parser's own: Go and Free report a 30-day window,
129
+ * everything else (including an absent plan) reports weekly. Recovery reads the
130
+ * window the parser actually wrote rather than second-guessing it.
131
+ */
132
+ export function codexQuotaWindowForPlan(plan?: unknown): "monthly" | "weekly" {
133
+ return isThirtyDayOnlyCodexPlan(plan) ? "monthly" : "weekly";
134
+ }
135
+
136
+ export function isCompleteCodexQuotaRecoverySnapshot(
137
+ quota: Pick<StoredAccountQuota, "weeklyPercent" | "monthlyPercent" | "monthlyIsPrimaryWindow" | "shortPercent"> | null,
138
+ plan?: unknown,
139
+ ): boolean {
140
+ if (!quota || isCodexQuotaExhausted(quota, plan)) return false;
141
+ // Recovery still fails closed on MISSING EVIDENCE — a credits-only or windowless payload
142
+ // carries no usage reading at all and must never clear a cooldown. What it does not do is
143
+ // fail closed on an unfamiliar plan NAME, which only ever meant "cooled forever".
144
+ //
145
+ // The parser classifies windows by DURATION, not by plan name: a Team response whose
146
+ // primary window is explicitly monthly parses to monthlyPercent only (no secondary
147
+ // window exists), so requiring weeklyPercent because the plan is not go/free would
148
+ // strand exactly those accounts until their predicted expiry. Accept whichever window(s)
149
+ // the parser actually wrote; Go/Free never carry a weekly value, so monthly-only is
150
+ // required there.
151
+ //
152
+ // Audit correction: "the parser wrote monthlyPercent" is NOT by itself evidence for a
153
+ // weekly-quota plan. A tertiary-only response also writes monthlyPercent, and it says
154
+ // nothing about the weekly quota that actually gates a Team/Plus account — accepting it
155
+ // would clear the cooldown on a reading of a different window. Only an explicitly-monthly
156
+ // PRIMARY window is the governing reading, which is what `monthlyIsPrimaryWindow` records.
157
+ if (codexQuotaWindowForPlan(plan) === "monthly") {
158
+ return finitePercent(quota.monthlyPercent);
159
+ }
160
+ if (finitePercent(quota.weeklyPercent)) return true;
161
+ return quota.monthlyIsPrimaryWindow === true && finitePercent(quota.monthlyPercent);
162
+ }
163
+
164
+ function finitePercent(value: number | undefined): boolean {
165
+ return typeof value === "number" && Number.isFinite(value);
166
+ }
167
+
168
+ export function normalizeUsagePercent(value: unknown): number | undefined {
169
+ const numeric = typeof value === "number"
170
+ ? value
171
+ : typeof value === "string" && value.trim() !== ""
172
+ ? Number(value)
173
+ : undefined;
174
+ if (typeof numeric !== "number" || !Number.isFinite(numeric)) return undefined;
175
+ return Math.max(0, Math.min(100, numeric));
176
+ }
177
+
178
+ function normalizeResetAt(value: unknown): number | undefined {
179
+ const numeric = typeof value === "number"
180
+ ? value
181
+ : typeof value === "string" && value.trim() !== ""
182
+ ? Number(value)
183
+ : undefined;
184
+ if (typeof numeric !== "number" || !Number.isFinite(numeric) || numeric < 0) return undefined;
185
+ return numeric;
186
+ }
187
+
188
+ function hasKnownQuotaValue(quota: Omit<StoredAccountQuota, "updatedAt">): boolean {
189
+ return [quota.weeklyPercent, quota.monthlyPercent, quota.shortPercent]
190
+ .some(value => typeof value === "number" && Number.isFinite(value));
191
+ }
192
+
193
+ /** True only for a window that DECLARES a duration shorter than a day. */
194
+ function isExplicitShortWindow(window: WhamUsageWindow | null | undefined): boolean {
195
+ const seconds = window?.limit_window_seconds;
196
+ return typeof seconds === "number"
197
+ && Number.isFinite(seconds)
198
+ && seconds > 0
199
+ && seconds < WEEKLY_WINDOW_MIN_SECONDS;
200
+ }
201
+
202
+ function isExplicitMonthlyWindow(window: WhamUsageWindow | null | undefined): boolean {
203
+ const seconds = window?.limit_window_seconds;
204
+ return typeof seconds === "number"
205
+ && Number.isFinite(seconds)
206
+ && seconds >= MONTHLY_WINDOW_MIN_SECONDS;
207
+ }
208
+
209
+ function isExplicitMonthlyWindowMinutes(windowMinutes: unknown): boolean {
210
+ const minutes = typeof windowMinutes === "number"
211
+ ? windowMinutes
212
+ : typeof windowMinutes === "string" && windowMinutes.trim() !== ""
213
+ ? Number(windowMinutes)
214
+ : undefined;
215
+ return typeof minutes === "number"
216
+ && Number.isFinite(minutes)
217
+ && minutes >= MONTHLY_WINDOW_MIN_MINUTES;
218
+ }
219
+
220
+
221
+ function snapshotHasWeekly(quota: Omit<StoredAccountQuota, "updatedAt">): boolean {
222
+ return quota.weeklyPercent !== undefined || quota.weeklyResetAt !== undefined;
223
+ }
224
+
225
+ function snapshotHasMonthly(quota: Omit<StoredAccountQuota, "updatedAt">): boolean {
226
+ return quota.monthlyPercent !== undefined || quota.monthlyResetAt !== undefined;
227
+ }
228
+
229
+ function snapshotHasShort(quota: Omit<StoredAccountQuota, "updatedAt">): boolean {
230
+ return quota.shortPercent !== undefined
231
+ || quota.shortResetAt !== undefined
232
+ || quota.shortWindowSeconds !== undefined;
233
+ }
234
+
235
+ function snapshotHasUsage(quota: Omit<StoredAccountQuota, "updatedAt">): boolean {
236
+ return snapshotHasWeekly(quota) || snapshotHasMonthly(quota) || snapshotHasShort(quota);
237
+ }
238
+ export function setAccountQuotaFromParsed(
239
+ accountId: string,
240
+ quota: Omit<StoredAccountQuota, "updatedAt"> | null,
241
+ writerGeneration = captureConfigGeneration(),
242
+ ): void {
243
+ if (!quota) return;
244
+ if (!mayCommitAccountQuota(accountId, writerGeneration)) return;
245
+ const existing = accountQuota.get(accountId);
246
+ const next: StoredAccountQuota = { updatedAt: Date.now() };
247
+ const creditsOnly = quota.resetCredits !== undefined && !snapshotHasUsage(quota);
248
+
249
+ if (creditsOnly) {
250
+ if (existing?.weeklyPercent !== undefined) next.weeklyPercent = existing.weeklyPercent;
251
+ if (existing?.weeklyResetAt !== undefined) next.weeklyResetAt = existing.weeklyResetAt;
252
+ if (existing?.monthlyPercent !== undefined) next.monthlyPercent = existing.monthlyPercent;
253
+ if (existing?.monthlyResetAt !== undefined) next.monthlyResetAt = existing.monthlyResetAt;
254
+ if (existing?.monthlyIsPrimaryWindow === true) next.monthlyIsPrimaryWindow = true;
255
+ if (existing?.shortPercent !== undefined) next.shortPercent = existing.shortPercent;
256
+ if (existing?.shortResetAt !== undefined) next.shortResetAt = existing.shortResetAt;
257
+ if (existing?.shortWindowSeconds !== undefined) next.shortWindowSeconds = existing.shortWindowSeconds;
258
+ next.resetCredits = quota.resetCredits;
259
+ accountQuota.set(accountId, next);
260
+ schedulePersistAccountQuotas();
261
+ return;
262
+ }
263
+
264
+ if (snapshotHasWeekly(quota)) {
265
+ if (quota.weeklyPercent !== undefined) next.weeklyPercent = quota.weeklyPercent;
266
+ if (quota.weeklyResetAt !== undefined) next.weeklyResetAt = quota.weeklyResetAt;
267
+ } else if (snapshotHasMonthly(quota) && !snapshotHasWeekly(quota)) {
268
+ // Monthly-only snapshots intentionally clear stale weekly values (issue #382).
269
+ } else if (existing?.weeklyPercent !== undefined) {
270
+ next.weeklyPercent = existing.weeklyPercent;
271
+ if (existing.weeklyResetAt !== undefined) next.weeklyResetAt = existing.weeklyResetAt;
272
+ }
273
+
274
+ if (snapshotHasMonthly(quota)) {
275
+ if (quota.monthlyPercent !== undefined) next.monthlyPercent = quota.monthlyPercent;
276
+ if (quota.monthlyResetAt !== undefined) next.monthlyResetAt = quota.monthlyResetAt;
277
+ // Carry the provenance with the value it describes. Recovery reads `freshQuota` directly,
278
+ // so this is not on its path today — but a cached snapshot that kept `monthlyPercent`
279
+ // while silently dropping `monthlyIsPrimaryWindow` would look like tertiary-only data to
280
+ // any future reader, and that failure would be invisible.
281
+ if (quota.monthlyIsPrimaryWindow === true) next.monthlyIsPrimaryWindow = true;
282
+ } else if ((snapshotHasWeekly(quota) || snapshotHasShort(quota))
283
+ && existing?.monthlyPercent !== undefined) {
284
+ next.monthlyPercent = existing.monthlyPercent;
285
+ if (existing.monthlyResetAt !== undefined) next.monthlyResetAt = existing.monthlyResetAt;
286
+ if (existing.monthlyIsPrimaryWindow === true) next.monthlyIsPrimaryWindow = true;
287
+ }
288
+
289
+ if (snapshotHasShort(quota)) {
290
+ if (quota.shortPercent !== undefined) next.shortPercent = quota.shortPercent;
291
+ if (quota.shortResetAt !== undefined) next.shortResetAt = quota.shortResetAt;
292
+ if (quota.shortWindowSeconds !== undefined) next.shortWindowSeconds = quota.shortWindowSeconds;
293
+ } else {
294
+ // Header and reset-credit updates are partial snapshots. Preserve the last full WHAM
295
+ // burst tuple when those updates do not carry enough window metadata to replace it.
296
+ if (existing?.shortPercent !== undefined) next.shortPercent = existing.shortPercent;
297
+ if (existing?.shortResetAt !== undefined) next.shortResetAt = existing.shortResetAt;
298
+ if (existing?.shortWindowSeconds !== undefined) next.shortWindowSeconds = existing.shortWindowSeconds;
299
+ }
300
+
301
+ if (quota.resetCredits !== undefined) next.resetCredits = quota.resetCredits;
302
+ else if (existing?.resetCredits !== undefined) next.resetCredits = existing.resetCredits;
303
+
304
+ accountQuota.set(accountId, next);
305
+ schedulePersistAccountQuotas();
306
+ }
307
+
308
+ export function parseUpstreamQuotaHeaders(headers: Headers): Omit<StoredAccountQuota, "updatedAt"> | null {
309
+ const primaryRaw = headers.get("x-codex-primary-used-percent");
310
+ const secondaryRaw = headers.get("x-codex-secondary-used-percent");
311
+ const tertiaryRaw = headers.get("x-codex-tertiary-used-percent");
312
+ const primaryResetRaw = headers.get("x-codex-primary-reset-at");
313
+ const secondaryResetRaw = headers.get("x-codex-secondary-reset-at");
314
+ const tertiaryResetRaw = headers.get("x-codex-tertiary-reset-at");
315
+ const primaryWindowMinutes = headers.get("x-codex-primary-window-minutes");
316
+ const secondaryWindowMinutes = headers.get("x-codex-secondary-window-minutes");
317
+
318
+ const quota: Omit<StoredAccountQuota, "updatedAt"> = {};
319
+ const primaryPercent = normalizeUsagePercent(primaryRaw);
320
+ const secondaryPercent = normalizeUsagePercent(secondaryRaw);
321
+ const tertiaryPercent = normalizeUsagePercent(tertiaryRaw);
322
+ const primaryResetAt = normalizeResetAt(primaryResetRaw);
323
+ const secondaryResetAt = normalizeResetAt(secondaryResetRaw);
324
+ const tertiaryResetAt = normalizeResetAt(tertiaryResetRaw);
325
+ const primaryIsMonthly = primaryRaw !== null && isExplicitMonthlyWindowMinutes(primaryWindowMinutes);
326
+
327
+ if (primaryIsMonthly) {
328
+ if (primaryPercent !== undefined) {
329
+ quota.monthlyPercent = primaryPercent;
330
+ if (primaryResetAt !== undefined) quota.monthlyResetAt = primaryResetAt;
331
+ // Same provenance rule as parseUsageQuota(): this monthly value is the governing
332
+ // primary window, not a supplementary tertiary one. Not on the recovery path today,
333
+ // but the two parsers must agree on what a bare monthlyPercent means.
334
+ quota.monthlyIsPrimaryWindow = true;
335
+ }
336
+ if (secondaryPercent !== undefined) {
337
+ quota.weeklyPercent = secondaryPercent;
338
+ if (secondaryResetAt !== undefined) quota.weeklyResetAt = secondaryResetAt;
339
+ }
340
+ } else {
341
+ const weeklyPercent = primaryPercent ?? secondaryPercent;
342
+ const weeklyResetAt = primaryPercent !== undefined
343
+ ? primaryResetAt
344
+ : secondaryResetAt;
345
+ if (weeklyPercent !== undefined) {
346
+ quota.weeklyPercent = weeklyPercent;
347
+ if (weeklyResetAt !== undefined) quota.weeklyResetAt = weeklyResetAt;
348
+ }
349
+ }
350
+
351
+ if (tertiaryPercent !== undefined && quota.monthlyPercent === undefined) {
352
+ quota.monthlyPercent = tertiaryPercent;
353
+ if (tertiaryResetAt !== undefined) quota.monthlyResetAt = tertiaryResetAt;
354
+ }
355
+
356
+ return hasKnownQuotaValue(quota) ? quota : null;
357
+ }
358
+
359
+ export function applyAccountQuotaFromUpstreamHeaders(
360
+ accountId: string,
361
+ headers: Headers,
362
+ writerGeneration = captureConfigGeneration(),
363
+ ): void {
364
+ const quota = parseUpstreamQuotaHeaders(headers);
365
+ if (!quota) return;
366
+ setAccountQuotaFromParsed(accountId, quota, writerGeneration);
367
+ }
368
+
369
+ export function updateAccountQuota(
370
+ accountId: string,
371
+ weekly: unknown,
372
+ weeklyResetAt?: unknown,
373
+ monthly?: unknown,
374
+ monthlyResetAt?: unknown,
375
+ resetCredits?: number,
376
+ writerGeneration = captureConfigGeneration(),
377
+ ): void {
378
+ if (!mayCommitAccountQuota(accountId, writerGeneration)) return;
379
+ const existing = accountQuota.get(accountId);
380
+ const nextWeekly = normalizeUsagePercent(weekly);
381
+ const nextMonthly = normalizeUsagePercent(monthly);
382
+ if (nextWeekly === undefined && nextMonthly === undefined && resetCredits === undefined) return;
383
+
384
+ const quota: StoredAccountQuota = {
385
+ ...(existing?.weeklyPercent !== undefined ? { weeklyPercent: existing.weeklyPercent } : {}),
386
+ ...(existing?.monthlyPercent !== undefined ? { monthlyPercent: existing.monthlyPercent } : {}),
387
+ // Carry provenance with the value it describes. Dropping it here would downgrade a proven
388
+ // explicit-primary reading to "unproven" on the next unrelated weekly update.
389
+ ...(existing?.monthlyPercent !== undefined && existing.monthlyIsPrimaryWindow === true
390
+ ? { monthlyIsPrimaryWindow: true }
391
+ : {}),
392
+ ...(existing?.weeklyResetAt !== undefined ? { weeklyResetAt: existing.weeklyResetAt } : {}),
393
+ ...(existing?.monthlyResetAt !== undefined ? { monthlyResetAt: existing.monthlyResetAt } : {}),
394
+ ...(existing?.shortPercent !== undefined ? { shortPercent: existing.shortPercent } : {}),
395
+ ...(existing?.shortResetAt !== undefined ? { shortResetAt: existing.shortResetAt } : {}),
396
+ ...(existing?.shortWindowSeconds !== undefined ? { shortWindowSeconds: existing.shortWindowSeconds } : {}),
397
+ ...(existing?.resetCredits !== undefined ? { resetCredits: existing.resetCredits } : {}),
398
+ updatedAt: Date.now(),
399
+ };
400
+
401
+ const nextWeeklyResetAt = normalizeResetAt(weeklyResetAt);
402
+ const nextMonthlyResetAt = normalizeResetAt(monthlyResetAt);
403
+ if (nextWeekly !== undefined) {
404
+ quota.weeklyPercent = nextWeekly;
405
+ if (nextWeeklyResetAt !== undefined) quota.weeklyResetAt = nextWeeklyResetAt;
406
+ }
407
+ if (nextMonthly !== undefined) {
408
+ quota.monthlyPercent = nextMonthly;
409
+ if (nextMonthlyResetAt !== undefined) quota.monthlyResetAt = nextMonthlyResetAt;
410
+ // A caller-supplied monthly value arrives without window provenance, so it REPLACES the
411
+ // proven reading and must not inherit its flag — otherwise an unproven number would be
412
+ // treated as governing evidence.
413
+ delete quota.monthlyIsPrimaryWindow;
414
+ }
415
+ if (resetCredits !== undefined) quota.resetCredits = resetCredits;
416
+
417
+ accountQuota.set(accountId, quota);
418
+ schedulePersistAccountQuotas();
419
+ }
420
+
421
+ function hydrateAccountQuotasFromDisk(): void {
422
+ if (diskHydrated) return;
423
+ diskHydrated = true;
424
+ try {
425
+ const path = join(getConfigDir(), QUOTA_CACHE_FILENAME);
426
+ if (!existsSync(path)) return;
427
+ const raw = readFileSync(path, "utf8");
428
+ const parsed = JSON.parse(raw) as QuotaDiskFile;
429
+ if (!parsed || parsed.version !== 1 || !parsed.quotas || typeof parsed.quotas !== "object") return;
430
+ const now = Date.now();
431
+ for (const [accountId, quota] of Object.entries(parsed.quotas)) {
432
+ if (!quota || typeof quota !== "object" || typeof quota.updatedAt !== "number") continue;
433
+ if (now - quota.updatedAt > QUOTA_DISK_MAX_AGE_MS) continue;
434
+ if (!accountQuota.has(accountId)) accountQuota.set(accountId, quota);
435
+ }
436
+ } catch {
437
+ // Corrupt/missing cache must never block routing or the dashboard.
438
+ }
439
+ }
440
+
441
+ function schedulePersistAccountQuotas(): void {
442
+ if (persistTimer) clearTimeout(persistTimer);
443
+ persistTimer = setTimeout(() => {
444
+ persistTimer = null;
445
+ try {
446
+ const quotas: Record<string, StoredAccountQuota> = {};
447
+ for (const [accountId, quota] of accountQuota.entries()) {
448
+ quotas[accountId] = quota;
449
+ }
450
+ const body: QuotaDiskFile = { version: 1, quotas };
451
+ atomicWriteFile(join(getConfigDir(), QUOTA_CACHE_FILENAME), `${JSON.stringify(body)}\n`);
452
+ } catch {
453
+ // Best-effort persistence only.
454
+ }
455
+ }, QUOTA_PERSIST_DEBOUNCE_MS);
456
+ }
457
+
458
+ export function getAccountQuota(accountId: string): StoredAccountQuota | null {
459
+ hydrateAccountQuotasFromDisk();
460
+ return accountQuota.get(accountId) ?? null;
461
+ }
462
+
463
+ export function listAccountQuotas(): IterableIterator<[string, StoredAccountQuota]> {
464
+ hydrateAccountQuotasFromDisk();
465
+ return accountQuota.entries();
466
+ }
467
+
468
+ export function clearAccountQuota(accountId?: string): void {
469
+ if (accountId) {
470
+ accountQuota.delete(accountId);
471
+ schedulePersistAccountQuotas();
472
+ return;
473
+ }
474
+ accountQuota.clear();
475
+ diskHydrated = false;
476
+ if (persistTimer) {
477
+ clearTimeout(persistTimer);
478
+ persistTimer = null;
479
+ }
480
+ try {
481
+ const path = join(getConfigDir(), QUOTA_CACHE_FILENAME);
482
+ if (existsSync(path)) unlinkSync(path);
483
+ } catch {
484
+ // Best-effort; memory is already cleared.
485
+ }
486
+ }
487
+
488
+ export function reconcileCodexQuotaAccounts(context: GenerationContext): number {
489
+ if (context.generation <= lastReconciledGeneration) return 0;
490
+ hydrateAccountQuotasFromDisk();
491
+ let removed = 0;
492
+ for (const accountId of accountQuota.keys()) {
493
+ if (context.codexAccountIds.has(accountId)) continue;
494
+ accountQuota.delete(accountId);
495
+ removed += 1;
496
+ }
497
+ liveAccountIds = new Set(context.codexAccountIds);
498
+ lastReconciledGeneration = context.generation;
499
+ if (removed > 0) schedulePersistAccountQuotas();
500
+ return removed;
501
+ }
502
+
503
+ export function parseUsageQuota(data: WhamUsageResponse): Omit<StoredAccountQuota, "updatedAt"> | null {
504
+ const resetCredits = typeof data.rate_limit_reset_credits?.available_count === "number"
505
+ ? data.rate_limit_reset_credits.available_count
506
+ : undefined;
507
+
508
+ if (!data.rate_limit) {
509
+ return resetCredits !== undefined ? { resetCredits } : null;
510
+ }
511
+
512
+ const quota: Omit<StoredAccountQuota, "updatedAt"> = {};
513
+ const thirtyDayOnly = codexQuotaWindowForPlan(data.plan_type) === "monthly";
514
+ const primaryWindow = data.rate_limit.primary_window;
515
+ const secondaryWindow = data.rate_limit.secondary_window;
516
+ const tertiaryWindow = data.rate_limit.tertiary_window;
517
+ const primaryPercent = normalizeUsagePercent(primaryWindow?.used_percent);
518
+ const secondaryPercent = normalizeUsagePercent(secondaryWindow?.used_percent);
519
+ const tertiaryPercent = normalizeUsagePercent(tertiaryWindow?.used_percent);
520
+ const primaryResetAt = normalizeResetAt(primaryWindow?.reset_at);
521
+ const secondaryResetAt = normalizeResetAt(secondaryWindow?.reset_at);
522
+ const tertiaryResetAt = normalizeResetAt(tertiaryWindow?.reset_at);
523
+ const primaryIsMonthly = isExplicitMonthlyWindow(primaryWindow);
524
+
525
+ // [Decision Log]
526
+ // - 목적과 의도: distinguish weekly and roughly monthly WHAM primary windows without plan-name guesses.
527
+ // - 기존 구현 및 제약 조건: primary meant weekly, and older responses omit limit_window_seconds.
528
+ // - 검토한 주요 대안: exact-duration matching, plan-specific mapping, and a duration lower bound.
529
+ // - 선택한 방식: only an explicit primary duration of at least 28 days changes it to monthly.
530
+ // - 다른 대안 대신 이 방식을 선택한 이유: it accepts calendar-month variance and preserves legacy payloads.
531
+ // - 장점, 단점 및 영향: Team monthly quotas classify correctly; unknown durations remain weekly by design.
532
+ // #1791: a primary window that declares a sub-day duration is a burst window, not the
533
+ // weekly one. Skip it so the secondary (the real 7-day window) is what lands in
534
+ // `weeklyPercent`; without this the 5-hour bar was reported as weekly and the actual
535
+ // weekly reading was dropped on the floor.
536
+ const primaryIsShort = isExplicitShortWindow(primaryWindow);
537
+ const weeklyCandidatePercent = primaryIsShort ? undefined : primaryPercent;
538
+ const weeklyCandidateResetAt = primaryIsShort ? undefined : primaryResetAt;
539
+ // Keep the burst reading instead of dropping it on the floor: it is a real limit, and
540
+ // the account is blocked when it fills even though the weekly window is fine (#1791).
541
+ if (primaryIsShort && primaryPercent !== undefined) {
542
+ quota.shortPercent = primaryPercent;
543
+ if (primaryResetAt !== undefined) quota.shortResetAt = primaryResetAt;
544
+ const seconds = primaryWindow?.limit_window_seconds;
545
+ if (typeof seconds === "number" && Number.isFinite(seconds)) quota.shortWindowSeconds = seconds;
546
+ }
547
+ const weeklyPercent = primaryIsMonthly ? secondaryPercent : weeklyCandidatePercent ?? secondaryPercent;
548
+ const weeklyResetAt = primaryIsMonthly
549
+ ? secondaryResetAt
550
+ : weeklyCandidatePercent !== undefined ? weeklyCandidateResetAt : secondaryResetAt;
551
+ const monthlyPercent = primaryIsMonthly ? primaryPercent ?? tertiaryPercent : tertiaryPercent;
552
+ const monthlyResetAt = primaryIsMonthly && primaryPercent !== undefined ? primaryResetAt : tertiaryResetAt;
553
+ if (thirtyDayOnly) {
554
+ if (monthlyPercent !== undefined) {
555
+ quota.monthlyPercent = monthlyPercent;
556
+ if (monthlyResetAt !== undefined) quota.monthlyResetAt = monthlyResetAt;
557
+ }
558
+ } else if (weeklyPercent !== undefined) {
559
+ quota.weeklyPercent = weeklyPercent;
560
+ if (weeklyResetAt !== undefined) quota.weeklyResetAt = weeklyResetAt;
561
+ }
562
+ if (!thirtyDayOnly && monthlyPercent !== undefined) {
563
+ quota.monthlyPercent = monthlyPercent;
564
+ if (monthlyResetAt !== undefined) quota.monthlyResetAt = monthlyResetAt;
565
+ // Record WHERE this reading came from. Only an explicitly-monthly primary window is the
566
+ // account's governing quota; a tertiary window lands in the same field but describes a
567
+ // different period, so recovery must not treat the two as interchangeable.
568
+ if (primaryIsMonthly && primaryPercent !== undefined) quota.monthlyIsPrimaryWindow = true;
569
+ }
570
+ if (resetCredits !== undefined) quota.resetCredits = resetCredits;
571
+
572
+ return hasKnownQuotaValue(quota) || resetCredits !== undefined ? quota : null;
573
+ }
@@ -0,0 +1,62 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { invalidateCodexModelsCache, syncCatalogModels } from "./catalog";
3
+ import type { ComboCatalogOmission } from "./catalog/aggregation";
4
+ import { CODEX_MODELS_CACHE_PATH } from "./paths";
5
+ import { atomicWriteFile } from "../config";
6
+ import type { OcxConfig } from "../types";
7
+ import type { CodexCatalogSyncOptions } from "./catalog/sync";
8
+
9
+ export interface CodexCatalogRefreshResult {
10
+ added: number;
11
+ path: string;
12
+ catalogExists: boolean;
13
+ catalogWritten: boolean;
14
+ cacheSynced: boolean;
15
+ comboOmissions: ComboCatalogOmission[];
16
+ /** Desired OFF observed under K during the catalog commit; no cache write either. */
17
+ skippedReason?: "desired_disabled";
18
+ }
19
+
20
+ interface RefreshDeps {
21
+ syncCatalogModels: typeof syncCatalogModels;
22
+ invalidateCodexModelsCache: typeof invalidateCodexModelsCache;
23
+ existsSync: typeof existsSync;
24
+ }
25
+
26
+ const defaultDeps: RefreshDeps = {
27
+ syncCatalogModels,
28
+ invalidateCodexModelsCache,
29
+ existsSync,
30
+ };
31
+
32
+ export function syncCodexModelsCacheFromCatalog(catalogPath: string): void {
33
+ const content = readFileSync(catalogPath, "utf8");
34
+ atomicWriteFile(CODEX_MODELS_CACHE_PATH, content);
35
+ }
36
+
37
+ /**
38
+ * Rebuild Codex's on-disk model catalog and force Codex's models cache stale
39
+ * when a catalog file exists. The cache must keep Codex's fetched_at/client_version
40
+ * wrapper shape; writing the raw catalog back here makes app-server/TUI refreshes
41
+ * inconsistent with the CLI models-manager cache path.
42
+ */
43
+ export async function refreshCodexModelCatalog(
44
+ config: OcxConfig,
45
+ deps: RefreshDeps = defaultDeps,
46
+ options?: CodexCatalogSyncOptions,
47
+ ): Promise<CodexCatalogRefreshResult> {
48
+ const result = await deps.syncCatalogModels(config, options);
49
+ const catalogExists = deps.existsSync(result.path);
50
+ const catalogWritten = result.catalogWritten === true;
51
+ const comboOmissions = result.comboOmissions ?? [];
52
+ if (result.skippedReason === "desired_disabled") {
53
+ // The commit path observed OFF under K. Invalidate nothing: rewriting the
54
+ // models cache here would be exactly the routed-cache write the skip refused.
55
+ return { ...result, catalogExists, catalogWritten: false, cacheSynced: false, comboOmissions };
56
+ }
57
+ if (!catalogExists) {
58
+ return { ...result, catalogExists, catalogWritten: false, cacheSynced: false, comboOmissions };
59
+ }
60
+ const cacheSynced = deps.invalidateCodexModelsCache(options);
61
+ return { ...result, catalogExists, catalogWritten, cacheSynced, comboOmissions };
62
+ }