@iislee/opencodex 2.11.0 → 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 (675) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +114 -19
  3. package/bin/ocx.mjs +164 -36
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-BF38heuV.js +104 -0
  6. package/gui/dist/assets/index-DMiI18Kv.css +1 -0
  7. package/gui/dist/index.html +2 -2
  8. package/gui/dist/provider-icons/alibaba-color.svg +1 -1
  9. package/gui/dist/provider-icons/antigravity-color.svg +1 -1
  10. package/gui/dist/provider-icons/claude-color.svg +1 -1
  11. package/gui/dist/provider-icons/cline-color.svg +16 -0
  12. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
  13. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  14. package/gui/dist/provider-icons/copilot-color.svg +1 -1
  15. package/gui/dist/provider-icons/cursor-color.svg +1 -1
  16. package/gui/dist/provider-icons/deepseek-color.svg +1 -1
  17. package/gui/dist/provider-icons/firepass-color.svg +1 -1
  18. package/gui/dist/provider-icons/fireworks-color.svg +1 -1
  19. package/gui/dist/provider-icons/gemini-color.svg +1 -1
  20. package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
  21. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
  22. package/gui/dist/provider-icons/grok.svg +1 -1
  23. package/gui/dist/provider-icons/groq-color.svg +1 -1
  24. package/gui/dist/provider-icons/huggingface-color.svg +1 -1
  25. package/gui/dist/provider-icons/kimi-color.svg +1 -1
  26. package/gui/dist/provider-icons/kiro-color.svg +2 -2
  27. package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
  28. package/gui/dist/provider-icons/mistral-color.svg +1 -1
  29. package/gui/dist/provider-icons/moonshot-color.svg +1 -1
  30. package/gui/dist/provider-icons/nvidia-color.svg +1 -1
  31. package/gui/dist/provider-icons/ollama-color.svg +1 -1
  32. package/gui/dist/provider-icons/openai.svg +1 -1
  33. package/gui/dist/provider-icons/opencode.svg +2 -1
  34. package/gui/dist/provider-icons/openrouter-color.svg +1 -1
  35. package/gui/dist/provider-icons/pi.svg +2 -2
  36. package/gui/dist/provider-icons/qianfan-color.svg +1 -1
  37. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
  38. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
  39. package/gui/dist/provider-icons/vllm-color.svg +1 -1
  40. package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
  41. package/package.json +19 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +376 -52
  44. package/src/adapters/base.ts +54 -7
  45. package/src/adapters/client-fingerprint.ts +18 -12
  46. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  47. package/src/adapters/command-code.ts +601 -0
  48. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  49. package/src/adapters/cursor/cursor-errors.ts +129 -6
  50. package/src/adapters/cursor/discovery.ts +61 -4
  51. package/src/adapters/cursor/effort-map.ts +27 -3
  52. package/src/adapters/cursor/framing.ts +39 -0
  53. package/src/adapters/cursor/h2-pool.ts +123 -0
  54. package/src/adapters/cursor/http1-bidi.ts +361 -0
  55. package/src/adapters/cursor/images.ts +704 -0
  56. package/src/adapters/cursor/live-models.ts +174 -58
  57. package/src/adapters/cursor/live-transport.ts +609 -170
  58. package/src/adapters/cursor/native-exec-common.ts +23 -2
  59. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  60. package/src/adapters/cursor/native-exec-network.ts +1 -1
  61. package/src/adapters/cursor/native-exec-shell.ts +0 -1
  62. package/src/adapters/cursor/native-exec.ts +101 -14
  63. package/src/adapters/cursor/protobuf-events.ts +829 -11
  64. package/src/adapters/cursor/protobuf-request.ts +383 -65
  65. package/src/adapters/cursor/request-builder.ts +237 -31
  66. package/src/adapters/cursor/tool-definitions.ts +242 -12
  67. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  68. package/src/adapters/cursor/transport.ts +22 -0
  69. package/src/adapters/cursor/types.ts +28 -1
  70. package/src/adapters/cursor.ts +133 -7
  71. package/src/adapters/google-antigravity-replay.ts +558 -34
  72. package/src/adapters/google-antigravity-wire.ts +43 -10
  73. package/src/adapters/google-http.ts +38 -10
  74. package/src/adapters/google-tool-schema.ts +80 -15
  75. package/src/adapters/google-truncation.ts +11 -0
  76. package/src/adapters/google.ts +618 -74
  77. package/src/adapters/identity.ts +39 -6
  78. package/src/adapters/kiro-errors.ts +11 -0
  79. package/src/adapters/kiro-events.ts +19 -1
  80. package/src/adapters/kiro-thinking.ts +18 -2
  81. package/src/adapters/kiro-tools.ts +10 -1
  82. package/src/adapters/kiro.ts +92 -54
  83. package/src/adapters/mimo-free.ts +17 -0
  84. package/src/adapters/openai-chat-url.ts +11 -0
  85. package/src/adapters/openai-chat.ts +1311 -336
  86. package/src/adapters/openai-responses-url.ts +16 -0
  87. package/src/adapters/openai-responses.ts +830 -56
  88. package/src/adapters/registry.ts +175 -0
  89. package/src/adapters/responses-tool-schema.ts +67 -0
  90. package/src/adapters/tool-call-id.ts +119 -0
  91. package/src/adapters/tool-catalog-nudge.ts +104 -21
  92. package/src/adapters/xai-web-search.ts +185 -0
  93. package/src/bridge.ts +428 -61
  94. package/src/chat/inbound.ts +43 -19
  95. package/src/chat/outbound.ts +82 -26
  96. package/src/claude/agents-inject.ts +32 -9
  97. package/src/claude/context-windows.ts +21 -5
  98. package/src/claude/desktop-3p.ts +243 -9
  99. package/src/claude/gateway-cache.ts +41 -4
  100. package/src/claude/inbound.ts +72 -3
  101. package/src/claude/model-info.ts +38 -15
  102. package/src/claude/outbound.ts +70 -16
  103. package/src/cli/account-api.ts +35 -1
  104. package/src/cli/account-auth.ts +33 -6
  105. package/src/cli/account-catalog-refresh.ts +14 -0
  106. package/src/cli/account-extended.ts +389 -2
  107. package/src/cli/account-main.ts +317 -0
  108. package/src/cli/account.ts +30 -6
  109. package/src/cli/agent.ts +45 -1
  110. package/src/cli/claude-agent-startup-sync.ts +73 -0
  111. package/src/cli/claude-desktop.ts +29 -4
  112. package/src/cli/claude.ts +103 -34
  113. package/src/cli/codex-log-guard-doctor.ts +103 -0
  114. package/src/cli/codex-shim-autorestore.ts +2 -0
  115. package/src/cli/codex-shim-readiness.ts +76 -0
  116. package/src/cli/combo.ts +8 -0
  117. package/src/cli/config-command.ts +74 -10
  118. package/src/cli/dispatch.ts +593 -0
  119. package/src/cli/doctor.ts +315 -43
  120. package/src/cli/ensure-desired-integrations.ts +152 -0
  121. package/src/cli/export-command.ts +46 -20
  122. package/src/cli/help.ts +20 -271
  123. package/src/cli/index.ts +378 -519
  124. package/src/cli/init.ts +4 -17
  125. package/src/cli/integrations.ts +120 -2
  126. package/src/cli/lab.ts +607 -0
  127. package/src/cli/launcher-context.ts +77 -0
  128. package/src/cli/minimax.ts +497 -0
  129. package/src/cli/models-runtime.ts +35 -2
  130. package/src/cli/models.ts +100 -14
  131. package/src/cli/observe.ts +92 -3
  132. package/src/cli/opencode.ts +4 -2
  133. package/src/cli/provider-runtime.ts +18 -1
  134. package/src/cli/provider.ts +24 -3
  135. package/src/cli/ready.ts +301 -0
  136. package/src/cli/registry.ts +437 -0
  137. package/src/cli/root.ts +86 -0
  138. package/src/cli/route-policy.ts +92 -0
  139. package/src/cli/runtime-api.ts +6 -3
  140. package/src/cli/star-prompt.ts +71 -15
  141. package/src/cli/status.ts +10 -3
  142. package/src/cli/system-restart-client.ts +146 -0
  143. package/src/cli/tray-proxy.ts +153 -6
  144. package/src/cli/v2.ts +105 -10
  145. package/src/cli.ts +1 -1
  146. package/src/clients/config-export.ts +1358 -21
  147. package/src/codex/account-label.ts +14 -1
  148. package/src/codex/account-lifecycle.ts +130 -13
  149. package/src/codex/account-namespaces.ts +49 -3
  150. package/src/codex/account-priority.ts +83 -0
  151. package/src/codex/account-store.ts +29 -2
  152. package/src/codex/account-usability.ts +25 -2
  153. package/src/codex/admission.ts +256 -0
  154. package/src/codex/affinity-debug.ts +162 -0
  155. package/src/codex/app-server-processes.ts +493 -106
  156. package/src/codex/app-server-restart-service.ts +232 -0
  157. package/src/codex/auth-api.ts +849 -242
  158. package/src/codex/auth-collision.ts +5 -3
  159. package/src/codex/auth-context.ts +345 -32
  160. package/src/codex/autostart-health.ts +8 -1
  161. package/src/codex/catalog/account-models.ts +67 -0
  162. package/src/codex/catalog/aggregation.ts +68 -10
  163. package/src/codex/catalog/bundled.ts +331 -33
  164. package/src/codex/catalog/effort.ts +121 -30
  165. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  166. package/src/codex/catalog/kinds.ts +2 -0
  167. package/src/codex/catalog/metadata.ts +529 -45
  168. package/src/codex/catalog/native-models.ts +72 -0
  169. package/src/codex/catalog/parsing.ts +224 -30
  170. package/src/codex/catalog/provider-fetch.ts +1460 -134
  171. package/src/codex/catalog/sync.ts +1449 -186
  172. package/src/codex/catalog-admission.ts +199 -0
  173. package/src/codex/catalog-refresh-status.ts +105 -0
  174. package/src/codex/catalog-write-serialization.ts +242 -0
  175. package/src/codex/catalog.ts +6 -3
  176. package/src/codex/codex-write-lock.ts +384 -0
  177. package/src/codex/convergence-types.ts +614 -0
  178. package/src/codex/convergence.ts +651 -0
  179. package/src/codex/coordinator-doctor.ts +332 -0
  180. package/src/codex/custom-model-catalog-migration.ts +176 -0
  181. package/src/codex/desired-state.ts +230 -0
  182. package/src/codex/features.ts +636 -39
  183. package/src/codex/generation.ts +202 -0
  184. package/src/codex/history-job.ts +407 -0
  185. package/src/codex/history-lock.ts +242 -0
  186. package/src/codex/history-migration-guardian.ts +26 -20
  187. package/src/codex/history-provider.ts +231 -28
  188. package/src/codex/history-transition.ts +105 -0
  189. package/src/codex/history-worker.ts +220 -0
  190. package/src/codex/inject-coordination.ts +290 -0
  191. package/src/codex/inject.ts +1073 -152
  192. package/src/codex/injected-marker.ts +37 -3
  193. package/src/codex/integration-record.ts +266 -0
  194. package/src/codex/internal/catalog-writer.ts +203 -0
  195. package/src/codex/internal/history-writer.ts +80 -0
  196. package/src/codex/journal.ts +66 -4
  197. package/src/codex/log-guard/inspect.ts +506 -0
  198. package/src/codex/log-guard/lock.ts +150 -0
  199. package/src/codex/log-guard/maintenance.ts +403 -0
  200. package/src/codex/log-guard/path-safety.ts +88 -0
  201. package/src/codex/log-guard/policy.ts +44 -0
  202. package/src/codex/log-guard/processes.ts +205 -0
  203. package/src/codex/log-guard/protection.ts +489 -0
  204. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  205. package/src/codex/main-account-cache.ts +24 -0
  206. package/src/codex/main-account.ts +29 -1
  207. package/src/codex/management-convergence.ts +167 -0
  208. package/src/codex/model-cache.ts +56 -10
  209. package/src/codex/model-entitlements.ts +353 -0
  210. package/src/codex/native-main-admission.ts +47 -0
  211. package/src/codex/native-main-auth-temp.ts +187 -0
  212. package/src/codex/native-main-claim.ts +178 -0
  213. package/src/codex/native-main-lock-file.ts +162 -0
  214. package/src/codex/native-main-owner.ts +329 -0
  215. package/src/codex/native-profile-api.ts +247 -0
  216. package/src/codex/native-profile-manager.ts +1531 -0
  217. package/src/codex/native-profile-processes.ts +121 -0
  218. package/src/codex/native-profile-recovery.ts +99 -0
  219. package/src/codex/native-profile-stage-store.ts +387 -0
  220. package/src/codex/native-profile-startup.ts +492 -0
  221. package/src/codex/native-profile-store.ts +855 -0
  222. package/src/codex/native-profile-types.ts +120 -0
  223. package/src/codex/native-residue.ts +682 -0
  224. package/src/codex/paths.ts +80 -1
  225. package/src/codex/plan-from-token.ts +140 -0
  226. package/src/codex/plan.ts +40 -0
  227. package/src/codex/plugins-doctor.ts +1 -1
  228. package/src/codex/pool-rotation.ts +74 -4
  229. package/src/codex/project-config-warnings.ts +20 -6
  230. package/src/codex/prompt-journal.ts +352 -0
  231. package/src/codex/prompt-layers.ts +967 -0
  232. package/src/codex/prompt-lock.ts +143 -0
  233. package/src/codex/quota-rejection.ts +298 -0
  234. package/src/codex/quota.ts +175 -13
  235. package/src/codex/refresh.ts +11 -2
  236. package/src/codex/reset-credit-recovery.ts +1044 -0
  237. package/src/codex/routing.ts +505 -94
  238. package/src/codex/runtime.ts +159 -38
  239. package/src/codex/shim.ts +1009 -28
  240. package/src/codex/subagent-model-fallback.ts +350 -35
  241. package/src/codex/sync.ts +191 -2
  242. package/src/codex/transition-state.ts +612 -0
  243. package/src/codex/upstream-host-health.ts +368 -0
  244. package/src/codex/user-identity.ts +557 -0
  245. package/src/codex/warmup.ts +187 -81
  246. package/src/codex/write-coordination.ts +114 -0
  247. package/src/combos/failover.ts +20 -0
  248. package/src/combos/index.ts +4 -0
  249. package/src/combos/request.ts +32 -0
  250. package/src/combos/types.ts +81 -9
  251. package/src/config/provider-name.ts +24 -0
  252. package/src/config.ts +1762 -140
  253. package/src/generated/compatibility-version.json +3164 -0
  254. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  255. package/src/grok/inject.ts +16 -5
  256. package/src/grok/inspect.ts +45 -0
  257. package/src/grok/sync.ts +2 -2
  258. package/src/images/loop.ts +152 -29
  259. package/src/images/plan.ts +23 -13
  260. package/src/integrations/config-io.ts +269 -0
  261. package/src/integrations/journal.ts +315 -0
  262. package/src/integrations/merge.ts +135 -0
  263. package/src/integrations/mutation-flight.ts +71 -0
  264. package/src/integrations/native/ownership-preflight.ts +202 -0
  265. package/src/integrations/omp-yaml-source.ts +358 -0
  266. package/src/integrations/owned-refresh.ts +74 -0
  267. package/src/integrations/ownership.ts +111 -0
  268. package/src/integrations/registry.ts +159 -0
  269. package/src/integrations/serialize.ts +314 -0
  270. package/src/integrations/state.ts +361 -0
  271. package/src/integrations/store.ts +103 -0
  272. package/src/integrations/writer-lock.ts +98 -0
  273. package/src/integrations/writer.ts +691 -0
  274. package/src/lab/artifacts/sanitize.ts +586 -0
  275. package/src/lab/artifacts/secure-fs.ts +475 -0
  276. package/src/lab/artifacts/store.ts +310 -0
  277. package/src/lab/automation/budgets.ts +78 -0
  278. package/src/lab/automation/config-persistence.ts +256 -0
  279. package/src/lab/automation/constants.ts +39 -0
  280. package/src/lab/automation/cooldown.ts +103 -0
  281. package/src/lab/automation/dispatch.ts +211 -0
  282. package/src/lab/automation/index.ts +13 -0
  283. package/src/lab/automation/orchestrator.ts +499 -0
  284. package/src/lab/automation/persistence.ts +512 -0
  285. package/src/lab/automation/planner.ts +371 -0
  286. package/src/lab/automation/policy.ts +136 -0
  287. package/src/lab/automation/queue.ts +191 -0
  288. package/src/lab/automation/recovery.ts +24 -0
  289. package/src/lab/automation/route-context.ts +21 -0
  290. package/src/lab/automation/run-key.ts +44 -0
  291. package/src/lab/automation/runs-query.ts +34 -0
  292. package/src/lab/automation/types.ts +160 -0
  293. package/src/lab/conformance/assertion.ts +325 -0
  294. package/src/lab/conformance/digest.ts +22 -0
  295. package/src/lab/conformance/executor.ts +741 -0
  296. package/src/lab/conformance/fixture-provider.ts +27 -0
  297. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  298. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  299. package/src/lab/conformance/harness-budget.ts +47 -0
  300. package/src/lab/conformance/index.ts +5 -0
  301. package/src/lab/conformance/jcs.ts +64 -0
  302. package/src/lab/conformance/json-pointer.ts +39 -0
  303. package/src/lab/conformance/manifest.ts +180 -0
  304. package/src/lab/conformance/mcp-stub.ts +179 -0
  305. package/src/lab/conformance/negative-controls.ts +164 -0
  306. package/src/lab/conformance/observation.ts +355 -0
  307. package/src/lab/conformance/runner.ts +68 -0
  308. package/src/lab/conformance/sse-normalize.ts +59 -0
  309. package/src/lab/conformance/suite-manifest.ts +78 -0
  310. package/src/lab/conformance/types.ts +214 -0
  311. package/src/lab/constants.ts +126 -0
  312. package/src/lab/digest.ts +64 -0
  313. package/src/lab/events/errors.ts +9 -0
  314. package/src/lab/events/limits.ts +117 -0
  315. package/src/lab/events/types.ts +229 -0
  316. package/src/lab/events/validate.ts +781 -0
  317. package/src/lab/fabric/constants.ts +40 -0
  318. package/src/lab/fabric/executor.ts +492 -0
  319. package/src/lab/fabric/index.ts +80 -0
  320. package/src/lab/fabric/manifest.ts +222 -0
  321. package/src/lab/fabric/observe.ts +489 -0
  322. package/src/lab/fabric/patch.ts +79 -0
  323. package/src/lab/fabric/producer-child.ts +139 -0
  324. package/src/lab/fabric/producer-isolate.ts +276 -0
  325. package/src/lab/fabric/producer-protocol.ts +61 -0
  326. package/src/lab/fabric/scratch.ts +439 -0
  327. package/src/lab/fabric/subject.ts +106 -0
  328. package/src/lab/fabric/types.ts +134 -0
  329. package/src/lab/fabric/verifier.ts +98 -0
  330. package/src/lab/index.ts +54 -0
  331. package/src/lab/ledger/artifact-refs.ts +127 -0
  332. package/src/lab/ledger/invalidation.ts +136 -0
  333. package/src/lab/ledger/purge.ts +310 -0
  334. package/src/lab/ledger/store.ts +532 -0
  335. package/src/lab/live/credential-lease.ts +53 -0
  336. package/src/lab/live/destination.ts +155 -0
  337. package/src/lab/live/executor.ts +336 -0
  338. package/src/lab/live/inert-tools.ts +56 -0
  339. package/src/lab/live/manifest.ts +85 -0
  340. package/src/lab/live/mcp-loopback.ts +57 -0
  341. package/src/lab/live/runner.ts +19 -0
  342. package/src/lab/live/sandbox.ts +61 -0
  343. package/src/lab/live/suite-manifest.ts +41 -0
  344. package/src/lab/live/transport.ts +118 -0
  345. package/src/lab/live/types.ts +197 -0
  346. package/src/lab/observe/from-conformance.ts +301 -0
  347. package/src/lab/observe/from-live.ts +117 -0
  348. package/src/lab/paths.ts +153 -0
  349. package/src/lab/projection/rebuild.ts +495 -0
  350. package/src/lab/projection/schema.ts +135 -0
  351. package/src/lab/projection/verdicts.ts +474 -0
  352. package/src/lab/projection/verification.ts +412 -0
  353. package/src/lab/public/bundle.ts +217 -0
  354. package/src/lab/public/community-authority.ts +175 -0
  355. package/src/lab/public/community-files.ts +29 -0
  356. package/src/lab/public/community.ts +479 -0
  357. package/src/lab/public/file-safety.ts +155 -0
  358. package/src/lab/public/ids.ts +26 -0
  359. package/src/lab/public/index.ts +16 -0
  360. package/src/lab/public/mutation-lock.ts +424 -0
  361. package/src/lab/public/operator.ts +353 -0
  362. package/src/lab/public/origin-purge.ts +79 -0
  363. package/src/lab/public/origin.ts +203 -0
  364. package/src/lab/public/privacy.ts +143 -0
  365. package/src/lab/public/private-file.ts +261 -0
  366. package/src/lab/public/project.ts +124 -0
  367. package/src/lab/public/purge-test-fault.ts +21 -0
  368. package/src/lab/public/purge.ts +223 -0
  369. package/src/lab/public/registry.ts +44 -0
  370. package/src/lab/public/revocation.ts +252 -0
  371. package/src/lab/public/signature.ts +243 -0
  372. package/src/lab/public/storage.ts +105 -0
  373. package/src/lab/public/strict-json.ts +206 -0
  374. package/src/lab/public/time.ts +26 -0
  375. package/src/lab/public/types.ts +172 -0
  376. package/src/lab/public/validate.ts +391 -0
  377. package/src/lab/query/catalog.ts +101 -0
  378. package/src/lab/query/connection.ts +107 -0
  379. package/src/lab/query/constants.ts +4 -0
  380. package/src/lab/query/cursor.ts +132 -0
  381. package/src/lab/query/dto-map.ts +277 -0
  382. package/src/lab/query/errors.ts +22 -0
  383. package/src/lab/query/freshness.ts +53 -0
  384. package/src/lab/query/index.ts +45 -0
  385. package/src/lab/query/latest-observation.ts +59 -0
  386. package/src/lab/query/passive-production.ts +159 -0
  387. package/src/lab/query/queries.ts +444 -0
  388. package/src/lab/query/types.ts +266 -0
  389. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  390. package/src/lab/subject/installation-salt.ts +112 -0
  391. package/src/lab/subject/protocol-subject.ts +80 -0
  392. package/src/lab/subject/route-subject.ts +74 -0
  393. package/src/lib/app-owned-memory-stores.ts +22 -0
  394. package/src/lib/bounded-body.ts +153 -9
  395. package/src/lib/bun-runtime.ts +125 -12
  396. package/src/lib/bun-stream-caps.ts +13 -9
  397. package/src/lib/codex-restart-contract.ts +120 -0
  398. package/src/lib/config-ownership.ts +6 -2
  399. package/src/lib/destination-policy.ts +65 -1
  400. package/src/lib/errors.ts +44 -2
  401. package/src/lib/fabric-task-execution-authority.ts +7 -0
  402. package/src/lib/fabric-task-host.ts +29 -0
  403. package/src/lib/lab-activation.ts +223 -0
  404. package/src/lib/lab-live-execution-authority.ts +13 -0
  405. package/src/lib/lab-live-host.ts +30 -0
  406. package/src/lib/lab-live-pinned-sender.ts +56 -0
  407. package/src/lib/lab-live-route-production.ts +130 -0
  408. package/src/lib/lab-passive-linker-registration.ts +26 -0
  409. package/src/lib/local-management-attestation.ts +51 -0
  410. package/src/lib/local-management-capability.ts +100 -0
  411. package/src/lib/local-provider-reload-contract.ts +100 -0
  412. package/src/lib/optional-shutdown-hooks.ts +57 -0
  413. package/src/lib/pinned-http.ts +145 -26
  414. package/src/lib/process-control.ts +4 -1
  415. package/src/lib/provider-outbound.ts +49 -9
  416. package/src/lib/redact.ts +419 -3
  417. package/src/lib/self-launch-argv.ts +15 -0
  418. package/src/lib/server-resource-ownership.ts +71 -0
  419. package/src/lib/shadow-call.ts +35 -4
  420. package/src/lib/sse-decoder.ts +41 -0
  421. package/src/lib/state-store-registrations.ts +10 -2
  422. package/src/lib/system-restart-contract.ts +73 -0
  423. package/src/lib/token-estimate.ts +19 -2
  424. package/src/lib/tool-argument-integers.ts +202 -0
  425. package/src/lib/translator-budget.ts +44 -0
  426. package/src/lib/upstream-http-version.ts +57 -0
  427. package/src/lib/upstream-reachability.ts +95 -0
  428. package/src/lib/upstream-retry.ts +156 -3
  429. package/src/lib/windows-atomic-replace.ts +155 -0
  430. package/src/lib/windows-elevation.ts +70 -2
  431. package/src/lib/windows-secret-acl.ts +409 -69
  432. package/src/lib/windows-service-wrappers.ts +72 -0
  433. package/src/lib/windows-text.ts +106 -0
  434. package/src/lib/windows-user-principal.ts +341 -0
  435. package/src/lib/winsw.ts +33 -5
  436. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  437. package/src/oauth/account-import/index.ts +15 -0
  438. package/src/oauth/account-import/parser.ts +83 -0
  439. package/src/oauth/account-import/registry.ts +18 -0
  440. package/src/oauth/account-import/service.ts +75 -0
  441. package/src/oauth/account-import/types.ts +91 -0
  442. package/src/oauth/anthropic.ts +12 -1
  443. package/src/oauth/callback-server.ts +8 -2
  444. package/src/oauth/chatgpt.ts +12 -1
  445. package/src/oauth/command-code.ts +239 -0
  446. package/src/oauth/cursor.ts +46 -5
  447. package/src/oauth/google-antigravity.ts +35 -3
  448. package/src/oauth/health.ts +20 -12
  449. package/src/oauth/index.ts +398 -66
  450. package/src/oauth/key-providers.ts +16 -0
  451. package/src/oauth/kimi.ts +16 -2
  452. package/src/oauth/kiro.ts +50 -6
  453. package/src/oauth/local-token-detect.ts +11 -2
  454. package/src/oauth/log.ts +3 -1
  455. package/src/oauth/login-cli.ts +88 -28
  456. package/src/oauth/nous.ts +798 -0
  457. package/src/oauth/store.ts +119 -21
  458. package/src/oauth/token-guardian.ts +9 -3
  459. package/src/pi/models.ts +2 -2
  460. package/src/providers/alibaba-region-migration.ts +1 -1
  461. package/src/providers/antigravity-models.ts +521 -31
  462. package/src/providers/base-url-choices.ts +10 -0
  463. package/src/providers/codex-capacity.ts +292 -0
  464. package/src/providers/command-code-efforts.ts +144 -0
  465. package/src/providers/context-cap.ts +22 -5
  466. package/src/providers/cursor-pool.ts +72 -0
  467. package/src/providers/derive.ts +253 -6
  468. package/src/providers/fastwire.ts +501 -0
  469. package/src/providers/free-directory.ts +10 -7
  470. package/src/providers/google-vertex-location.ts +14 -0
  471. package/src/providers/key-failover.ts +71 -3
  472. package/src/providers/label.ts +1 -1
  473. package/src/providers/model-discovery-limits.ts +16 -0
  474. package/src/providers/model-discovery.ts +115 -22
  475. package/src/providers/model-rename-migration.ts +255 -0
  476. package/src/providers/model-rename-startup.ts +28 -0
  477. package/src/providers/openai-sidecar.ts +72 -4
  478. package/src/providers/openai-tier-startup.ts +31 -2
  479. package/src/providers/openai-tiers.ts +119 -4
  480. package/src/providers/openai-virtual-models.ts +1 -0
  481. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  482. package/src/providers/provider-id-rewrite.ts +29 -0
  483. package/src/providers/quota.ts +1319 -38
  484. package/src/providers/registry.ts +1429 -111
  485. package/src/providers/request-pacing.ts +310 -0
  486. package/src/providers/service-tier.ts +277 -0
  487. package/src/providers/slug-codec.ts +42 -6
  488. package/src/providers/static-model-discovery.ts +86 -0
  489. package/src/providers/xai-responses-opt-in.ts +15 -0
  490. package/src/providers/xai-transport.ts +11 -4
  491. package/src/reasoning-effort.ts +49 -1
  492. package/src/responses/compaction.ts +26 -1
  493. package/src/responses/custom-tool-compat.ts +266 -0
  494. package/src/responses/hosted-tool-policy.ts +9 -0
  495. package/src/responses/namespace-tool-compat.ts +355 -0
  496. package/src/responses/parser.ts +220 -38
  497. package/src/responses/provider-continuation.ts +98 -0
  498. package/src/responses/provider-opaque-metadata.ts +73 -0
  499. package/src/responses/reasoning-envelope.ts +9 -1
  500. package/src/responses/reasoning-replay-cache.ts +426 -0
  501. package/src/responses/schema.ts +7 -1
  502. package/src/responses/spill-store.ts +75 -10
  503. package/src/responses/state.ts +565 -27
  504. package/src/responses/thought-signature-replay.ts +347 -0
  505. package/src/responses/tool-search-compat.ts +301 -0
  506. package/src/responses/truncated-stop-reason.ts +60 -0
  507. package/src/router.ts +366 -30
  508. package/src/routing/analytics.ts +378 -0
  509. package/src/routing/capability.ts +244 -0
  510. package/src/routing/compatibility/assemble.ts +73 -0
  511. package/src/routing/compatibility/behavior.ts +278 -0
  512. package/src/routing/compatibility/catalog.ts +99 -0
  513. package/src/routing/compatibility/endpoint.ts +52 -0
  514. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  515. package/src/routing/compatibility/policy.ts +181 -0
  516. package/src/routing/compatibility/provider-slot.ts +56 -0
  517. package/src/routing/compatibility/reader.ts +110 -0
  518. package/src/routing/compatibility/subject.ts +191 -0
  519. package/src/routing/compatibility/types.ts +64 -0
  520. package/src/routing/compatibility/version.ts +104 -0
  521. package/src/routing/cost.ts +77 -0
  522. package/src/routing/evaluator.ts +495 -0
  523. package/src/routing/health.ts +412 -0
  524. package/src/routing/history/cursor.ts +43 -0
  525. package/src/routing/history/indexer.ts +605 -0
  526. package/src/routing/history/schema.ts +72 -0
  527. package/src/routing/profile-namespace.ts +15 -0
  528. package/src/routing/profile.ts +547 -0
  529. package/src/routing/quota.ts +145 -0
  530. package/src/routing/request-evidence.ts +45 -0
  531. package/src/routing/trace.ts +776 -0
  532. package/src/server/adapter-resolve.ts +2 -29
  533. package/src/server/auth-cors.ts +267 -46
  534. package/src/server/background-lifecycle.ts +182 -0
  535. package/src/server/chat-completions.ts +130 -56
  536. package/src/server/chat-native-sse.ts +331 -0
  537. package/src/server/chat-native.ts +426 -0
  538. package/src/server/claude-messages.ts +159 -43
  539. package/src/server/direct-local-http.ts +347 -0
  540. package/src/server/effort-policy.ts +18 -0
  541. package/src/server/github-copilot-responses-repair.ts +338 -0
  542. package/src/server/gui-static.ts +39 -10
  543. package/src/server/images.ts +94 -12
  544. package/src/server/index.ts +865 -181
  545. package/src/server/lifecycle.ts +284 -13
  546. package/src/server/live.ts +136 -17
  547. package/src/server/local-management-read-client.ts +90 -0
  548. package/src/server/local-provider-reload-client.ts +137 -0
  549. package/src/server/management/agent-settings-routes.ts +398 -103
  550. package/src/server/management/api-key-usage.ts +31 -5
  551. package/src/server/management/body.ts +6 -0
  552. package/src/server/management/combo-routes.ts +62 -24
  553. package/src/server/management/config-routes.ts +464 -51
  554. package/src/server/management/context.ts +80 -2
  555. package/src/server/management/integration-routes.ts +498 -0
  556. package/src/server/management/lab-automation-routes.ts +206 -0
  557. package/src/server/management/lab-routes.ts +563 -0
  558. package/src/server/management/logs-usage-routes.ts +101 -32
  559. package/src/server/management/model-routes.ts +189 -131
  560. package/src/server/management/model-rows.ts +163 -0
  561. package/src/server/management/native-integration-routes.ts +769 -0
  562. package/src/server/management/oauth-account-routes.ts +80 -4
  563. package/src/server/management/provider-capability-config.ts +48 -0
  564. package/src/server/management/provider-routes.ts +764 -157
  565. package/src/server/management/request-history-routes.ts +191 -0
  566. package/src/server/management/routing-analytics-routes.ts +74 -0
  567. package/src/server/management/routing-profile-routes.ts +380 -0
  568. package/src/server/management/shared.ts +27 -11
  569. package/src/server/management/sidebar-routes.ts +47 -31
  570. package/src/server/management/storage-log-guard-routes.ts +186 -0
  571. package/src/server/management/sync-response.ts +69 -0
  572. package/src/server/management/system-restart.ts +288 -32
  573. package/src/server/management/system-routes.ts +77 -0
  574. package/src/server/management/usage-summary-cache.ts +9 -1
  575. package/src/server/management/vision-sidecar-options.ts +167 -0
  576. package/src/server/management/web-search-sidecar-options.ts +120 -0
  577. package/src/server/management-api.ts +115 -14
  578. package/src/server/management-auth.ts +220 -5
  579. package/src/server/passive-route-linker.ts +66 -0
  580. package/src/server/ports.ts +41 -1
  581. package/src/server/proxy-liveness.ts +132 -5
  582. package/src/server/readiness.ts +99 -0
  583. package/src/server/relay-eager.ts +82 -42
  584. package/src/server/relay.ts +236 -76
  585. package/src/server/request-decompress.ts +113 -6
  586. package/src/server/request-log.ts +235 -22
  587. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  588. package/src/server/responses/agent-task-recovery.ts +465 -0
  589. package/src/server/responses/collaboration.ts +204 -35
  590. package/src/server/responses/compact.ts +442 -55
  591. package/src/server/responses/core.ts +2872 -331
  592. package/src/server/responses/empty-completion-guard.ts +276 -0
  593. package/src/server/responses/encrypted-payload.ts +62 -39
  594. package/src/server/responses/fetch-helpers.ts +79 -4
  595. package/src/server/responses/input-admission.ts +185 -0
  596. package/src/server/responses/pacing-overload.ts +13 -0
  597. package/src/server/responses/policy-fallback.ts +178 -0
  598. package/src/server/responses/responses-field-backfill.ts +251 -0
  599. package/src/server/responses/terminal-guard.ts +26 -5
  600. package/src/server/responses/upstream-error.ts +5 -0
  601. package/src/server/responses/ws-upstream.ts +308 -0
  602. package/src/server/responses-custom-tool-repair.ts +282 -0
  603. package/src/server/responses-item-id-repair.ts +54 -6
  604. package/src/server/responses-json-events.ts +90 -0
  605. package/src/server/responses-model-rewrite.ts +29 -0
  606. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  607. package/src/server/responses-snapshot-repair.ts +621 -0
  608. package/src/server/responses-terminal-repair.ts +342 -0
  609. package/src/server/responses-tool-search-repair.ts +267 -0
  610. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  611. package/src/server/responses.ts +18 -2
  612. package/src/server/search.ts +78 -13
  613. package/src/server/sse-frame-buffer.ts +292 -0
  614. package/src/server/sse-payload-rewrite.ts +110 -22
  615. package/src/server/startup-action-control.ts +8 -1
  616. package/src/server/startup-health-cache.ts +19 -1
  617. package/src/server/system-env.ts +80 -9
  618. package/src/server/ws-bridge.ts +39 -38
  619. package/src/service-manager-probe.ts +892 -0
  620. package/src/service.ts +1111 -90
  621. package/src/sidecar/auth.ts +92 -0
  622. package/src/sidecar/candidates.ts +83 -0
  623. package/src/storage/cleanup.ts +2 -2
  624. package/src/storage/scanner.ts +1 -1
  625. package/src/storage/worker-lifecycle.ts +14 -14
  626. package/src/tray/windows-tray.ps1 +83 -9
  627. package/src/tray/windows.ts +43 -16
  628. package/src/types/accounts.ts +37 -0
  629. package/src/types/config.ts +845 -0
  630. package/src/types/provider.ts +545 -0
  631. package/src/types/request.ts +384 -0
  632. package/src/types/tools.ts +131 -0
  633. package/src/types/wire.ts +80 -0
  634. package/src/types.ts +104 -1236
  635. package/src/update/index.ts +32 -19
  636. package/src/update/job.ts +442 -67
  637. package/src/update/notify.ts +12 -6
  638. package/src/update/npm-cache-preflight.d.mts +47 -0
  639. package/src/update/npm-cache-preflight.mjs +201 -0
  640. package/src/update/transactional-install.d.mts +22 -0
  641. package/src/update/transactional-install.mjs +259 -0
  642. package/src/usage/cost.ts +0 -0
  643. package/src/usage/expected-prices.ts +268 -16
  644. package/src/usage/log.ts +606 -41
  645. package/src/usage/summary.ts +177 -9
  646. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  647. package/src/usage/user-cost-overlays.ts +314 -0
  648. package/src/vision/anthropic-describe.ts +10 -6
  649. package/src/vision/backends.ts +97 -0
  650. package/src/vision/describe.ts +9 -3
  651. package/src/vision/eligibility.ts +250 -0
  652. package/src/vision/index.ts +238 -24
  653. package/src/vision/reasoning.ts +55 -0
  654. package/src/vision/routed-describe.ts +175 -0
  655. package/src/vision/timeout-bounds.ts +9 -0
  656. package/src/web-search/anthropic-executor.ts +13 -7
  657. package/src/web-search/backends.ts +108 -0
  658. package/src/web-search/exa-executor.ts +88 -0
  659. package/src/web-search/executor.ts +11 -3
  660. package/src/web-search/gemini-executor.ts +141 -0
  661. package/src/web-search/index.ts +150 -15
  662. package/src/web-search/loop.ts +279 -50
  663. package/src/web-search/parse.ts +125 -30
  664. package/src/web-search/sources.ts +60 -0
  665. package/src/web-search/xai-executor.ts +219 -0
  666. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  667. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  668. package/gui/dist/provider-icons/antigravity.svg +0 -1
  669. package/gui/dist/provider-icons/claude.svg +0 -1
  670. package/gui/dist/provider-icons/copilot.svg +0 -1
  671. package/gui/dist/provider-icons/cursor.svg +0 -2
  672. package/gui/dist/provider-icons/gemini.svg +0 -1
  673. package/gui/dist/provider-icons/grok-color.svg +0 -1
  674. package/gui/dist/provider-icons/kiro.svg +0 -14
  675. package/src/cli/internal-dispatch.ts +0 -20
@@ -15,20 +15,38 @@ import { CODEX_HOME, getCodexHome } from "./paths";
15
15
  import { CODEX_UNKNOWN_USAGE_SCORE, getAccountQuota } from "./quota";
16
16
  import {
17
17
  canAcquireCodexQuotaProbeLease,
18
+ codexQuotaScopeForModel,
18
19
  computeCodexUsageScore,
20
+ getCodexQuotaHealthSnapshot,
21
+ getEffectiveActiveCodexAccountId,
19
22
  getPoolAccountPlan,
20
23
  isCodexAccountInCooldown,
21
24
  } from "./routing";
22
- import { isCodexAccountUsable } from "./account-usability";
25
+ import {
26
+ isCodexAccountUsable,
27
+ type CodexAccountUsabilityOptions,
28
+ } from "./account-usability";
23
29
  import { isCodexAccountPaused } from "./account-pause";
24
30
  import { slugEquals } from "../providers/slug-codec";
25
31
  import { isThreadSpawnRequest } from "../server/effort-policy";
26
32
  import { PROVIDER_REGISTRY } from "../providers/registry";
27
- import { isCanonicalOpenAiForwardProvider } from "../providers/openai-tiers";
33
+ import {
34
+ CODEX_FORWARD_BASE_URL,
35
+ OPENAI_CODEX_PROVIDER_ID,
36
+ isCanonicalOpenAiForwardProvider,
37
+ } from "../providers/openai-tiers";
28
38
  import { routeModel, type RouteResult } from "../router";
29
39
  import { sweepExpiredOnWrite } from "../lib/state-store-sweeper";
40
+ import { codexAccountNamespaceForModel } from "./account-namespace-match";
41
+ import {
42
+ getUpstreamHostHealth,
43
+ normalizeUpstreamHostCircuitThreshold,
44
+ upstreamHostHealthKey,
45
+ } from "./upstream-host-health";
30
46
  export const DEFAULT_SUBAGENT_MODEL_FALLBACK_POLL_MS = 60_000;
31
47
 
48
+ const CODEX_FORWARD_ORIGIN = new URL(CODEX_FORWARD_BASE_URL).origin.toLowerCase();
49
+
32
50
  type SubagentQuotaPrimeFn = (config: OcxConfig, reason: string) => Promise<void>;
33
51
  let subagentQuotaPrimeForTests: SubagentQuotaPrimeFn | null = null;
34
52
  let quotaPrimeInFlight: Promise<void> | null = null;
@@ -68,6 +86,13 @@ function isDisabledFallbackModel(model: string, config: OcxConfig): boolean {
68
86
  const slash = model.indexOf("/");
69
87
  const provider = model.slice(0, slash);
70
88
  const modelId = model.slice(slash + 1);
89
+ if (codexAccountNamespaceForModel(config.codexAccountNamespaces, model)) {
90
+ return disabled.some(stored =>
91
+ stored === model
92
+ || stored === modelId
93
+ || slugEquals(stored, "openai", modelId)
94
+ );
95
+ }
71
96
  return disabled.some(stored => stored === model || slugEquals(stored, provider, modelId));
72
97
  }
73
98
 
@@ -79,13 +104,27 @@ function pollIntervalMs(config: OcxConfig): number {
79
104
  return configured;
80
105
  }
81
106
 
82
- function normalizedChain(primary: string, config: OcxConfig, extra: readonly string[] = []): string[] {
107
+ function fallbackChainKey(model: string, namespaces: unknown): string {
108
+ const selector = codexAccountNamespaceForModel(namespaces, model);
109
+ if (!selector) return JSON.stringify(["model", model.toLowerCase()]);
110
+ const slash = model.indexOf("/");
111
+ // Selector keys are exact-case account boundaries. Keep that segment distinct while
112
+ // retaining legacy case-insensitive de-duplication for the native model suffix.
113
+ return JSON.stringify(["account", selector, model.slice(slash + 1).toLowerCase()]);
114
+ }
115
+
116
+ function normalizedChain(
117
+ primary: string,
118
+ config: OcxConfig,
119
+ extra: readonly string[] = [],
120
+ trailing: readonly string[] = [],
121
+ ): string[] {
83
122
  const chain: string[] = [];
84
123
  const seen = new Set<string>();
85
124
  const push = (model: string | undefined) => {
86
125
  if (!model || model.trim() === "") return;
87
126
  const trimmed = model.trim();
88
- const key = trimmed.toLowerCase();
127
+ const key = fallbackChainKey(trimmed, config.codexAccountNamespaces);
89
128
  if (seen.has(key)) return;
90
129
  seen.add(key);
91
130
  chain.push(trimmed);
@@ -93,6 +132,7 @@ function normalizedChain(primary: string, config: OcxConfig, extra: readonly str
93
132
  push(primary);
94
133
  for (const model of extra) push(model);
95
134
  for (const model of config.subagentModelFallback ?? []) push(model);
135
+ for (const model of trailing) push(model);
96
136
  return chain;
97
137
  }
98
138
 
@@ -109,8 +149,14 @@ function quotaThreshold(config: OcxConfig): number {
109
149
  return threshold > 0 ? threshold : Number.POSITIVE_INFINITY;
110
150
  }
111
151
 
152
+ /**
153
+ * The account routing would actually use, not just the persisted operator
154
+ * selection: round-robin, fill-first, failover, and priority preemption all
155
+ * move the cursor in memory only, so reading the raw field would check quota
156
+ * against an account this request is not going to touch.
157
+ */
112
158
  function activeCodexAccountId(config: OcxConfig): string | null {
113
- return config.activeCodexAccountId ?? null;
159
+ return getEffectiveActiveCodexAccountId(config) ?? null;
114
160
  }
115
161
 
116
162
  /**
@@ -127,9 +173,18 @@ function resolvePoolFallbackAccountId(
127
173
  return activeCodexAccountId(config);
128
174
  }
129
175
 
176
+ function resolveRouteFallbackAccountId(
177
+ route: RouteResult | null,
178
+ config: OcxConfig,
179
+ accountId?: string | null,
180
+ ): string | null {
181
+ return route?.codexAccountId ?? resolvePoolFallbackAccountId(config, accountId);
182
+ }
183
+
130
184
  function isRoutableFallbackModel(model: string, config: OcxConfig): boolean {
131
185
  const slash = model.indexOf("/");
132
186
  if (slash > 0) {
187
+ if (codexAccountNamespaceForModel(config.codexAccountNamespaces, model)) return true;
133
188
  const providerName = model.slice(0, slash);
134
189
  if (!hasOwnProvider(config.providers, providerName)) {
135
190
  // Allow well-known "vendor/model" ids (e.g. anthropic/claude-*) to flow as
@@ -150,7 +205,7 @@ export function isNativeModelQuotaExhausted(
150
205
  ): boolean {
151
206
  const route = tryRouteFallbackModel(config, model);
152
207
  if (!route || !isPoolCodexRoute(route)) return false;
153
- const resolvedAccountId = resolvePoolFallbackAccountId(config, accountId);
208
+ const resolvedAccountId = resolveRouteFallbackAccountId(route, config, accountId);
154
209
  if (!resolvedAccountId) return false;
155
210
  const quota = getAccountQuota(resolvedAccountId);
156
211
  const usage = computeCodexUsageScore(quota, getPoolAccountPlan(config, resolvedAccountId));
@@ -167,7 +222,11 @@ export function isModelHealthBlocked(
167
222
  const route = tryRouteFallbackModel(config, model);
168
223
  const poolScoped = !!route && isPoolCodexRoute(route);
169
224
  const health = modelHealth.get(
170
- healthKey(model, resolvePoolFallbackAccountId(config, accountId), poolScoped),
225
+ healthKey(
226
+ model,
227
+ resolveRouteFallbackAccountId(route, config, accountId),
228
+ poolScoped,
229
+ ),
171
230
  );
172
231
  return !!health && health.unavailableUntil > now;
173
232
  }
@@ -177,6 +236,7 @@ export function isSubagentModelUnavailable(
177
236
  config: OcxConfig,
178
237
  accountId?: string | null,
179
238
  now = Date.now(),
239
+ accountUsabilityOptions?: CodexAccountUsabilityOptions,
180
240
  ): boolean {
181
241
  if (isDisabledFallbackModel(model, config)) return true;
182
242
  if (!isRoutableFallbackModel(model, config)) return true;
@@ -187,11 +247,17 @@ export function isSubagentModelUnavailable(
187
247
 
188
248
  // Pool candidates need a usable account. Derive requirement from the resolved
189
249
  // route (canonical openai defaults to pool even when codexAccountMode is omitted).
190
- const resolvedAccountId = resolvePoolFallbackAccountId(config, accountId);
250
+ const resolvedAccountId = resolveRouteFallbackAccountId(route, config, accountId);
191
251
  if (!resolvedAccountId) return true;
192
252
  if (isCodexAccountPaused(config, resolvedAccountId)) return true;
193
- if (!isCodexAccountUsable(config, resolvedAccountId)) return true;
194
- if (
253
+ if (!isCodexAccountUsable(config, resolvedAccountId, accountUsabilityOptions)) return true;
254
+ if (route.codexAccountId !== undefined) {
255
+ // An account-qualified route is pinned and cannot consume Pool's recovery-probe
256
+ // escape hatch. Honor both account-wide and model-scoped cooldowns so fallback
257
+ // advances instead of selecting a candidate that exact auth will reject.
258
+ const quotaScope = codexQuotaScopeForModel(route.modelId);
259
+ if (getCodexQuotaHealthSnapshot(resolvedAccountId, quotaScope, now) !== null) return true;
260
+ } else if (
195
261
  isCodexAccountInCooldown(resolvedAccountId, now)
196
262
  && !canAcquireCodexQuotaProbeLease(resolvedAccountId, now)
197
263
  ) {
@@ -207,8 +273,10 @@ export function selectAvailableSubagentModel(
207
273
  accountId?: string | null,
208
274
  now = Date.now(),
209
275
  nativeFallbackOnly = false,
276
+ accountUsabilityOptions?: CodexAccountUsabilityOptions,
277
+ trailingFallback: readonly string[] = [],
210
278
  ): { model: string; rewritten: boolean; skipped: string[] } {
211
- const chain = normalizedChain(primary, config, extraFallback);
279
+ const chain = normalizedChain(primary, config, extraFallback, trailingFallback);
212
280
  const skipped: string[] = [];
213
281
  for (const candidate of chain) {
214
282
  if (nativeFallbackOnly) {
@@ -218,7 +286,7 @@ export function selectAvailableSubagentModel(
218
286
  continue;
219
287
  }
220
288
  }
221
- if (isSubagentModelUnavailable(candidate, config, accountId, now)) {
289
+ if (isSubagentModelUnavailable(candidate, config, accountId, now, accountUsabilityOptions)) {
222
290
  skipped.push(candidate);
223
291
  continue;
224
292
  }
@@ -240,7 +308,11 @@ export function noteSubagentModelFailure(
240
308
  const route = tryRouteFallbackModel(config, model);
241
309
  const poolScoped = !!route && isPoolCodexRoute(route);
242
310
  modelHealth.set(
243
- healthKey(model, resolvePoolFallbackAccountId(config, accountId), poolScoped),
311
+ healthKey(
312
+ model,
313
+ resolveRouteFallbackAccountId(route, config, accountId),
314
+ poolScoped,
315
+ ),
244
316
  {
245
317
  unavailableUntil: now + interval,
246
318
  reason: "quota_exhausted",
@@ -324,13 +396,16 @@ export function readCodexAgentModel(role: string, codexHome = CODEX_HOME): strin
324
396
  export function resolveAgentModelFallbackForPrimary(
325
397
  primary: string,
326
398
  codexHome = CODEX_HOME,
399
+ namespaces?: unknown,
327
400
  ): string[] {
328
401
  const merged: string[] = [];
329
402
  const seen = new Set<string>();
330
403
  const push = (model: string | null | undefined) => {
331
404
  if (!model || model.trim() === "") return;
332
405
  const trimmed = model.trim();
333
- const key = trimmed.toLowerCase();
406
+ // Match global-chain de-dupe: keep account-selector prefixes case-sensitive when
407
+ // those selectors are configured, while ordinary provider/model ids stay case-insensitive.
408
+ const key = fallbackChainKey(trimmed, namespaces);
334
409
  if (seen.has(key)) return;
335
410
  seen.add(key);
336
411
  merged.push(trimmed);
@@ -343,18 +418,66 @@ export function resolveAgentModelFallbackForPrimary(
343
418
  return merged;
344
419
  }
345
420
 
421
+ function subagentQuotaPrimeBlockedByHostCircuit(config: OcxConfig): boolean {
422
+ if (normalizeUpstreamHostCircuitThreshold(config.upstreamHostCircuitThreshold) === 0) return false;
423
+ const key = upstreamHostHealthKey(OPENAI_CODEX_PROVIDER_ID, CODEX_FORWARD_ORIGIN);
424
+ return getUpstreamHostHealth(key)?.cooldownUntil !== undefined;
425
+ }
426
+
427
+ /**
428
+ * Per-primary-model fallback chains from opencodex config (#1190).
429
+ *
430
+ * Storing `model_fallback` inside `$CODEX_HOME/agents/*.toml` makes Codex >= 0.146
431
+ * reject the whole role file as an unknown field. The config-keyed map is the
432
+ * supported home for that metadata; keys match the requested primary model id,
433
+ * using the same raw/encoded slug tolerance as the TOML role lookup.
434
+ */
435
+ export function resolveConfiguredModelFallbackForPrimary(
436
+ primary: string,
437
+ config: OcxConfig,
438
+ ): string[] {
439
+ const byModel = config.subagentModelFallbackByModel;
440
+ if (!byModel || typeof byModel !== "object") return [];
441
+ const entries: string[] = [];
442
+ const seen = new Set<string>();
443
+ const push = (model: string) => {
444
+ const trimmed = model.trim();
445
+ if (trimmed === "") return;
446
+ const key = fallbackChainKey(trimmed, config.codexAccountNamespaces);
447
+ if (seen.has(key)) return;
448
+ seen.add(key);
449
+ entries.push(trimmed);
450
+ };
451
+ for (const [key, chain] of Object.entries(byModel)) {
452
+ if (!slugsEquivalent(key, primary)) continue;
453
+ for (const model of chain) push(model);
454
+ }
455
+ return entries;
456
+ }
457
+
346
458
  /**
347
459
  * Best-effort quota refresh before subagent model selection.
348
460
  * Concurrent callers share one in-flight promise. The success TTL is updated only
349
461
  * after a successful refresh so failures remain retryable. Errors are swallowed so
350
- * spawn routing can continue.
462
+ * spawn routing can continue. When the canonical ChatGPT origin is circuit-blocked,
463
+ * cached quota is used instead of sending credential-bearing usage probes to the
464
+ * same origin before the request's final host admission check.
351
465
  */
352
- export function maybePrimeSubagentQuota(config: OcxConfig, now = Date.now()): Promise<void> {
466
+ export function maybePrimeSubagentQuota(
467
+ config: OcxConfig,
468
+ now = Date.now(),
469
+ options: { nativeMainReadsForbidden?: boolean } = {},
470
+ ): Promise<void> {
471
+ if (options.nativeMainReadsForbidden) return Promise.resolve();
472
+ if (subagentQuotaPrimeBlockedByHostCircuit(config)) return Promise.resolve();
353
473
  if (quotaPrimeInFlight) return quotaPrimeInFlight;
354
474
  if (!shouldPrimeSubagentQuota(config, now)) return Promise.resolve();
355
475
 
356
476
  quotaPrimeInFlight = (async () => {
357
477
  try {
478
+ // Re-check after claiming single-flight ownership so a circuit opened by
479
+ // a concurrent request cannot race us into a fresh usage-probe pass.
480
+ if (subagentQuotaPrimeBlockedByHostCircuit(config)) return;
358
481
  if (subagentQuotaPrimeForTests) {
359
482
  await subagentQuotaPrimeForTests(config, "subagent-spawn");
360
483
  } else {
@@ -391,18 +514,28 @@ export function applySubagentModelFallback(
391
514
  accountId?: string | null,
392
515
  now = Date.now(),
393
516
  nativeFallbackOnly = false,
517
+ accountUsabilityOptions?: CodexAccountUsabilityOptions,
394
518
  ): { from?: string; to?: string; skipped?: string[] } | null {
395
519
  if (!isThreadSpawnRequest(headers)) return null;
396
- const roleFallback = resolveAgentModelFallbackForPrimary(parsed.modelId, getCodexHome());
520
+ const tomlRoleFallback = resolveAgentModelFallbackForPrimary(
521
+ parsed.modelId,
522
+ getCodexHome(),
523
+ config.codexAccountNamespaces,
524
+ );
525
+ // Config-keyed chains are the supported per-role home (#1190); TOML `model_fallback`
526
+ // stays readable for backwards compatibility with homes written before Codex 0.146.
527
+ const configuredFallback = resolveConfiguredModelFallbackForPrimary(parsed.modelId, config);
397
528
  const globalFallback = config.subagentModelFallback ?? [];
398
- if (globalFallback.length === 0 && roleFallback.length === 0) return null;
529
+ if (globalFallback.length === 0 && configuredFallback.length === 0 && tomlRoleFallback.length === 0) return null;
399
530
  const selection = selectAvailableSubagentModel(
400
531
  parsed.modelId,
401
532
  config,
402
- roleFallback,
533
+ configuredFallback,
403
534
  accountId,
404
535
  now,
405
536
  nativeFallbackOnly,
537
+ accountUsabilityOptions,
538
+ tomlRoleFallback,
406
539
  );
407
540
  if (!selection.rewritten) return selection.skipped.length > 0
408
541
  ? { from: parsed.modelId, to: parsed.modelId, skipped: selection.skipped }
@@ -419,33 +552,192 @@ export function subagentFallbackGuidanceText(config: OcxConfig): string {
419
552
  return ` Subagent model fallback chain (priority order): ${quoted}. When the primary model is quota-exhausted, opencodex rewrites thread_spawn requests to the next available model automatically.`;
420
553
  }
421
554
 
422
- const TOML_STRING_ARRAY = /^(model_fallback)\s*=\s*\[(.*)\]\s*$/s;
555
+ const TOML_MODEL_FALLBACK_KEY = /^\s*(?:model_fallback|"model_fallback"|'model_fallback')\s*=/;
556
+
557
+ type TomlModelFallbackField = { present: false; value: null } | { present: true; value: string[] | null };
558
+
559
+ type TomlScanState = {
560
+ inMultilineString: '"""' | "'''" | null;
561
+ arrayDepth: number;
562
+ };
563
+
564
+ /** Track TOML strings, comments, and array brackets on one line. */
565
+ function scanTomlLine(line: string, state: TomlScanState): void {
566
+ let i = 0;
567
+ while (i < line.length) {
568
+ const ch = line[i]!;
569
+ if (ch === "#") return;
570
+ if (ch === '"' || ch === "'") {
571
+ const delimiter = ch.repeat(3);
572
+ if (line.startsWith(delimiter, i)) {
573
+ const end = findTomlMultilineStringEnd(line, i + 3, ch);
574
+ if (end === -1) {
575
+ state.inMultilineString = delimiter as '"""' | "'''";
576
+ return;
577
+ }
578
+ i = end + 3;
579
+ continue;
580
+ }
581
+ i++;
582
+ while (i < line.length) {
583
+ if (ch === '"' && line[i] === "\\") {
584
+ i += 2;
585
+ continue;
586
+ }
587
+ if (line[i] === ch) break;
588
+ i++;
589
+ }
590
+ i++;
591
+ continue;
592
+ }
593
+ if (ch === "[") state.arrayDepth++;
594
+ else if (ch === "]") state.arrayDepth = Math.max(0, state.arrayDepth - 1);
595
+ i++;
596
+ }
597
+ }
598
+
599
+ /** Parse a TOML string starting at `start`; returns the decoded value and end offset. */
600
+ function parseTomlStringAt(text: string, start: number): { value: string; end: number } | null {
601
+ const quote = text[start]!;
602
+ const delimiter = quote.repeat(3);
603
+ if (text.startsWith(delimiter, start)) {
604
+ const end = findTomlMultilineStringEnd(text, start + 3, quote);
605
+ if (end === -1) return null;
606
+ let value = text.slice(start + 3, end).trim();
607
+ if (quote === '"') value = value.replace(/\\"/g, '"').replace(/\\\\/g, "\\");
608
+ return { value, end: end + 3 };
609
+ }
610
+ let i = start + 1;
611
+ let value = "";
612
+ while (i < text.length) {
613
+ const ch = text[i]!;
614
+ if (quote === '"' && ch === "\\") {
615
+ const next = text[i + 1];
616
+ if (next === '"' || next === "\\") {
617
+ value += next;
618
+ i += 2;
619
+ continue;
620
+ }
621
+ }
622
+ if (ch === quote) return { value, end: i + 1 };
623
+ value += ch;
624
+ i++;
625
+ }
626
+ return null;
627
+ }
628
+
629
+ function findTomlMultilineStringEnd(text: string, from: number, quote: string): number {
630
+ const delimiter = quote.repeat(3);
631
+ let index = text.indexOf(delimiter, from);
632
+ while (index !== -1) {
633
+ if (quote === "'") return index;
634
+ let backslashes = 0;
635
+ for (let j = index - 1; j >= 0 && text[j] === "\\"; j--) backslashes += 1;
636
+ if (backslashes % 2 === 0) return index;
637
+ index = text.indexOf(delimiter, index + 1);
638
+ }
639
+ return -1;
640
+ }
423
641
 
424
- function parseTomlStringArray(raw: string): string[] {
425
- const matches = [...raw.matchAll(/"((?:\\.|[^"\\])*)"/g)];
426
- return matches.map(match => match[1]!.replace(/\\"/g, "\""));
642
+ /** After an array close, only horizontal whitespace, an inline comment, or the line end is valid. */
643
+ function isValidTomlArrayTail(text: string, from: number): boolean {
644
+ let i = from;
645
+ while (i < text.length && (text[i] === " " || text[i] === "\t")) i += 1;
646
+ if (i >= text.length || text[i] === "\n" || text[i] === "\r") return true;
647
+ if (text[i] === "#") {
648
+ while (i < text.length && text[i] !== "\n") i += 1;
649
+ return true;
650
+ }
651
+ return false;
652
+ }
653
+
654
+ /** Parse a TOML string-array value; null when the value is not a well-formed array of strings. */
655
+ function parseTomlStringArrayValue(text: string): string[] | null {
656
+ const values: string[] = [];
657
+ let i = 0;
658
+ const skipIgnored = () => {
659
+ while (i < text.length) {
660
+ const ch = text[i]!;
661
+ if (ch === "#") {
662
+ while (i < text.length && text[i] !== "\n") i += 1;
663
+ continue;
664
+ }
665
+ if (/\s/.test(ch)) {
666
+ i += 1;
667
+ continue;
668
+ }
669
+ break;
670
+ }
671
+ };
672
+ skipIgnored();
673
+ if (text[i] !== "[") return null;
674
+ i += 1;
675
+ let expectValue = true;
676
+ for (;;) {
677
+ skipIgnored();
678
+ if (i >= text.length) return null;
679
+ const ch = text[i]!;
680
+ if (ch === "]") {
681
+ if (!isValidTomlArrayTail(text, i + 1)) return null;
682
+ return values;
683
+ }
684
+ if (ch === ",") {
685
+ if (expectValue) return null; // leading or doubled comma
686
+ expectValue = true;
687
+ i += 1;
688
+ continue;
689
+ }
690
+ if (ch === '"' || ch === "'") {
691
+ if (!expectValue) return null; // adjacent strings must be comma-separated
692
+ const parsed = parseTomlStringAt(text, i);
693
+ if (!parsed) return null;
694
+ values.push(parsed.value);
695
+ i = parsed.end;
696
+ expectValue = false;
697
+ continue;
698
+ }
699
+ return null;
700
+ }
427
701
  }
428
702
 
429
- function parseTomlModelFallback(content: string): string[] | null {
430
- const match = content.match(/^\s*model_fallback\s*=\s*\[(.*?)\]\s*$/ms);
431
- if (!match) return null;
432
- return parseTomlStringArray(match[1] ?? "");
703
+ /**
704
+ * TOML-aware, presence-aware parse of the `model_fallback` field. Quoted keys
705
+ * are recognized, and text inside strings (including multiline strings) is
706
+ * never treated as a key. `present` is true whenever the key exists, even when
707
+ * the value is not a readable string array; `value` is null in that case.
708
+ */
709
+ function parseTomlModelFallbackField(content: string): TomlModelFallbackField {
710
+ const lines = content.split(/\r?\n/);
711
+ const state: TomlScanState = { inMultilineString: null, arrayDepth: 0 };
712
+ for (let i = 0; i < lines.length; i += 1) {
713
+ const line = lines[i]!;
714
+ if (state.inMultilineString) {
715
+ const end = findTomlMultilineStringEnd(line, 0, state.inMultilineString[0]!);
716
+ if (end === -1) continue;
717
+ state.inMultilineString = null;
718
+ scanTomlLine(line.slice(end + 3), state);
719
+ continue;
720
+ }
721
+ if (state.arrayDepth === 0) {
722
+ const key = line.match(TOML_MODEL_FALLBACK_KEY);
723
+ if (key) {
724
+ const rest = `${line.slice(key[0].length)}\n${lines.slice(i + 1).join("\n")}`;
725
+ return { present: true, value: parseTomlStringArrayValue(rest) };
726
+ }
727
+ }
728
+ scanTomlLine(line, state);
729
+ }
730
+ return { present: false, value: null };
433
731
  }
434
732
 
435
733
  export function readAgentModelFallback(filePath: string): string[] | null {
436
734
  try {
437
735
  const content = readFileSync(filePath, "utf8");
438
- const multiline = parseTomlModelFallback(content);
439
- if (multiline) return multiline;
440
- for (const line of content.split(/\r?\n/)) {
441
- const match = line.match(TOML_STRING_ARRAY);
442
- if (!match) continue;
443
- return parseTomlStringArray(match[2] ?? "");
444
- }
736
+ const parsed = parseTomlModelFallbackField(content);
737
+ return parsed.present ? parsed.value : null;
445
738
  } catch {
446
739
  return null;
447
740
  }
448
- return null;
449
741
  }
450
742
 
451
743
  export function readCodexAgentModelFallback(role: string, codexHome = CODEX_HOME): string[] {
@@ -454,6 +746,29 @@ export function readCodexAgentModelFallback(role: string, codexHome = CODEX_HOME
454
746
  return readAgentModelFallback(file) ?? [];
455
747
  }
456
748
 
749
+ /**
750
+ * True when the role TOML carries a `model_fallback` key, even an empty array.
751
+ * Presence is what matters for the doctor scan: Codex >= 0.146 rejects the
752
+ * field as unknown and skips the whole role regardless of its value. Uses the
753
+ * same TOML-aware parse as the fallback-reading path, so quoted keys count and
754
+ * text inside string literals does not.
755
+ */
756
+ export function hasCodexAgentModelFallbackField(role: string, codexHome = CODEX_HOME): boolean {
757
+ const file = join(codexHome, "agents", `${role}.toml`);
758
+ if (!existsSync(file)) return false;
759
+ try {
760
+ const content = readFileSync(file, "utf8");
761
+ return parseTomlModelFallbackField(content).present;
762
+ } catch {
763
+ return false;
764
+ }
765
+ }
766
+
767
+ /** Roles whose TOML still carries `model_fallback`, including empty arrays. */
768
+ export function scanCodexAgentRolesWithTomlModelFallback(codexHome = CODEX_HOME): string[] {
769
+ return listCodexAgentRoles(codexHome).filter(role => hasCodexAgentModelFallbackField(role, codexHome));
770
+ }
771
+
457
772
  export function listCodexAgentRoles(codexHome = CODEX_HOME): string[] {
458
773
  const dir = join(codexHome, "agents");
459
774
  if (!existsSync(dir)) return [];