@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,19 +1,18 @@
1
+ import { readBoundedResponseBody } from "../lib/bounded-body";
2
+
1
3
  export class CodexWarmupError extends Error {
2
- code: "http_status" | "missing_body" | "stream_failed" | "stream_incomplete" | "stream_error" | "invalid_sse" | "no_terminal" | "transport";
4
+ code: "http_status" | "missing_body" | "stream_failed" | "stream_incomplete" | "stream_error" | "stream_too_large" | "invalid_sse" | "no_terminal" | "transport";
3
5
  status?: number;
4
- /** Upstream error detail extracted from the response body (truncated to 512 chars). */
5
- upstreamDetail?: string;
6
6
 
7
7
  constructor(
8
8
  code: CodexWarmupError["code"],
9
9
  message = "Codex warmup failed",
10
- options: { status?: number; cause?: unknown; upstreamDetail?: string } = {},
10
+ options: { status?: number; cause?: unknown } = {},
11
11
  ) {
12
12
  super(message);
13
13
  this.name = "CodexWarmupError";
14
14
  this.code = code;
15
15
  this.status = options.status;
16
- this.upstreamDetail = options.upstreamDetail;
17
16
  if (options.cause !== undefined) this.cause = options.cause;
18
17
  }
19
18
  }
@@ -29,37 +28,31 @@ const CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
29
28
  const DEFAULT_MODEL = "gpt-5.4-mini";
30
29
  const FALLBACK_MODELS = ["gpt-5.5"];
31
30
  const DEFAULT_TIMEOUT_MS = 30_000;
31
+ const MAX_TIMEOUT_MS = 0x7fff_ffff;
32
32
  const MAX_ERROR_BODY_BYTES = 2048;
33
+ const MAX_WARMUP_STREAM_BYTES = 1024 * 1024;
33
34
 
34
- /** Read the first MAX_ERROR_BODY_BYTES of a response body and extract an error message. */
35
- async function readErrorDetail(res: Response): Promise<string | undefined> {
35
+ /** Bound and release an upstream error body without exposing provider-controlled text. */
36
+ async function drainErrorBody(res: Response, signal: AbortSignal): Promise<void> {
36
37
  try {
37
- const text = await res.text();
38
- const trimmed = text.slice(0, MAX_ERROR_BODY_BYTES);
39
- try {
40
- const json = JSON.parse(trimmed) as Record<string, unknown>;
41
- // ChatGPT backend error shape: { error: { message: "..." } } or { detail: "..." }
42
- const nested = json.error;
43
- if (nested && typeof nested === "object" && typeof (nested as Record<string, unknown>).message === "string") {
44
- return ((nested as Record<string, unknown>).message as string).slice(0, 512);
45
- }
46
- if (typeof json.detail === "string") return json.detail.slice(0, 512);
47
- if (typeof json.error === "string") return (json.error as string).slice(0, 512);
48
- if (typeof json.message === "string") return json.message.slice(0, 512);
49
- } catch {
50
- // Non-JSON response body may contain sensitive data (tokens, credentials).
51
- // Only surface structured error messages, never raw text.
38
+ await readBoundedResponseBody(res, {
39
+ signal,
40
+ maxBytes: MAX_ERROR_BODY_BYTES,
41
+ fatalUtf8: true,
42
+ });
43
+ } catch (error) {
44
+ if (signal.aborted) {
45
+ throw new CodexWarmupError("transport", "Codex warmup request failed", {
46
+ cause: error,
47
+ });
52
48
  }
53
- return undefined;
54
- } catch {
55
- return undefined;
49
+ // The bounded reader owns cancellation for oversized, invalid, or stalled bodies.
56
50
  }
57
51
  }
58
52
 
59
53
  function safeWarmupReason(err: unknown): string {
60
54
  if (err instanceof CodexWarmupError) {
61
- const base = err.status ? `${err.code}:${err.status}` : err.code;
62
- return err.upstreamDetail ? `${base} — ${err.upstreamDetail}` : base;
55
+ return err.status ? `${err.code}:${err.status}` : err.code;
63
56
  }
64
57
  return "transport";
65
58
  }
@@ -89,83 +82,196 @@ function parseSseFrame(frame: string): unknown | null {
89
82
  }
90
83
  }
91
84
 
92
- async function drainWarmupSse(body: ReadableStream<Uint8Array>): Promise<void> {
85
+ async function drainWarmupSse(body: ReadableStream<Uint8Array>, signal: AbortSignal): Promise<void> {
93
86
  const reader = body.getReader();
94
87
  const decoder = new TextDecoder();
95
- let buffer = "";
88
+ let buffer = new Uint8Array(Math.min(MAX_WARMUP_STREAM_BYTES, 64 * 1024));
89
+ let bufferedBytes = 0;
90
+ let scanOffset = 0;
91
+ let bytesRead = 0;
92
+ const abortError = () => new CodexWarmupError("transport", "Codex warmup request failed", {
93
+ cause: signal.reason,
94
+ });
95
+ const cancelReader = () => {
96
+ try {
97
+ void reader.cancel(signal.reason).catch(() => {});
98
+ } catch {
99
+ // Some custom stream implementations throw synchronously from cancel().
100
+ }
101
+ };
102
+ // Fetch implementations usually error the response body when their signal is
103
+ // aborted, but a ReadableStream is not intrinsically coupled to that signal.
104
+ // Race only the currently pending read against a removable abort listener;
105
+ // Bun 1.3 can leave read() parked until a custom source's cancel promise
106
+ // settles, while a shared never-settled race promise would retain one handler
107
+ // per chunk. Cancellation remains best-effort and is never awaited.
108
+ const readWithSignal = (): Promise<Awaited<ReturnType<typeof reader.read>>> => {
109
+ if (signal.aborted) {
110
+ cancelReader();
111
+ return Promise.reject(abortError());
112
+ }
113
+ const read = reader.read();
114
+ void read.catch(() => {});
115
+ return new Promise((resolve, reject) => {
116
+ let settled = false;
117
+ const finish = (action: () => void) => {
118
+ if (settled) return;
119
+ settled = true;
120
+ signal.removeEventListener("abort", onAbort);
121
+ action();
122
+ };
123
+ const onAbort = () => {
124
+ cancelReader();
125
+ finish(() => reject(abortError()));
126
+ };
127
+ signal.addEventListener("abort", onAbort, { once: true });
128
+ if (signal.aborted) {
129
+ onAbort();
130
+ return;
131
+ }
132
+ read.then(
133
+ result => finish(() => resolve(result)),
134
+ error => finish(() => reject(error)),
135
+ );
136
+ });
137
+ };
138
+ const ensureCapacity = (requiredBytes: number) => {
139
+ if (requiredBytes <= buffer.byteLength) return;
140
+ const grown = new Uint8Array(Math.min(
141
+ MAX_WARMUP_STREAM_BYTES,
142
+ Math.max(requiredBytes, buffer.byteLength * 2),
143
+ ));
144
+ grown.set(buffer.subarray(0, bufferedBytes));
145
+ buffer = grown;
146
+ };
147
+ const findFrameDelimiter = (start: number): { index: number; length: 2 | 3 | 4 } | undefined => {
148
+ for (let index = start; index < bufferedBytes - 1; index += 1) {
149
+ const firstLength = buffer[index] === 10
150
+ ? 1
151
+ : buffer[index] === 13 && buffer[index + 1] === 10 ? 2 : 0;
152
+ if (firstLength === 0) continue;
153
+ const secondStart = index + firstLength;
154
+ const secondLength = buffer[secondStart] === 10
155
+ ? 1
156
+ : buffer[secondStart] === 13 && buffer[secondStart + 1] === 10 ? 2 : 0;
157
+ if (secondLength > 0) return { index, length: (firstLength + secondLength) as 2 | 3 | 4 };
158
+ }
159
+ return undefined;
160
+ };
161
+ const acceptFrame = (frame: Uint8Array): boolean => {
162
+ const parsed = parseSseFrame(decoder.decode(frame));
163
+ const type = eventTypeFromData(parsed);
164
+ if (type === "response.completed") return true;
165
+ if (type === "response.failed") throw new CodexWarmupError("stream_failed");
166
+ if (type === "response.incomplete") throw new CodexWarmupError("stream_incomplete");
167
+ if (type === "error") throw new CodexWarmupError("stream_error");
168
+ return false;
169
+ };
96
170
 
97
171
  try {
172
+ if (signal.aborted) throw abortError();
98
173
  for (;;) {
99
- const { done, value } = await reader.read();
174
+ const { done, value } = await readWithSignal();
175
+ if (signal.aborted) throw abortError();
100
176
  if (done) break;
101
- buffer += decoder.decode(value, { stream: true });
177
+ if (value.byteLength > MAX_WARMUP_STREAM_BYTES - bytesRead) {
178
+ throw new CodexWarmupError("stream_too_large", "Codex warmup stream exceeded the size limit");
179
+ }
180
+ bytesRead += value.byteLength;
181
+ ensureCapacity(bufferedBytes + value.byteLength);
182
+ buffer.set(value, bufferedBytes);
183
+ bufferedBytes += value.byteLength;
102
184
 
185
+ let consumedBytes = 0;
103
186
  for (;;) {
104
- const frameEnd = buffer.search(/\r?\n\r?\n/);
105
- if (frameEnd < 0) break;
106
- const frame = buffer.slice(0, frameEnd);
107
- const delimiterLength = buffer[frameEnd] === "\r" ? 4 : 2;
108
- buffer = buffer.slice(frameEnd + delimiterLength);
109
- const parsed = parseSseFrame(frame);
110
- const type = eventTypeFromData(parsed);
111
- if (type === "response.completed") return;
112
- if (type === "response.failed") throw new CodexWarmupError("stream_failed");
113
- if (type === "response.incomplete") throw new CodexWarmupError("stream_incomplete");
114
- if (type === "error") throw new CodexWarmupError("stream_error");
187
+ const delimiter = findFrameDelimiter(scanOffset);
188
+ if (!delimiter) {
189
+ // A delimiter can start at most three bytes before the next chunk.
190
+ scanOffset = Math.max(consumedBytes, bufferedBytes - 3);
191
+ break;
192
+ }
193
+ if (acceptFrame(buffer.subarray(consumedBytes, delimiter.index))) return;
194
+ consumedBytes = delimiter.index + delimiter.length;
195
+ scanOffset = consumedBytes;
196
+ }
197
+ if (consumedBytes > 0) {
198
+ buffer.copyWithin(0, consumedBytes, bufferedBytes);
199
+ bufferedBytes -= consumedBytes;
200
+ scanOffset = Math.max(0, scanOffset - consumedBytes);
115
201
  }
116
202
  }
117
203
 
118
- if (buffer.trim()) {
119
- const parsed = parseSseFrame(buffer);
120
- const type = eventTypeFromData(parsed);
121
- if (type === "response.completed") return;
122
- if (type === "response.failed") throw new CodexWarmupError("stream_failed");
123
- if (type === "response.incomplete") throw new CodexWarmupError("stream_incomplete");
124
- if (type === "error") throw new CodexWarmupError("stream_error");
125
- }
204
+ if (bufferedBytes > 0 && acceptFrame(buffer.subarray(0, bufferedBytes))) return;
126
205
 
127
206
  throw new CodexWarmupError("no_terminal", "Codex warmup ended before completion");
128
207
  } finally {
129
- reader.releaseLock();
208
+ try {
209
+ reader.releaseLock();
210
+ } catch {
211
+ // A hostile cancel promise may keep the final read locked after timeout.
212
+ }
130
213
  }
131
214
  }
132
215
 
133
216
  async function tryWarmup(options: CodexWarmupOptions, model: string): Promise<void> {
134
- let res: Response;
135
- try {
136
- res = await fetch(CODEX_RESPONSES_URL, {
137
- method: "POST",
138
- headers: {
139
- Authorization: `Bearer ${options.accessToken}`,
140
- "ChatGPT-Account-Id": options.chatgptAccountId,
141
- "Content-Type": "application/json",
142
- },
143
- body: JSON.stringify({
144
- model,
145
- instructions: "Reply with OK.",
146
- input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] }],
147
- stream: true,
148
- store: false,
149
- }),
150
- signal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS),
151
- });
152
- } catch (err) {
153
- throw new CodexWarmupError("transport", "Codex warmup request failed", { cause: err });
154
- }
155
-
156
- if (!res.ok) {
157
- const upstreamDetail = await readErrorDetail(res);
158
- throw new CodexWarmupError("http_status", "Codex warmup was rejected", {
159
- status: res.status,
160
- upstreamDetail,
217
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
218
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0 || timeoutMs > MAX_TIMEOUT_MS) {
219
+ throw new CodexWarmupError("transport", "Codex warmup request failed", {
220
+ cause: new RangeError("Codex warmup timeout is outside the supported range"),
161
221
  });
162
222
  }
163
- if (!res.body) throw new CodexWarmupError("missing_body");
223
+ // Bun 1.3 can leave AbortSignal.timeout() dormant while a custom response
224
+ // stream has a pending read. A ref'ed timer and explicit controller make the
225
+ // same deadline cover response headers and the full success/error body.
226
+ const deadline = new AbortController();
227
+ const signal = deadline.signal;
228
+ const timer = setTimeout(() => {
229
+ deadline.abort(new DOMException("Codex warmup timed out", "TimeoutError"));
230
+ }, timeoutMs);
164
231
 
165
232
  try {
166
- await drainWarmupSse(res.body);
233
+ let res: Response;
234
+ try {
235
+ res = await fetch(CODEX_RESPONSES_URL, {
236
+ method: "POST",
237
+ headers: {
238
+ Authorization: `Bearer ${options.accessToken}`,
239
+ "ChatGPT-Account-Id": options.chatgptAccountId,
240
+ "Content-Type": "application/json",
241
+ },
242
+ body: JSON.stringify({
243
+ model,
244
+ instructions: "Reply with OK.",
245
+ input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] }],
246
+ stream: true,
247
+ store: false,
248
+ }),
249
+ signal,
250
+ });
251
+ } catch (err) {
252
+ throw new CodexWarmupError("transport", "Codex warmup request failed", { cause: err });
253
+ }
254
+
255
+ if (!res.ok) {
256
+ await drainErrorBody(res, signal);
257
+ throw new CodexWarmupError("http_status", "Codex warmup was rejected", {
258
+ status: res.status,
259
+ });
260
+ }
261
+ const body = res.body;
262
+ if (!body) throw new CodexWarmupError("missing_body");
263
+
264
+ try {
265
+ await drainWarmupSse(body, signal);
266
+ } finally {
267
+ try {
268
+ void body.cancel().catch(() => {});
269
+ } catch {
270
+ // Some custom stream implementations throw synchronously from cancel().
271
+ }
272
+ }
167
273
  } finally {
168
- await res.body?.cancel().catch(() => {});
274
+ clearTimeout(timer);
169
275
  }
170
276
  }
171
277
 
@@ -0,0 +1,114 @@
1
+ /**
2
+ * The witness a native Codex write is coordinated by.
3
+ *
4
+ * NOT an `AdmissionSnapshot`, and the difference is the whole reason this file
5
+ * exists. `admitCodexWrite` refuses before it constructs a snapshot unless
6
+ * ownership is `owned` (`admission.ts`), so every snapshot it produces carries
7
+ * `owned` — the field cannot hold `unknown` at all. Reusing that type here would
8
+ * have meant one of two untruths: either the call becomes gated on ownership,
9
+ * which refuses Codex injection on every Windows machine and on Linux without a
10
+ * reachable user bus, or a hand-built snapshot claims an admission that never
11
+ * happened.
12
+ *
13
+ * So this type authorizes THE WRITE, not the DECISION to write. Deciding whether
14
+ * a write should happen at all is admission's job and belongs to its own phase,
15
+ * behind the Windows definition-chain walk.
16
+ *
17
+ * What the id covers, and why it is the OUTPUT rather than the inputs: the bytes
18
+ * `injectCodexConfig` emits depend on the port, the resolved catalog path, the
19
+ * hostname, websocket mode, legacy mode and the managed sub-agent defaults. An
20
+ * enumeration of those was already incomplete once. Hashing the computed
21
+ * candidate bytes closes the class instead of the instance — an input that
22
+ * changes the output changes the id whether or not anyone remembered to list it.
23
+ */
24
+ import { createHash } from "node:crypto";
25
+
26
+ export interface CodexWriteCandidate {
27
+ /** The exact string about to replace `config.toml`. */
28
+ readonly configBytes: string;
29
+ /** The exact string about to replace the profile. */
30
+ readonly profileBytes: string;
31
+ /** The RESOLVED catalog path, never the raw option. */
32
+ readonly catalogPath: string | null;
33
+ }
34
+
35
+ export interface CodexWriteEvidence {
36
+ /** sha256 of the `config.toml` bytes this operation read as its input. */
37
+ readonly nativeInputIdentity: string;
38
+ /** Digest of the persisted OpenCodex config these bytes were derived from. */
39
+ readonly persistedIdentity: string;
40
+ /** Generation as observed before the lock; `present:false` means no coordinator yet. */
41
+ readonly generation: Readonly<{ present: boolean; value: number }>;
42
+ /** Content identity of the journal, or a stable marker for absence. */
43
+ readonly journalIdentity: string;
44
+ readonly canonicalTargets: Readonly<{
45
+ config: string;
46
+ profile: string;
47
+ journal: string;
48
+ }>;
49
+ }
50
+
51
+ export interface CodexWriteCoordination {
52
+ readonly candidate: CodexWriteCandidate;
53
+ readonly evidence: CodexWriteEvidence;
54
+ /**
55
+ * Recorded context, deliberately OUTSIDE `comparisonId`.
56
+ *
57
+ * The lock compares one id and nothing else, so a field that is copied rather
58
+ * than re-observed detects no drift by being in it — it would only match
59
+ * itself. Ownership is not re-observed under the lock (that would mean running
60
+ * a service-manager subprocess while N and C are held), so it travels as
61
+ * something the operation SAW, not as something the comparison PROVES.
62
+ */
63
+ readonly observedOwnership: "owned" | "foreign" | "unknown";
64
+ readonly comparisonId: string;
65
+ }
66
+
67
+ /**
68
+ * Collapse a generation to one token.
69
+ *
70
+ * Absent and present-zero are the same authority — no cooperating write has
71
+ * committed — and they cannot be observed the same way: before the lock only
72
+ * absence is visible, and inside it only the committed zero is. Without this
73
+ * they never match and every first write refuses.
74
+ */
75
+ function generationToken(generation: CodexWriteEvidence["generation"]): string {
76
+ const { present, value } = generation;
77
+ if (!Number.isSafeInteger(value) || value < 0) {
78
+ throw new TypeError(`A config generation must be a non-negative safe integer, got ${String(value)}.`);
79
+ }
80
+ return present && value > 0 ? `gen:${value}` : "gen:0";
81
+ }
82
+
83
+ export function hashCodexWriteCoordination(
84
+ candidate: CodexWriteCandidate,
85
+ evidence: CodexWriteEvidence,
86
+ ): string {
87
+ return createHash("sha256")
88
+ .update(JSON.stringify([
89
+ // The output, hashed directly. Two operations that intend different bytes
90
+ // cannot share an id, however they came to differ.
91
+ createHash("sha256").update(candidate.configBytes).digest("hex"),
92
+ createHash("sha256").update(candidate.profileBytes).digest("hex"),
93
+ candidate.catalogPath,
94
+ evidence.nativeInputIdentity,
95
+ evidence.persistedIdentity,
96
+ generationToken(evidence.generation),
97
+ evidence.journalIdentity,
98
+ evidence.canonicalTargets,
99
+ ]))
100
+ .digest("hex");
101
+ }
102
+
103
+ export function codexWriteCoordination(
104
+ candidate: CodexWriteCandidate,
105
+ evidence: CodexWriteEvidence,
106
+ observedOwnership: CodexWriteCoordination["observedOwnership"],
107
+ ): CodexWriteCoordination {
108
+ return {
109
+ candidate,
110
+ evidence,
111
+ observedOwnership,
112
+ comparisonId: hashCodexWriteCoordination(candidate, evidence),
113
+ };
114
+ }
@@ -121,6 +121,26 @@ export function comboFailureDecision(
121
121
  if (isCyberPolicyCode(options?.code)) return "stop";
122
122
  const error = classifyError(status, "upstream_error", message);
123
123
  if (isCyberPolicyCode(error.code)) return "stop";
124
+ // A local input-admission refusal (#1524) says "this candidate cannot fit the request",
125
+ // not "the request is impossible": the next candidate may have a larger context window.
126
+ //
127
+ // This MUST be tested before the generic stop list below. Our own refusal message says
128
+ // "context window" -- that is what it refuses on -- and the classifier remaps that phrase,
129
+ // so checking the stop list first swallowed the signal and ended the chain. An UPSTREAM
130
+ // `context_length_exceeded` carries no admission code and still falls through to stop.
131
+ //
132
+ // Matched on the STRUCTURED code only, which classifyError now preserves for our own
133
+ // refusal. A raw substring test would additionally let any upstream override a terminal
134
+ // verdict by echoing the token in prose we do not control.
135
+ //
136
+ // Precise about what this is NOT: an upstream can still SET this code deliberately, since
137
+ // both extractors read the upstream error object. That is bounded rather than dangerous --
138
+ // an upstream already controls other hop signals (429, 5xx), and traversal is finite: policy
139
+ // tries each candidate once via `tried`, and combo excludes each attempted target. So this is
140
+ // structured-code-only, not provably local.
141
+ if (options?.code === "input_admission_refused" || error.code === "input_admission_refused") {
142
+ return "hop";
143
+ }
124
144
  if (["origin_rejected", "context_length_exceeded", "invalid_request_error"].includes(error.code ?? "")) {
125
145
  return "stop";
126
146
  }
@@ -4,9 +4,12 @@ export {
4
4
  comboConfigError,
5
5
  comboConfigIssues,
6
6
  comboDefaultEffort,
7
+ comboDisabledModelId,
8
+ comboDisabledModelSelectors,
7
9
  comboModelId,
8
10
  comboPublicModelId,
9
11
  getCombo,
12
+ isNativeAliasCombo,
10
13
  isValidComboId,
11
14
  listComboIds,
12
15
  normalizeComboConfig,
@@ -36,6 +39,7 @@ export {
36
39
  } from "./failover";
37
40
  export {
38
41
  comboIdFromRawBody,
42
+ comboRequestHasImageInput,
39
43
  concreteComboRequestBody,
40
44
  resetComboEffortWarningStateForTests,
41
45
  } from "./request";
@@ -23,6 +23,36 @@ export function comboIdFromRawBody(body: unknown, config: OcxConfig): string | n
23
23
  return resolveComboId(config, model);
24
24
  }
25
25
 
26
+ /**
27
+ * Detect image-bearing Responses *input* only.
28
+ *
29
+ * Must not walk the full request body: tool JSON schemas, metadata, or extension
30
+ * payloads can legally contain `{ "type": "input_image" }` without any image
31
+ * being dispatched. After previous_response_id expansion, scan the materialised
32
+ * `input` tree (message content and function_call_output.output).
33
+ */
34
+ export function comboRequestHasImageInput(body: unknown): boolean {
35
+ if (!body || typeof body !== "object" || Array.isArray(body)) return false;
36
+ return responsesInputHasImage((body as { input?: unknown }).input);
37
+ }
38
+
39
+ function responsesInputHasImage(input: unknown): boolean {
40
+ if (typeof input === "string" || input == null) return false;
41
+ if (!Array.isArray(input)) return false;
42
+ return input.some(responsesInputNodeHasImage);
43
+ }
44
+
45
+ function responsesInputNodeHasImage(value: unknown): boolean {
46
+ if (!value || typeof value !== "object") return false;
47
+ if (Array.isArray(value)) return value.some(responsesInputNodeHasImage);
48
+ const record = value as Record<string, unknown>;
49
+ if (record.type === "input_image") return true;
50
+ // Message content parts and nested function_call_output content/output arrays.
51
+ if (record.content !== undefined && responsesInputNodeHasImage(record.content)) return true;
52
+ if (record.output !== undefined && responsesInputNodeHasImage(record.output)) return true;
53
+ return false;
54
+ }
55
+
26
56
  export function concreteComboRequestBody(
27
57
  body: unknown,
28
58
  target: Pick<OcxComboTarget, "provider" | "model">,
@@ -40,6 +70,8 @@ export function concreteComboRequestBody(
40
70
  && !Object.prototype.hasOwnProperty.call(reasoning, "effort")
41
71
  );
42
72
  if (!needsDefault) return clone;
73
+ // Picker availability treats an unknown ladder as a wildcard, but runtime
74
+ // injection stays fail-closed until this concrete target advertises support.
43
75
  if (!targetReasoningEfforts?.includes(defaultEffort)) {
44
76
  const key = `${target.provider}/${target.model}:${defaultEffort}`;
45
77
  if (!warnedUnsupportedDefaults.has(key)) {