@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
@@ -1,9 +1,11 @@
1
1
  import { resolveEnvValue } from "../config";
2
2
  import {
3
+ CodexPoolAuthenticationError,
3
4
  headersForCodexAuthContext,
4
5
  hasCallerCodexBearer,
5
6
  isCodexAuthContextUsable,
6
7
  resolveCodexAuthContext,
8
+ type CodexAccountSelectionAdmission,
7
9
  type CodexAuthContext,
8
10
  } from "../codex/auth-context";
9
11
  import { recordCodexUpstreamOutcome, type CodexUpstreamOutcome } from "../codex/routing";
@@ -11,6 +13,7 @@ import { extractAccountId } from "../oauth/chatgpt";
11
13
  import { ForwardAdmissionCredentialError, validateForwardAdmissionCredential } from "../server/auth-cors";
12
14
  import type { CodexAccountMode, OcxConfig, OcxProviderConfig } from "../types";
13
15
  import {
16
+ CODEX_FORWARD_BASE_URL,
14
17
  isCanonicalOpenAiForwardProvider,
15
18
  OPENAI_API_PROVIDER_ID,
16
19
  OPENAI_CODEX_PROVIDER_ID,
@@ -29,6 +32,17 @@ export interface ResolvedOpenAiForwardSidecar extends OpenAiForwardSidecarCandid
29
32
  recordOutcome?: (outcome: CodexUpstreamOutcome) => void;
30
33
  }
31
34
 
35
+ /**
36
+ * Server-resolved exact account selection for a ChatGPT sidecar call.
37
+ * `accountId` must come from a validated account-qualified route; request headers
38
+ * are never trusted as account ids. `modelId` is the sidecar's actual upstream
39
+ * model so cooldown admission and outcome recording use the correct quota scope.
40
+ */
41
+ export interface ExactOpenAiSidecarAccount {
42
+ accountId: string;
43
+ modelId: string;
44
+ }
45
+
32
46
  export interface OpenAiImagesProviderSelection {
33
47
  forwardCandidates: OpenAiForwardSidecarCandidate[];
34
48
  keyed?: {
@@ -41,11 +55,24 @@ export interface OpenAiImagesProviderSelection {
41
55
 
42
56
  export function listOpenAiForwardSidecarCandidates(config: OcxConfig): OpenAiForwardSidecarCandidate[] {
43
57
  const provider = config.providers[OPENAI_CODEX_PROVIDER_ID];
44
- if (!provider || provider.disabled === true || !isCanonicalOpenAiForwardProvider(provider)) return [];
58
+ if (!provider || provider.disabled === true) return [];
59
+ // The built-in registry defaults an omitted authMode to forward. Normalize only that
60
+ // missing field before the strict adapter/destination check; explicit key mode and
61
+ // noncanonical destinations remain ineligible for ChatGPT credential injection.
62
+ const canonicalProvider = provider.authMode === undefined
63
+ ? { ...provider, authMode: "forward" as const }
64
+ : provider;
65
+ if (!isCanonicalOpenAiForwardProvider(canonicalProvider)) return [];
66
+ // The predicate accepts harmless trailing-slash variants. Pin the provider returned
67
+ // to credential-bearing sidecars so every consumer builds one exact ChatGPT path
68
+ // instead of independently concatenating the operator's equivalent spelling.
69
+ const pinnedProvider = canonicalProvider.baseUrl === CODEX_FORWARD_BASE_URL
70
+ ? canonicalProvider
71
+ : { ...canonicalProvider, baseUrl: CODEX_FORWARD_BASE_URL };
45
72
  return [{
46
73
  providerName: OPENAI_CODEX_PROVIDER_ID,
47
- provider,
48
- accountMode: providerCodexAccountMode(OPENAI_CODEX_PROVIDER_ID, provider) ?? "pool",
74
+ provider: pinnedProvider,
75
+ accountMode: providerCodexAccountMode(OPENAI_CODEX_PROVIDER_ID, pinnedProvider) ?? "pool",
49
76
  }];
50
77
  }
51
78
 
@@ -70,7 +97,12 @@ export async function resolveFirstUsableOpenAiSidecar(
70
97
  candidates: readonly OpenAiForwardSidecarCandidate[],
71
98
  incomingHeaders: Headers,
72
99
  config: OcxConfig,
100
+ options: {
101
+ exactAccount?: ExactOpenAiSidecarAccount;
102
+ beginCodexAccountSelection?: () => CodexAccountSelectionAdmission | undefined;
103
+ } = {},
73
104
  ): Promise<ResolvedOpenAiForwardSidecar | undefined> {
105
+ const { exactAccount } = options;
74
106
  let callerBearerMayBeForwarded = true;
75
107
  try {
76
108
  validateForwardAdmissionCredential(incomingHeaders, config);
@@ -79,6 +111,39 @@ export async function resolveFirstUsableOpenAiSidecar(
79
111
  callerBearerMayBeForwarded = false;
80
112
  }
81
113
  for (const candidate of candidates) {
114
+ if (exactAccount) {
115
+ // An account-qualified model is an explicit user choice. Resolve the stored
116
+ // credential directly even when the provider is globally Direct, and never
117
+ // consult Pool active state, affinity, probes, or alternates.
118
+ const authContext = await resolveCodexAuthContext(incomingHeaders, config, "pool", {
119
+ accountId: exactAccount.accountId,
120
+ modelId: exactAccount.modelId,
121
+ beginCodexAccountSelection: options.beginCodexAccountSelection,
122
+ });
123
+ if ((authContext.kind !== "pool" && authContext.kind !== "main-pool")
124
+ || !isCodexAuthContextUsable(authContext, config)) {
125
+ // Exact selection is fail-closed. A generation/runtime-state race must not fall through
126
+ // to the caller-bearer error or let a later candidate select another account.
127
+ throw new CodexPoolAuthenticationError("Selected Codex account is unavailable");
128
+ }
129
+ return {
130
+ ...candidate,
131
+ authContext,
132
+ headers: headersForCodexAuthContext(incomingHeaders, authContext),
133
+ recordOutcome: (outcome: CodexUpstreamOutcome) => recordCodexUpstreamOutcome(
134
+ config,
135
+ authContext.accountId,
136
+ outcome,
137
+ {
138
+ modelId: exactAccount.modelId,
139
+ fixedAccount: true,
140
+ probeLeaseId: authContext.probeLeaseId,
141
+ probeQuotaScope: authContext.probeQuotaScope,
142
+ writerGeneration: authContext.writerGeneration,
143
+ },
144
+ ),
145
+ };
146
+ }
82
147
  if (candidate.accountMode === "direct") {
83
148
  if (!callerBearerMayBeForwarded || !hasCallerCodexBearer(incomingHeaders)) continue;
84
149
  const headers = directSidecarHeaders(incomingHeaders);
@@ -89,7 +154,9 @@ export async function resolveFirstUsableOpenAiSidecar(
89
154
  headers,
90
155
  };
91
156
  }
92
- const authContext = await resolveCodexAuthContext(incomingHeaders, config, candidate.accountMode);
157
+ const authContext = await resolveCodexAuthContext(incomingHeaders, config, candidate.accountMode, {
158
+ beginCodexAccountSelection: options.beginCodexAccountSelection,
159
+ });
93
160
  if (!isCodexAuthContextUsable(authContext, config)) continue;
94
161
  return {
95
162
  ...candidate,
@@ -102,6 +169,7 @@ export async function resolveFirstUsableOpenAiSidecar(
102
169
  authContext.accountId,
103
170
  outcome,
104
171
  {
172
+ threadId: authContext.affinityKey,
105
173
  probeLeaseId: authContext.probeLeaseId,
106
174
  writerGeneration: authContext.writerGeneration,
107
175
  },
@@ -1,4 +1,10 @@
1
- import { backupConfigBeforeOpenAiTierMigration, saveConfig } from "../config";
1
+ import {
2
+ backupConfigBeforeOpenAiTierMigration,
3
+ OpenAiTierBackupCollisionError,
4
+ OpenAiTierRollbackPreserveError,
5
+ preserveOpenAiTierRollbackSnapshot,
6
+ saveConfig,
7
+ } from "../config";
2
8
  import type { OcxConfig } from "../types";
3
9
  import { projectOpenAiTierMigration } from "./openai-tiers";
4
10
 
@@ -6,6 +12,23 @@ export interface OpenAiTierStartupDeps {
6
12
  project: typeof projectOpenAiTierMigration;
7
13
  backup: () => void;
8
14
  save: (config: OcxConfig) => void;
15
+ preserveRollback?: (error: OpenAiTierBackupCollisionError) => void;
16
+ }
17
+
18
+ function defaultPreserveRollback(error: OpenAiTierBackupCollisionError): void {
19
+ if (!error.configPath) throw error;
20
+ try {
21
+ const preserved = preserveOpenAiTierRollbackSnapshot(error.configPath);
22
+ console.warn(`[openai-provider-migration] Preserved rollback snapshot at ${preserved}`);
23
+ } catch (cause) {
24
+ if (
25
+ cause instanceof OpenAiTierRollbackPreserveError
26
+ && (cause.code === "missing" || cause.code === "not-rollback")
27
+ ) {
28
+ throw error;
29
+ }
30
+ throw cause;
31
+ }
9
32
  }
10
33
 
11
34
  const DEFAULT_DEPS: OpenAiTierStartupDeps = {
@@ -20,7 +43,13 @@ export function runOpenAiTierStartupMigration(
20
43
  ): OcxConfig {
21
44
  const projection = deps.project(config);
22
45
  if (!projection.changed) return projection.config;
23
- deps.backup();
46
+ try {
47
+ deps.backup();
48
+ } catch (error) {
49
+ if (!(error instanceof OpenAiTierBackupCollisionError)) throw error;
50
+ (deps.preserveRollback ?? defaultPreserveRollback)(error);
51
+ deps.backup();
52
+ }
24
53
  deps.save(projection.config);
25
54
  for (const warning of projection.warnings) console.warn(`[openai-provider-migration] ${warning}`);
26
55
  return projection.config;
@@ -1,5 +1,7 @@
1
- import type { CodexAccountMode, OcxConfig, OcxProviderConfig } from "../types";
1
+ import type { CodexAccountMode, OcxConfig, OcxProviderConfig, ProviderCostOverlay } from "../types";
2
2
  import { OPENAI_PROVIDER_TIER_VERSION } from "../types";
3
+ import { openaiResponsesUrl } from "../adapters/openai-responses-url";
4
+ import { MAX_COST4_RATE } from "../usage/expected-prices";
3
5
 
4
6
  export const OPENAI_CODEX_PROVIDER_ID = "openai";
5
7
  export const LEGACY_OPENAI_MULTI_PROVIDER_ID = "openai-multi";
@@ -35,7 +37,35 @@ export function isCanonicalOpenAiForwardProvider(provider: OcxProviderConfig): b
35
37
  && normalizedBaseUrl(provider.baseUrl) === CODEX_FORWARD_BASE_URL;
36
38
  }
37
39
 
38
- const OPENAI_API_BASE_URL = "https://api.openai.com/v1";
40
+ const OPENAI_API_ORIGIN = "https://api.openai.com";
41
+ const OPENAI_API_BASE_URL = `${OPENAI_API_ORIGIN}/v1`;
42
+ const OPENAI_API_RESPONSES_URL = `${OPENAI_API_BASE_URL}/responses`;
43
+
44
+ /**
45
+ * The Responses endpoint the adapter would actually POST key-auth traffic to, normalized.
46
+ *
47
+ * Mirrors the adapter's own construction (`src/adapters/openai-responses.ts`): a configured
48
+ * `responsesPath` is appended to the base verbatim, and only the default branch runs the
49
+ * `/v1/responses` suffix normalization. Classifying on the base URL alone would call
50
+ * `baseUrl: "https://api.openai.com"` with `responsesPath: "/other"` official even though that
51
+ * request never reaches the official Responses endpoint.
52
+ */
53
+ function resolvedResponsesEndpoint(provider: OcxProviderConfig): string | undefined {
54
+ try {
55
+ const raw = provider.responsesPath === undefined
56
+ ? openaiResponsesUrl(provider.baseUrl)
57
+ : `${provider.baseUrl.replace(/\/$/, "")}${provider.responsesPath}`;
58
+ return normalizedBaseUrl(raw);
59
+ } catch {
60
+ return undefined;
61
+ }
62
+ }
63
+
64
+ function isOfficialOpenAiResponsesDestination(provider: OcxProviderConfig): boolean {
65
+ // Exact normalized URL keeps lookalike/suffix hosts out of this set: `api.openai.com.evil.test`
66
+ // resolves to its own origin, never to the official one.
67
+ return resolvedResponsesEndpoint(provider) === OPENAI_API_RESPONSES_URL;
68
+ }
39
69
 
40
70
  /**
41
71
  * Whether this provider can serve `POST /responses/compact`. The canonical ChatGPT
@@ -53,6 +83,36 @@ export function supportsNativeResponsesCompactEndpoint(
53
83
  && normalizedBaseUrl(provider.baseUrl) === OPENAI_API_BASE_URL;
54
84
  }
55
85
 
86
+ /**
87
+ * Whether this destination is an OpenAI-operated Responses backend — the canonical ChatGPT Codex
88
+ * surface or the official OpenAI API.
89
+ *
90
+ * Deliberately not keyed on `authMode === "forward"`: a noncanonical forward provider does not
91
+ * receive the caller's credentials (see the forward-header gate in the Responses adapter), so
92
+ * forward auth says nothing about which backend is on the other end.
93
+ */
94
+ export function isOpenAiOperatedResponsesDestination(provider: OcxProviderConfig): boolean {
95
+ if (isCanonicalOpenAiForwardProvider(provider)) return true;
96
+ return provider.adapter === "openai-responses"
97
+ && isOfficialOpenAiResponsesDestination(provider);
98
+ }
99
+
100
+ /**
101
+ * Whether this destination can decode a native (non-`ocx1:`) compaction blob.
102
+ *
103
+ * Only the backend that minted a blob can decode it. `authMode: "forward"` alone is not a signal:
104
+ * the adapter forwards caller credentials only to the canonical ChatGPT Codex surface, while a
105
+ * noncanonical forward provider receives no caller credentials and may point at any backend.
106
+ *
107
+ * Relay only to an OpenAI-operated destination or a destination whose operator explicitly opts in.
108
+ * Keyed by destination rather than provider id: a blob's issuer is the URL that produced it, not the
109
+ * local config key a replay travels under.
110
+ */
111
+ export function destinationDecodesNativeCompactionBlob(provider: OcxProviderConfig): boolean {
112
+ return isOpenAiOperatedResponsesDestination(provider)
113
+ || provider.decodesNativeCompactionBlobs === true;
114
+ }
115
+
56
116
  export interface OpenAiTierMigrationProjection {
57
117
  config: OcxConfig;
58
118
  changed: boolean;
@@ -69,11 +129,17 @@ export class OpenAiTierMigrationCollisionError extends Error {
69
129
  }
70
130
  }
71
131
 
72
- function managedLegacyMultiOverlay(provider: OcxProviderConfig): Pick<OcxProviderConfig, "disabled" | "selectedModels"> | null {
73
- const allowed = new Set(["adapter", "authMode", "baseUrl", "disabled", "selectedModels"]);
132
+ function managedLegacyMultiOverlay(
133
+ provider: OcxProviderConfig,
134
+ ): Pick<OcxProviderConfig, "disabled" | "selectedModels" | "modelCosts"> | null {
135
+ const allowed = new Set(["adapter", "authMode", "baseUrl", "disabled", "selectedModels", "modelCosts"]);
74
136
  if (!Object.keys(provider).every(key => allowed.has(key))) return null;
75
137
  if (!isCanonicalOpenAiForwardProvider(provider)) return null;
76
138
  if (provider.disabled !== undefined && typeof provider.disabled !== "boolean") return null;
139
+ // Keep the migration self-contained: a malformed overlay is a collision, not
140
+ // something to carry into the canonical row (importing config.ts here would
141
+ // create an import cycle — config.ts already imports this module).
142
+ if (provider.modelCosts !== undefined && !validLegacyOverlayCosts(provider.modelCosts)) return null;
77
143
  if (provider.selectedModels !== undefined && (
78
144
  !Array.isArray(provider.selectedModels)
79
145
  || provider.selectedModels.some(model => typeof model !== "string")
@@ -81,9 +147,26 @@ function managedLegacyMultiOverlay(provider: OcxProviderConfig): Pick<OcxProvide
81
147
  return {
82
148
  ...(provider.disabled !== undefined ? { disabled: provider.disabled } : {}),
83
149
  ...(provider.selectedModels !== undefined ? { selectedModels: [...provider.selectedModels] } : {}),
150
+ ...(provider.modelCosts !== undefined ? { modelCosts: provider.modelCosts } : {}),
84
151
  };
85
152
  }
86
153
 
154
+ /** Shape check for a legacy overlay: a plain record of complete non-negative finite Cost4 rows. */
155
+ function validLegacyOverlayCosts(value: unknown): boolean {
156
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
157
+ const fields = ["input", "output", "cacheRead", "cacheWrite"] as const;
158
+ return Object.values(value as Record<string, unknown>).every(entry => {
159
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return false;
160
+ const rates = entry as Record<string, unknown>;
161
+ return Object.keys(rates).length === fields.length
162
+ && fields.every(key => Object.hasOwn(rates, key)
163
+ && typeof rates[key] === "number"
164
+ && Number.isFinite(rates[key])
165
+ && (rates[key] as number) >= 0
166
+ && (rates[key] as number) <= MAX_COST4_RATE);
167
+ });
168
+ }
169
+
87
170
  function rewriteLegacyOpenAiSelectedId(value: string): string {
88
171
  return value.startsWith(LEGACY_OPENAI_MULTI_PREFIX)
89
172
  ? value.slice(LEGACY_OPENAI_MULTI_PREFIX.length)
@@ -95,6 +178,28 @@ function rewriteLegacyOpenAiModelList(values: string[] | undefined): string[] |
95
178
  return [...new Set(values.map(rewriteLegacyOpenAiSelectedId))];
96
179
  }
97
180
 
181
+ /**
182
+ * Rewrite legacy `openai-multi/<model>` keys in a modelCosts overlay so the
183
+ * prices still match after the migration resolves logs as provider `openai`
184
+ * with the bare model id. Keys without the legacy prefix pass through.
185
+ */
186
+ function rewriteLegacyOpenAiCostKeys(costs: Record<string, ProviderCostOverlay> | undefined): Record<string, ProviderCostOverlay> {
187
+ // Null-prototype map so prototype-named model ids (e.g. "__proto__") are
188
+ // stored as own properties instead of invoking the inherited setter.
189
+ const rewritten = Object.create(null) as Record<string, ProviderCostOverlay>;
190
+ if (costs) {
191
+ for (const [key, value] of Object.entries(costs)) {
192
+ const canonicalKey = rewriteLegacyOpenAiSelectedId(key);
193
+ // A bare <model> key always wins over its openai-multi/<model> equivalent
194
+ // inside the same row, regardless of JSON property order.
195
+ if (key === canonicalKey || !Object.hasOwn(rewritten, canonicalKey)) {
196
+ rewritten[canonicalKey] = value;
197
+ }
198
+ }
199
+ }
200
+ return rewritten;
201
+ }
202
+
98
203
  function mergeLegacyOpenAiProviderRows(
99
204
  openai: OcxProviderConfig | undefined,
100
205
  legacyMulti: OcxProviderConfig | undefined,
@@ -104,12 +209,22 @@ function mergeLegacyOpenAiProviderRows(
104
209
  ...(openai?.selectedModels ?? []),
105
210
  ...(legacyMulti?.selectedModels ?? []),
106
211
  ]);
212
+ // Both rows can carry disjoint overlays; merge them (canonical openai wins on
213
+ // key conflicts) so legacy Multi prices are not silently dropped. Keys are
214
+ // rewritten first so `openai-multi/<model>` entries still resolve after the
215
+ // provider is canonicalized to `openai`.
216
+ const modelCosts = {
217
+ ...rewriteLegacyOpenAiCostKeys(legacyMulti?.modelCosts),
218
+ ...rewriteLegacyOpenAiCostKeys(openai?.modelCosts),
219
+ };
220
+ const hasModelCosts = Object.keys(modelCosts).length > 0;
107
221
  const formerRows = [openai, legacyMulti].filter((row): row is OcxProviderConfig => row !== undefined);
108
222
  const disabled = formerRows.length > 0 && formerRows.every(row => row.disabled === true);
109
223
  return {
110
224
  ...canonicalCodexForwardProvider(mode),
111
225
  ...(disabled ? { disabled: true } : {}),
112
226
  ...(selectedModels && selectedModels.length > 0 ? { selectedModels } : {}),
227
+ ...(hasModelCosts ? { modelCosts } : {}),
113
228
  };
114
229
  }
115
230
 
@@ -62,6 +62,7 @@ export function applyOpenAiVirtualModel(
62
62
  logCtx.resolvedModel = resolution.wireModelId;
63
63
  route.modelId = resolution.wireModelId;
64
64
  parsed.modelId = resolution.wireModelId;
65
+ parsed._openAiVirtualSelectedModelId = resolution.selectedModelId;
65
66
 
66
67
  if (parsed._rawBody && typeof parsed._rawBody === "object" && !Array.isArray(parsed._rawBody)) {
67
68
  const raw = parsed._rawBody as Record<string, unknown>;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * OpenCode Zen short-window rate-limit guidance (#1145 / OCX-56).
3
+ *
4
+ * OpenCode's keyed and keyless Zen chat endpoints share `https://opencode.ai/zen/v1`.
5
+ * Free-model traffic can hit a short-window burst ceiling around 15–20 RPM
6
+ * (community-measured). Zen often answers with opaque `429 Rate limit exceeded`
7
+ * bodies and may omit `Retry-After` / `X-RateLimit-*`; when those headers are
8
+ * present they still take precedence. Distinct from the keyless desktop
9
+ * ~200 requests / 5h quota documented on `opencode-free`.
10
+ */
11
+ import { validateClientRetryAfterHeader } from "../lib/retry-after";
12
+ import { registryEntryForProviderDestination } from "./registry";
13
+
14
+ const OPENCODE_ZEN_PROVIDER_IDS = new Set(["opencode-zen", "opencode-free"]);
15
+
16
+ /** Observed free-model burst ceiling on Zen (not an official OpenCode figure). */
17
+ export const OPENCODE_ZEN_OBSERVED_RPM_HINT = "roughly 15-20 requests per minute";
18
+
19
+ /**
20
+ * Synthetic client backoff when Zen omits Retry-After after a rate-limit 429.
21
+ * Longer than the generic 2s default so Codex-shaped clients do not immediately
22
+ * re-hammer a ~15-20 RPM window.
23
+ */
24
+ export const OPENCODE_ZEN_SYNTHETIC_RETRY_AFTER_SEC = 15;
25
+
26
+ const ENRICHMENT_MARKER = "15-20 requests per minute";
27
+
28
+ export function isOpenCodeZenRateLimitProvider(opts: {
29
+ providerName?: string;
30
+ baseUrl?: string;
31
+ adapter?: string;
32
+ }): boolean {
33
+ const name = opts.providerName?.trim();
34
+ if (name && OPENCODE_ZEN_PROVIDER_IDS.has(name)) return true;
35
+ const baseUrl = opts.baseUrl?.trim();
36
+ if (!baseUrl) return false;
37
+ const entry = registryEntryForProviderDestination({
38
+ baseUrl,
39
+ adapter: opts.adapter?.trim() || "openai-chat",
40
+ authMode: "key",
41
+ });
42
+ return entry !== undefined && OPENCODE_ZEN_PROVIDER_IDS.has(entry.id);
43
+ }
44
+
45
+ /**
46
+ * Same-key `retryOn429` only applies on key-authenticated HTTP paths — not
47
+ * keyless `opencode-free` traffic and not custom `runTurn` transports.
48
+ */
49
+ export function supportsOpenCodeZenRetryOn429Guidance(opts: {
50
+ authMode?: string;
51
+ hasApiKey?: boolean;
52
+ supportsHttpSameKeyRetry?: boolean;
53
+ }): boolean {
54
+ if (opts.supportsHttpSameKeyRetry === false) return false;
55
+ if (opts.authMode !== undefined && opts.authMode !== "key") return false;
56
+ return opts.hasApiKey === true;
57
+ }
58
+
59
+ /**
60
+ * Append actionable Zen rate-limit context to a generic upstream 429 message and
61
+ * embed a parseable `try again in Ns` hint so {@link resolveClientRetryAfter}
62
+ * surfaces a useful Retry-After when the gateway sent none.
63
+ */
64
+ export function enrichOpenCodeZenRateLimitMessage(
65
+ message: string,
66
+ opts: {
67
+ status: number;
68
+ providerName?: string;
69
+ baseUrl?: string;
70
+ adapter?: string;
71
+ authMode?: string;
72
+ hasApiKey?: boolean;
73
+ /** Upstream Retry-After header; when valid, skip the synthetic 15s text hint. */
74
+ upstreamRetryAfter?: string | null;
75
+ /** False for custom `runTurn` transports outside the HTTP retry loop. */
76
+ supportsHttpSameKeyRetry?: boolean;
77
+ now?: number;
78
+ },
79
+ ): string {
80
+ if (opts.status !== 429) return message;
81
+ if (!isOpenCodeZenRateLimitProvider(opts)) return message;
82
+ if (!/rate\s*limit/i.test(message)) return message;
83
+ if (message.includes(ENRICHMENT_MARKER)) return message;
84
+
85
+ const upstreamRetry = validateClientRetryAfterHeader(
86
+ opts.upstreamRetryAfter,
87
+ opts.now ?? Date.now(),
88
+ );
89
+ const retryHint = upstreamRetry || /try again in \d/i.test(message)
90
+ ? ""
91
+ : ` Try again in ${OPENCODE_ZEN_SYNTHETIC_RETRY_AFTER_SEC}s.`;
92
+ const paceHint = supportsOpenCodeZenRetryOn429Guidance(opts)
93
+ ? " Slow the request pace, or set providers.opencode-zen.retryOn429 for same-key backoff."
94
+ : " Slow the request pace.";
95
+ return (
96
+ `${message}`
97
+ + ` OpenCode Zen free-model traffic is often limited to ${OPENCODE_ZEN_OBSERVED_RPM_HINT}`
98
+ + ` (observed; OpenCode does not publish this RPM, and may omit rate-limit headers).`
99
+ + `${retryHint}`
100
+ + paceHint
101
+ );
102
+ }
@@ -148,3 +148,32 @@ export function rewriteProviderReferences(config: OcxConfig, from: string, to: s
148
148
 
149
149
  return { changed, collisions };
150
150
  }
151
+
152
+ /**
153
+ * Drop the custom-model rows that belonged to a provider being removed.
154
+ *
155
+ * The sibling of the rename pass above. `rewriteProviderReferences` already
156
+ * carries `customModels[].provider` across a rename, so the array tracks the
157
+ * provider lifecycle — but removal used to delete only `config.providers[name]`
158
+ * and leave the rows behind. Those orphans still reach `/api/models` and the
159
+ * generated Codex catalog, which key on the row rather than on provider
160
+ * existence, so they surface as models that resolve to nothing (#1273).
161
+ *
162
+ * Only the rows are touched: the `customModelCatalogMigration` marker records
163
+ * one-time ownership of pre-marker rows and must survive removal unchanged, or
164
+ * an older binary's view of that ownership silently changes.
165
+ *
166
+ * Returns the number of rows dropped so callers can report it.
167
+ */
168
+ export function dropProviderCustomModels(config: OcxConfig, provider: string): number {
169
+ const existing = config.customModels;
170
+ if (!Array.isArray(existing) || existing.length === 0) return 0;
171
+ const kept = existing.filter(model => model.provider !== provider);
172
+ if (kept.length === existing.length) return 0;
173
+ // Match the add/remove routes: an emptied list is dropped rather than left as
174
+ // `[]`, so the `customModels` field is absent either way. Only that field —
175
+ // the `customModelCatalogMigration` marker is deliberately left in place.
176
+ if (kept.length > 0) config.customModels = kept;
177
+ else delete config.customModels;
178
+ return existing.length - kept.length;
179
+ }