@iislee/opencodex 2.11.0 → 2.35.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 (733) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +117 -22
  3. package/bin/ocx.mjs +245 -88
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-Be1sDpWw.css +1 -0
  6. package/gui/dist/assets/index-D4OKgUV4.js +114 -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 +21 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +475 -62
  44. package/src/adapters/base.ts +82 -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 +637 -0
  48. package/src/adapters/cursor/call-id.ts +44 -0
  49. package/src/adapters/cursor/catalog.ts +541 -0
  50. package/src/adapters/cursor/checkpoint-store.ts +308 -0
  51. package/src/adapters/cursor/cursor-errors.ts +144 -6
  52. package/src/adapters/cursor/discovery.ts +122 -14
  53. package/src/adapters/cursor/effort-map.ts +106 -4
  54. package/src/adapters/cursor/envelope-echo.ts +290 -0
  55. package/src/adapters/cursor/framing.ts +39 -0
  56. package/src/adapters/cursor/h2-pool.ts +123 -0
  57. package/src/adapters/cursor/http1-bidi.ts +361 -0
  58. package/src/adapters/cursor/images.ts +704 -0
  59. package/src/adapters/cursor/live-models.ts +180 -59
  60. package/src/adapters/cursor/live-transport.ts +623 -170
  61. package/src/adapters/cursor/message-mapper.ts +4 -1
  62. package/src/adapters/cursor/native-exec-common.ts +23 -2
  63. package/src/adapters/cursor/native-exec-desktop.ts +23 -0
  64. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  65. package/src/adapters/cursor/native-exec-network.ts +1 -1
  66. package/src/adapters/cursor/native-exec-shell.ts +5 -3
  67. package/src/adapters/cursor/native-exec.ts +121 -14
  68. package/src/adapters/cursor/protobuf-events.ts +829 -11
  69. package/src/adapters/cursor/protobuf-request.ts +447 -75
  70. package/src/adapters/cursor/request-builder.ts +271 -35
  71. package/src/adapters/cursor/tool-definitions.ts +252 -12
  72. package/src/adapters/cursor/tool-result-normalize.ts +115 -0
  73. package/src/adapters/cursor/transport.ts +22 -0
  74. package/src/adapters/cursor/types.ts +48 -1
  75. package/src/adapters/cursor.ts +336 -39
  76. package/src/adapters/exec-tool-result-normalize.ts +99 -0
  77. package/src/adapters/google-antigravity-replay.ts +713 -41
  78. package/src/adapters/google-antigravity-wire.ts +48 -10
  79. package/src/adapters/google-errors.ts +44 -12
  80. package/src/adapters/google-http.ts +38 -10
  81. package/src/adapters/google-tool-schema.ts +80 -15
  82. package/src/adapters/google-truncation.ts +11 -0
  83. package/src/adapters/google.ts +742 -86
  84. package/src/adapters/identity.ts +39 -6
  85. package/src/adapters/image.ts +1 -1
  86. package/src/adapters/kiro-constants.ts +27 -0
  87. package/src/adapters/kiro-errors.ts +11 -0
  88. package/src/adapters/kiro-events.ts +19 -1
  89. package/src/adapters/kiro-thinking.ts +18 -2
  90. package/src/adapters/kiro-tools.ts +65 -17
  91. package/src/adapters/kiro.ts +270 -73
  92. package/src/adapters/mimo-free.ts +17 -0
  93. package/src/adapters/openai-chat-url.ts +11 -0
  94. package/src/adapters/openai-chat.ts +1377 -344
  95. package/src/adapters/openai-responses-url.ts +16 -0
  96. package/src/adapters/openai-responses.ts +1164 -64
  97. package/src/adapters/registry.ts +175 -0
  98. package/src/adapters/responses-tool-schema.ts +67 -0
  99. package/src/adapters/run-turn-queue.ts +36 -1
  100. package/src/adapters/tool-call-id.ts +119 -0
  101. package/src/adapters/tool-catalog-nudge.ts +105 -21
  102. package/src/adapters/xai-tool-schema.ts +436 -0
  103. package/src/adapters/xai-web-search.ts +186 -0
  104. package/src/bridge.ts +512 -92
  105. package/src/chat/inbound.ts +57 -20
  106. package/src/chat/outbound.ts +97 -33
  107. package/src/claude/agents-inject.ts +40 -10
  108. package/src/claude/context-windows.ts +37 -14
  109. package/src/claude/desktop-3p.ts +243 -9
  110. package/src/claude/gateway-cache.ts +41 -4
  111. package/src/claude/inbound.ts +72 -3
  112. package/src/claude/model-info.ts +38 -15
  113. package/src/claude/outbound.ts +88 -21
  114. package/src/cli/access.ts +46 -3
  115. package/src/cli/account-api.ts +150 -19
  116. package/src/cli/account-auth.ts +33 -6
  117. package/src/cli/account-catalog-refresh.ts +14 -0
  118. package/src/cli/account-extended.ts +648 -28
  119. package/src/cli/account-main.ts +317 -0
  120. package/src/cli/account.ts +97 -19
  121. package/src/cli/agent.ts +53 -2
  122. package/src/cli/alias.ts +66 -0
  123. package/src/cli/capabilities-command.ts +94 -0
  124. package/src/cli/capabilities.ts +496 -0
  125. package/src/cli/claude-agent-startup-sync.ts +73 -0
  126. package/src/cli/claude-desktop.ts +60 -15
  127. package/src/cli/claude.ts +129 -35
  128. package/src/cli/codex-log-guard-doctor.ts +103 -0
  129. package/src/cli/codex-shim-autorestore.ts +2 -0
  130. package/src/cli/codex-shim-readiness.ts +76 -0
  131. package/src/cli/combo.ts +8 -0
  132. package/src/cli/config-command.ts +74 -10
  133. package/src/cli/dispatch.ts +821 -0
  134. package/src/cli/doctor.ts +440 -56
  135. package/src/cli/ensure-desired-integrations.ts +152 -0
  136. package/src/cli/export-command.ts +46 -20
  137. package/src/cli/help.ts +34 -274
  138. package/src/cli/index.ts +417 -528
  139. package/src/cli/init.ts +5 -17
  140. package/src/cli/inspect.ts +230 -0
  141. package/src/cli/integrations.ts +120 -2
  142. package/src/cli/lab.ts +607 -0
  143. package/src/cli/launcher-context.ts +77 -0
  144. package/src/cli/minimax.ts +497 -0
  145. package/src/cli/models-runtime.ts +130 -2
  146. package/src/cli/models.ts +118 -16
  147. package/src/cli/observe.ts +123 -11
  148. package/src/cli/opencode.ts +4 -2
  149. package/src/cli/provider-runtime.ts +34 -3
  150. package/src/cli/provider.ts +25 -3
  151. package/src/cli/ready.ts +301 -0
  152. package/src/cli/registry.ts +476 -0
  153. package/src/cli/root.ts +86 -0
  154. package/src/cli/route-policy.ts +92 -0
  155. package/src/cli/runtime-api.ts +57 -10
  156. package/src/cli/star-prompt.ts +71 -15
  157. package/src/cli/status.ts +51 -4
  158. package/src/cli/storage.ts +234 -0
  159. package/src/cli/system-command.ts +16 -0
  160. package/src/cli/system-restart-client.ts +146 -0
  161. package/src/cli/tray-proxy.ts +153 -6
  162. package/src/cli/usage-report.ts +184 -0
  163. package/src/cli/v2.ts +137 -18
  164. package/src/cli/version-skew.ts +46 -0
  165. package/src/cli.ts +1 -1
  166. package/src/clients/config-export.ts +1442 -23
  167. package/src/clients/effective-status.ts +30 -2
  168. package/src/clients/probes/cline.ts +81 -0
  169. package/src/codex/account-label.ts +35 -1
  170. package/src/codex/account-lifecycle.ts +130 -13
  171. package/src/codex/account-namespaces.ts +49 -3
  172. package/src/codex/account-pause.ts +2 -1
  173. package/src/codex/account-priority.ts +84 -0
  174. package/src/codex/account-store.ts +29 -2
  175. package/src/codex/account-usability.ts +25 -2
  176. package/src/codex/admission.ts +256 -0
  177. package/src/codex/affinity-debug.ts +162 -0
  178. package/src/codex/app-server-processes.ts +570 -107
  179. package/src/codex/app-server-restart-service.ts +232 -0
  180. package/src/codex/auth-api.ts +893 -246
  181. package/src/codex/auth-collision.ts +5 -3
  182. package/src/codex/auth-context.ts +348 -32
  183. package/src/codex/autostart-health.ts +24 -1
  184. package/src/codex/catalog/account-models.ts +67 -0
  185. package/src/codex/catalog/aggregation.ts +86 -10
  186. package/src/codex/catalog/bundled.ts +331 -33
  187. package/src/codex/catalog/effort.ts +138 -32
  188. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  189. package/src/codex/catalog/kinds.ts +2 -0
  190. package/src/codex/catalog/metadata.ts +555 -45
  191. package/src/codex/catalog/native-models.ts +75 -0
  192. package/src/codex/catalog/parsing.ts +269 -30
  193. package/src/codex/catalog/provider-fetch.ts +1626 -144
  194. package/src/codex/catalog/sync.ts +1576 -187
  195. package/src/codex/catalog-admission.ts +199 -0
  196. package/src/codex/catalog-refresh-status.ts +105 -0
  197. package/src/codex/catalog-write-serialization.ts +242 -0
  198. package/src/codex/catalog.ts +6 -3
  199. package/src/codex/codex-write-lock.ts +386 -0
  200. package/src/codex/convergence-types.ts +614 -0
  201. package/src/codex/convergence.ts +676 -0
  202. package/src/codex/coordinator-doctor.ts +332 -0
  203. package/src/codex/custom-model-catalog-migration.ts +176 -0
  204. package/src/codex/desired-state.ts +230 -0
  205. package/src/codex/desktop-app-restart.ts +355 -0
  206. package/src/codex/features.ts +636 -39
  207. package/src/codex/generation.ts +202 -0
  208. package/src/codex/history-job.ts +436 -0
  209. package/src/codex/history-lock.ts +242 -0
  210. package/src/codex/history-manifest.ts +112 -0
  211. package/src/codex/history-migration-guardian.ts +30 -24
  212. package/src/codex/history-provider.ts +1016 -235
  213. package/src/codex/history-transition.ts +105 -0
  214. package/src/codex/history-worker.ts +223 -0
  215. package/src/codex/inject-coordination.ts +373 -0
  216. package/src/codex/inject.ts +1114 -152
  217. package/src/codex/injected-marker.ts +37 -3
  218. package/src/codex/integration-record.ts +266 -0
  219. package/src/codex/internal/catalog-writer.ts +203 -0
  220. package/src/codex/internal/history-writer.ts +81 -0
  221. package/src/codex/journal.ts +66 -4
  222. package/src/codex/log-guard/inspect.ts +524 -0
  223. package/src/codex/log-guard/lock.ts +150 -0
  224. package/src/codex/log-guard/maintenance.ts +403 -0
  225. package/src/codex/log-guard/path-safety.ts +88 -0
  226. package/src/codex/log-guard/policy.ts +44 -0
  227. package/src/codex/log-guard/processes.ts +205 -0
  228. package/src/codex/log-guard/protection.ts +489 -0
  229. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  230. package/src/codex/main-account-cache.ts +24 -0
  231. package/src/codex/main-account.ts +29 -1
  232. package/src/codex/management-convergence.ts +167 -0
  233. package/src/codex/model-cache.ts +56 -10
  234. package/src/codex/model-entitlements.ts +360 -0
  235. package/src/codex/native-main-admission.ts +47 -0
  236. package/src/codex/native-main-auth-temp.ts +187 -0
  237. package/src/codex/native-main-claim.ts +178 -0
  238. package/src/codex/native-main-lock-file.ts +162 -0
  239. package/src/codex/native-main-owner.ts +329 -0
  240. package/src/codex/native-profile-api.ts +247 -0
  241. package/src/codex/native-profile-manager.ts +1531 -0
  242. package/src/codex/native-profile-processes.ts +121 -0
  243. package/src/codex/native-profile-recovery.ts +99 -0
  244. package/src/codex/native-profile-stage-store.ts +387 -0
  245. package/src/codex/native-profile-startup.ts +622 -0
  246. package/src/codex/native-profile-store.ts +855 -0
  247. package/src/codex/native-profile-types.ts +120 -0
  248. package/src/codex/native-residue.ts +675 -0
  249. package/src/codex/paths.ts +80 -1
  250. package/src/codex/plan-from-token.ts +140 -0
  251. package/src/codex/plan.ts +40 -0
  252. package/src/codex/plugins-doctor.ts +1 -1
  253. package/src/codex/pool-rotation.ts +74 -4
  254. package/src/codex/project-config-warnings.ts +20 -6
  255. package/src/codex/prompt-journal.ts +352 -0
  256. package/src/codex/prompt-layers.ts +1313 -0
  257. package/src/codex/prompt-lock.ts +143 -0
  258. package/src/codex/prompt-text-probe.ts +238 -0
  259. package/src/codex/quota-rejection.ts +298 -0
  260. package/src/codex/quota.ts +264 -21
  261. package/src/codex/refresh.ts +11 -2
  262. package/src/codex/reset-credit-recovery.ts +1044 -0
  263. package/src/codex/routing.ts +514 -94
  264. package/src/codex/runtime.ts +159 -38
  265. package/src/codex/shim.ts +1065 -31
  266. package/src/codex/subagent-model-fallback.ts +437 -43
  267. package/src/codex/sync.ts +191 -2
  268. package/src/codex/transition-state.ts +720 -0
  269. package/src/codex/upstream-host-health.ts +368 -0
  270. package/src/codex/user-identity.ts +557 -0
  271. package/src/codex/warmup.ts +187 -81
  272. package/src/codex/write-coordination.ts +114 -0
  273. package/src/combos/failover.ts +47 -0
  274. package/src/combos/index.ts +4 -0
  275. package/src/combos/request.ts +32 -0
  276. package/src/combos/types.ts +91 -9
  277. package/src/compatibility/index.ts +26 -0
  278. package/src/compatibility/manifest.ts +253 -0
  279. package/src/compatibility/openai-responses.ts +121 -0
  280. package/src/config/atomic-write.ts +219 -0
  281. package/src/config/paths.ts +40 -0
  282. package/src/config/process-state.ts +309 -0
  283. package/src/config/provider-name.ts +24 -0
  284. package/src/config/provider-validation.ts +177 -0
  285. package/src/config/rebase-provenance.ts +68 -0
  286. package/src/config.ts +1713 -709
  287. package/src/generated/compatibility-version.json +3324 -0
  288. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  289. package/src/grok/inject.ts +16 -5
  290. package/src/grok/inspect.ts +45 -0
  291. package/src/grok/sync.ts +2 -2
  292. package/src/images/loop.ts +157 -33
  293. package/src/images/plan.ts +24 -13
  294. package/src/integrations/config-io.ts +269 -0
  295. package/src/integrations/journal.ts +315 -0
  296. package/src/integrations/merge.ts +135 -0
  297. package/src/integrations/mutation-flight.ts +71 -0
  298. package/src/integrations/native/ownership-preflight.ts +202 -0
  299. package/src/integrations/omp-yaml-source.ts +358 -0
  300. package/src/integrations/owned-refresh.ts +74 -0
  301. package/src/integrations/ownership-policy.ts +160 -0
  302. package/src/integrations/ownership.ts +155 -0
  303. package/src/integrations/registry.ts +166 -0
  304. package/src/integrations/serialize.ts +314 -0
  305. package/src/integrations/state.ts +433 -0
  306. package/src/integrations/store.ts +103 -0
  307. package/src/integrations/writer-lock.ts +98 -0
  308. package/src/integrations/writer.ts +715 -0
  309. package/src/lab/artifacts/sanitize.ts +586 -0
  310. package/src/lab/artifacts/secure-fs.ts +475 -0
  311. package/src/lab/artifacts/store.ts +310 -0
  312. package/src/lab/automation/budgets.ts +78 -0
  313. package/src/lab/automation/config-persistence.ts +256 -0
  314. package/src/lab/automation/constants.ts +39 -0
  315. package/src/lab/automation/cooldown.ts +103 -0
  316. package/src/lab/automation/dispatch.ts +211 -0
  317. package/src/lab/automation/index.ts +13 -0
  318. package/src/lab/automation/orchestrator.ts +499 -0
  319. package/src/lab/automation/persistence.ts +512 -0
  320. package/src/lab/automation/planner.ts +371 -0
  321. package/src/lab/automation/policy.ts +136 -0
  322. package/src/lab/automation/queue.ts +191 -0
  323. package/src/lab/automation/recovery.ts +24 -0
  324. package/src/lab/automation/route-context.ts +21 -0
  325. package/src/lab/automation/run-key.ts +44 -0
  326. package/src/lab/automation/runs-query.ts +34 -0
  327. package/src/lab/automation/types.ts +160 -0
  328. package/src/lab/conformance/assertion.ts +325 -0
  329. package/src/lab/conformance/digest.ts +22 -0
  330. package/src/lab/conformance/executor.ts +741 -0
  331. package/src/lab/conformance/fixture-provider.ts +27 -0
  332. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  333. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  334. package/src/lab/conformance/harness-budget.ts +47 -0
  335. package/src/lab/conformance/index.ts +5 -0
  336. package/src/lab/conformance/jcs.ts +64 -0
  337. package/src/lab/conformance/json-pointer.ts +39 -0
  338. package/src/lab/conformance/manifest.ts +180 -0
  339. package/src/lab/conformance/mcp-stub.ts +179 -0
  340. package/src/lab/conformance/negative-controls.ts +164 -0
  341. package/src/lab/conformance/observation.ts +355 -0
  342. package/src/lab/conformance/runner.ts +68 -0
  343. package/src/lab/conformance/sse-normalize.ts +59 -0
  344. package/src/lab/conformance/suite-manifest.ts +78 -0
  345. package/src/lab/conformance/types.ts +214 -0
  346. package/src/lab/constants.ts +126 -0
  347. package/src/lab/digest.ts +64 -0
  348. package/src/lab/events/errors.ts +9 -0
  349. package/src/lab/events/limits.ts +117 -0
  350. package/src/lab/events/types.ts +229 -0
  351. package/src/lab/events/validate.ts +781 -0
  352. package/src/lab/fabric/constants.ts +40 -0
  353. package/src/lab/fabric/executor.ts +492 -0
  354. package/src/lab/fabric/index.ts +80 -0
  355. package/src/lab/fabric/manifest.ts +222 -0
  356. package/src/lab/fabric/observe.ts +489 -0
  357. package/src/lab/fabric/patch.ts +79 -0
  358. package/src/lab/fabric/producer-child.ts +139 -0
  359. package/src/lab/fabric/producer-isolate.ts +276 -0
  360. package/src/lab/fabric/producer-protocol.ts +61 -0
  361. package/src/lab/fabric/scratch.ts +439 -0
  362. package/src/lab/fabric/subject.ts +106 -0
  363. package/src/lab/fabric/types.ts +134 -0
  364. package/src/lab/fabric/verifier.ts +98 -0
  365. package/src/lab/index.ts +54 -0
  366. package/src/lab/ledger/artifact-refs.ts +127 -0
  367. package/src/lab/ledger/invalidation.ts +136 -0
  368. package/src/lab/ledger/purge.ts +310 -0
  369. package/src/lab/ledger/store.ts +532 -0
  370. package/src/lab/live/credential-lease.ts +53 -0
  371. package/src/lab/live/destination.ts +155 -0
  372. package/src/lab/live/executor.ts +336 -0
  373. package/src/lab/live/inert-tools.ts +56 -0
  374. package/src/lab/live/manifest.ts +85 -0
  375. package/src/lab/live/mcp-loopback.ts +57 -0
  376. package/src/lab/live/runner.ts +19 -0
  377. package/src/lab/live/sandbox.ts +61 -0
  378. package/src/lab/live/suite-manifest.ts +41 -0
  379. package/src/lab/live/transport.ts +118 -0
  380. package/src/lab/live/types.ts +197 -0
  381. package/src/lab/observe/from-conformance.ts +301 -0
  382. package/src/lab/observe/from-live.ts +117 -0
  383. package/src/lab/paths.ts +153 -0
  384. package/src/lab/projection/rebuild.ts +495 -0
  385. package/src/lab/projection/schema.ts +135 -0
  386. package/src/lab/projection/verdicts.ts +474 -0
  387. package/src/lab/projection/verification.ts +412 -0
  388. package/src/lab/public/bundle.ts +217 -0
  389. package/src/lab/public/community-authority.ts +175 -0
  390. package/src/lab/public/community-files.ts +29 -0
  391. package/src/lab/public/community.ts +479 -0
  392. package/src/lab/public/file-safety.ts +155 -0
  393. package/src/lab/public/ids.ts +26 -0
  394. package/src/lab/public/index.ts +16 -0
  395. package/src/lab/public/mutation-lock.ts +424 -0
  396. package/src/lab/public/operator.ts +353 -0
  397. package/src/lab/public/origin-purge.ts +79 -0
  398. package/src/lab/public/origin.ts +203 -0
  399. package/src/lab/public/privacy.ts +143 -0
  400. package/src/lab/public/private-file.ts +261 -0
  401. package/src/lab/public/project.ts +124 -0
  402. package/src/lab/public/purge-test-fault.ts +21 -0
  403. package/src/lab/public/purge.ts +223 -0
  404. package/src/lab/public/registry.ts +44 -0
  405. package/src/lab/public/revocation.ts +252 -0
  406. package/src/lab/public/signature.ts +243 -0
  407. package/src/lab/public/storage.ts +105 -0
  408. package/src/lab/public/strict-json.ts +206 -0
  409. package/src/lab/public/time.ts +26 -0
  410. package/src/lab/public/types.ts +172 -0
  411. package/src/lab/public/validate.ts +391 -0
  412. package/src/lab/query/catalog.ts +101 -0
  413. package/src/lab/query/connection.ts +107 -0
  414. package/src/lab/query/constants.ts +4 -0
  415. package/src/lab/query/cursor.ts +132 -0
  416. package/src/lab/query/dto-map.ts +277 -0
  417. package/src/lab/query/errors.ts +22 -0
  418. package/src/lab/query/freshness.ts +53 -0
  419. package/src/lab/query/index.ts +45 -0
  420. package/src/lab/query/latest-observation.ts +59 -0
  421. package/src/lab/query/passive-production.ts +159 -0
  422. package/src/lab/query/queries.ts +444 -0
  423. package/src/lab/query/types.ts +266 -0
  424. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  425. package/src/lab/subject/installation-salt.ts +112 -0
  426. package/src/lab/subject/protocol-subject.ts +80 -0
  427. package/src/lab/subject/route-subject.ts +74 -0
  428. package/src/lib/admin-secrets.ts +24 -0
  429. package/src/lib/app-owned-memory-stores.ts +22 -0
  430. package/src/lib/bounded-body.ts +167 -11
  431. package/src/lib/bun-runtime.ts +125 -12
  432. package/src/lib/bun-stream-caps.ts +13 -9
  433. package/src/lib/codex-restart-contract.ts +120 -0
  434. package/src/lib/config-ownership.ts +26 -2
  435. package/src/lib/destination-policy.ts +65 -1
  436. package/src/lib/errors.ts +80 -5
  437. package/src/lib/fabric-task-execution-authority.ts +7 -0
  438. package/src/lib/fabric-task-host.ts +29 -0
  439. package/src/lib/lab-activation.ts +223 -0
  440. package/src/lib/lab-live-execution-authority.ts +13 -0
  441. package/src/lib/lab-live-host.ts +30 -0
  442. package/src/lib/lab-live-pinned-sender.ts +56 -0
  443. package/src/lib/lab-live-route-production.ts +130 -0
  444. package/src/lib/lab-passive-linker-registration.ts +26 -0
  445. package/src/lib/local-management-attestation.ts +51 -0
  446. package/src/lib/local-management-capability.ts +100 -0
  447. package/src/lib/local-provider-reload-contract.ts +100 -0
  448. package/src/lib/optional-shutdown-hooks.ts +57 -0
  449. package/src/lib/package-tree-integrity.ts +101 -0
  450. package/src/lib/pinned-http.ts +145 -26
  451. package/src/lib/process-control.ts +6 -2
  452. package/src/lib/provider-outbound.ts +49 -9
  453. package/src/lib/redact.ts +419 -3
  454. package/src/lib/self-launch-argv.ts +15 -0
  455. package/src/lib/server-resource-ownership.ts +71 -0
  456. package/src/lib/service-secrets.ts +15 -0
  457. package/src/lib/shadow-call.ts +35 -4
  458. package/src/lib/sse-decoder.ts +41 -0
  459. package/src/lib/state-store-registrations.ts +10 -2
  460. package/src/lib/system-restart-contract.ts +73 -0
  461. package/src/lib/token-estimate.ts +19 -2
  462. package/src/lib/tool-argument-integers.ts +253 -0
  463. package/src/lib/translator-budget.ts +44 -0
  464. package/src/lib/upstream-http-version.ts +57 -0
  465. package/src/lib/upstream-reachability.ts +95 -0
  466. package/src/lib/upstream-retry.ts +156 -3
  467. package/src/lib/windows-atomic-replace.ts +155 -0
  468. package/src/lib/windows-elevation.ts +70 -2
  469. package/src/lib/windows-secret-acl.ts +409 -69
  470. package/src/lib/windows-service-wrappers.ts +72 -0
  471. package/src/lib/windows-text.ts +106 -0
  472. package/src/lib/windows-user-principal.ts +341 -0
  473. package/src/lib/winsw.ts +33 -5
  474. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  475. package/src/oauth/account-import/index.ts +15 -0
  476. package/src/oauth/account-import/parser.ts +83 -0
  477. package/src/oauth/account-import/registry.ts +18 -0
  478. package/src/oauth/account-import/service.ts +75 -0
  479. package/src/oauth/account-import/types.ts +91 -0
  480. package/src/oauth/anthropic.ts +12 -1
  481. package/src/oauth/callback-server.ts +30 -4
  482. package/src/oauth/chatgpt.ts +12 -1
  483. package/src/oauth/cline.ts +203 -0
  484. package/src/oauth/command-code.ts +239 -0
  485. package/src/oauth/cursor.ts +46 -5
  486. package/src/oauth/generic-account-failover.ts +231 -0
  487. package/src/oauth/google-antigravity.ts +35 -3
  488. package/src/oauth/health.ts +20 -12
  489. package/src/oauth/index.ts +462 -71
  490. package/src/oauth/key-providers.ts +25 -0
  491. package/src/oauth/kimi.ts +25 -3
  492. package/src/oauth/kiro.ts +95 -6
  493. package/src/oauth/local-token-detect.ts +13 -2
  494. package/src/oauth/log.ts +3 -1
  495. package/src/oauth/login-cli.ts +88 -28
  496. package/src/oauth/nous.ts +798 -0
  497. package/src/oauth/open-browser-choice.ts +26 -0
  498. package/src/oauth/store.ts +133 -26
  499. package/src/oauth/token-guardian.ts +9 -3
  500. package/src/oauth/types.ts +15 -0
  501. package/src/pi/models.ts +2 -2
  502. package/src/providers/alibaba-region-migration.ts +1 -1
  503. package/src/providers/antigravity-models.ts +521 -31
  504. package/src/providers/auto-compact-budget.ts +65 -0
  505. package/src/providers/base-url-choices.ts +10 -0
  506. package/src/providers/codex-capacity.ts +292 -0
  507. package/src/providers/command-code-efforts.ts +176 -0
  508. package/src/providers/context-cap.ts +26 -8
  509. package/src/providers/cursor-pool.ts +72 -0
  510. package/src/providers/default-aliases.ts +65 -0
  511. package/src/providers/derive.ts +281 -6
  512. package/src/providers/fastwire.ts +507 -0
  513. package/src/providers/free-directory.ts +10 -7
  514. package/src/providers/google-vertex-location.ts +14 -0
  515. package/src/providers/key-failover.ts +71 -3
  516. package/src/providers/label.ts +35 -2
  517. package/src/providers/model-discovery-limits.ts +16 -0
  518. package/src/providers/model-discovery.ts +115 -22
  519. package/src/providers/model-presets.ts +119 -0
  520. package/src/providers/model-rename-migration.ts +255 -0
  521. package/src/providers/model-rename-startup.ts +28 -0
  522. package/src/providers/new-model-policy.ts +146 -0
  523. package/src/providers/openai-sidecar.ts +72 -4
  524. package/src/providers/openai-tier-startup.ts +31 -2
  525. package/src/providers/openai-tiers.ts +119 -4
  526. package/src/providers/openai-virtual-models.ts +1 -0
  527. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  528. package/src/providers/provider-id-rewrite.ts +30 -0
  529. package/src/providers/quota.ts +1379 -40
  530. package/src/providers/registry.ts +1545 -112
  531. package/src/providers/request-pacing.ts +310 -0
  532. package/src/providers/service-tier.ts +277 -0
  533. package/src/providers/slug-codec.ts +94 -6
  534. package/src/providers/static-model-discovery.ts +86 -0
  535. package/src/providers/xai-responses-opt-in.ts +15 -0
  536. package/src/providers/xai-transport.ts +32 -4
  537. package/src/reasoning-effort.ts +68 -3
  538. package/src/responses/apply-patch-envelope.ts +63 -0
  539. package/src/responses/code-mode-helper-compat.ts +50 -0
  540. package/src/responses/compaction.ts +26 -1
  541. package/src/responses/custom-tool-compat.ts +384 -0
  542. package/src/responses/hosted-tool-policy.ts +9 -0
  543. package/src/responses/namespace-tool-compat.ts +435 -0
  544. package/src/responses/parser.ts +225 -38
  545. package/src/responses/provider-continuation.ts +98 -0
  546. package/src/responses/provider-opaque-metadata.ts +73 -0
  547. package/src/responses/reasoning-envelope.ts +9 -1
  548. package/src/responses/reasoning-replay-cache.ts +504 -0
  549. package/src/responses/schema.ts +12 -2
  550. package/src/responses/spill-store.ts +75 -10
  551. package/src/responses/state.ts +659 -31
  552. package/src/responses/thought-signature-replay.ts +364 -0
  553. package/src/responses/tool-search-compat.ts +301 -0
  554. package/src/responses/truncated-stop-reason.ts +60 -0
  555. package/src/responses/turn-termination.ts +107 -0
  556. package/src/router.ts +405 -31
  557. package/src/routing/analytics.ts +379 -0
  558. package/src/routing/capability.ts +244 -0
  559. package/src/routing/compatibility/assemble.ts +73 -0
  560. package/src/routing/compatibility/behavior.ts +278 -0
  561. package/src/routing/compatibility/catalog.ts +99 -0
  562. package/src/routing/compatibility/endpoint.ts +52 -0
  563. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  564. package/src/routing/compatibility/policy.ts +181 -0
  565. package/src/routing/compatibility/provider-slot.ts +56 -0
  566. package/src/routing/compatibility/reader.ts +110 -0
  567. package/src/routing/compatibility/subject.ts +191 -0
  568. package/src/routing/compatibility/types.ts +64 -0
  569. package/src/routing/compatibility/version.ts +104 -0
  570. package/src/routing/cost.ts +77 -0
  571. package/src/routing/evaluator.ts +495 -0
  572. package/src/routing/health.ts +412 -0
  573. package/src/routing/history/cursor.ts +43 -0
  574. package/src/routing/history/indexer.ts +605 -0
  575. package/src/routing/history/schema.ts +72 -0
  576. package/src/routing/profile-namespace.ts +15 -0
  577. package/src/routing/profile.ts +547 -0
  578. package/src/routing/quota.ts +155 -0
  579. package/src/routing/request-evidence.ts +45 -0
  580. package/src/routing/trace.ts +776 -0
  581. package/src/server/adapter-resolve.ts +2 -29
  582. package/src/server/auth-cors.ts +289 -48
  583. package/src/server/background-lifecycle.ts +182 -0
  584. package/src/server/chat-completions.ts +154 -72
  585. package/src/server/chat-native-sse.ts +331 -0
  586. package/src/server/chat-native.ts +445 -0
  587. package/src/server/claude-messages.ts +160 -44
  588. package/src/server/direct-local-http.ts +347 -0
  589. package/src/server/effort-policy.ts +18 -0
  590. package/src/server/github-copilot-responses-repair.ts +338 -0
  591. package/src/server/gui-static.ts +39 -10
  592. package/src/server/images.ts +94 -12
  593. package/src/server/index.ts +1021 -190
  594. package/src/server/lifecycle.ts +336 -14
  595. package/src/server/live.ts +136 -17
  596. package/src/server/local-management-read-client.ts +90 -0
  597. package/src/server/local-provider-reload-client.ts +137 -0
  598. package/src/server/management/agent-settings-routes.ts +433 -116
  599. package/src/server/management/api-key-usage.ts +31 -5
  600. package/src/server/management/body.ts +6 -0
  601. package/src/server/management/codex-prompt-routes.ts +570 -0
  602. package/src/server/management/combo-routes.ts +64 -25
  603. package/src/server/management/config-routes.ts +511 -51
  604. package/src/server/management/context.ts +89 -2
  605. package/src/server/management/integration-routes.ts +498 -0
  606. package/src/server/management/lab-automation-routes.ts +206 -0
  607. package/src/server/management/lab-routes.ts +563 -0
  608. package/src/server/management/logs-usage-routes.ts +127 -52
  609. package/src/server/management/model-routes.ts +461 -133
  610. package/src/server/management/model-rows.ts +167 -0
  611. package/src/server/management/native-integration-routes.ts +770 -0
  612. package/src/server/management/oauth-account-routes.ts +90 -8
  613. package/src/server/management/provider-capability-config.ts +48 -0
  614. package/src/server/management/provider-routes.ts +836 -159
  615. package/src/server/management/request-history-routes.ts +191 -0
  616. package/src/server/management/route-registry.ts +332 -0
  617. package/src/server/management/routing-analytics-routes.ts +74 -0
  618. package/src/server/management/routing-profile-routes.ts +380 -0
  619. package/src/server/management/shared.ts +27 -11
  620. package/src/server/management/sidebar-routes.ts +47 -31
  621. package/src/server/management/storage-log-guard-routes.ts +186 -0
  622. package/src/server/management/sync-response.ts +69 -0
  623. package/src/server/management/system-restart.ts +289 -33
  624. package/src/server/management/system-routes.ts +77 -0
  625. package/src/server/management/usage-summary-cache.ts +9 -1
  626. package/src/server/management/vision-sidecar-options.ts +167 -0
  627. package/src/server/management/web-search-sidecar-options.ts +120 -0
  628. package/src/server/management-api.ts +126 -23
  629. package/src/server/management-auth.ts +220 -5
  630. package/src/server/passive-route-linker.ts +66 -0
  631. package/src/server/port-reclaim.ts +1 -1
  632. package/src/server/ports.ts +41 -1
  633. package/src/server/proxy-liveness.ts +161 -10
  634. package/src/server/readiness.ts +99 -0
  635. package/src/server/relay-eager.ts +221 -54
  636. package/src/server/relay.ts +479 -87
  637. package/src/server/request-decompress.ts +113 -6
  638. package/src/server/request-log-conversation.ts +51 -0
  639. package/src/server/request-log.ts +298 -31
  640. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  641. package/src/server/responses/agent-task-recovery.ts +465 -0
  642. package/src/server/responses/codex-auth-error.ts +55 -0
  643. package/src/server/responses/collaboration.ts +239 -36
  644. package/src/server/responses/combo-stream-preflight.ts +178 -0
  645. package/src/server/responses/compact.ts +444 -72
  646. package/src/server/responses/core.ts +3579 -436
  647. package/src/server/responses/empty-completion-guard.ts +333 -0
  648. package/src/server/responses/encrypted-payload.ts +62 -39
  649. package/src/server/responses/fetch-helpers.ts +89 -99
  650. package/src/server/responses/input-admission.ts +187 -0
  651. package/src/server/responses/pacing-overload.ts +13 -0
  652. package/src/server/responses/passthrough-error.ts +33 -9
  653. package/src/server/responses/policy-fallback.ts +178 -0
  654. package/src/server/responses/responses-field-backfill.ts +343 -0
  655. package/src/server/responses/terminal-guard.ts +26 -5
  656. package/src/server/responses/upstream-error.ts +5 -0
  657. package/src/server/responses/ws-upstream.ts +381 -0
  658. package/src/server/responses-custom-tool-repair.ts +363 -0
  659. package/src/server/responses-item-id-repair.ts +54 -6
  660. package/src/server/responses-json-events.ts +90 -0
  661. package/src/server/responses-model-rewrite.ts +29 -0
  662. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  663. package/src/server/responses-snapshot-repair.ts +621 -0
  664. package/src/server/responses-terminal-repair.ts +363 -0
  665. package/src/server/responses-tool-search-repair.ts +267 -0
  666. package/src/server/responses-undeclared-tool-guard.ts +376 -0
  667. package/src/server/responses.ts +18 -2
  668. package/src/server/search.ts +78 -13
  669. package/src/server/sse-frame-buffer.ts +319 -0
  670. package/src/server/sse-payload-rewrite.ts +110 -22
  671. package/src/server/startup-action-control.ts +8 -1
  672. package/src/server/startup-health-cache.ts +19 -1
  673. package/src/server/system-env.ts +80 -9
  674. package/src/server/ws-bridge.ts +53 -40
  675. package/src/service-manager-probe.ts +892 -0
  676. package/src/service.ts +1153 -94
  677. package/src/sidecar/auth.ts +92 -0
  678. package/src/sidecar/candidates.ts +83 -0
  679. package/src/storage/cleanup.ts +2 -2
  680. package/src/storage/policy-job.ts +14 -4
  681. package/src/storage/policy.ts +88 -23
  682. package/src/storage/scanner.ts +1 -1
  683. package/src/storage/worker-lifecycle.ts +14 -14
  684. package/src/tray/windows-tray.ps1 +83 -9
  685. package/src/tray/windows.ts +43 -16
  686. package/src/types/accounts.ts +37 -0
  687. package/src/types/config.ts +911 -0
  688. package/src/types/provider.ts +614 -0
  689. package/src/types/request.ts +397 -0
  690. package/src/types/tools.ts +236 -0
  691. package/src/types/wire.ts +80 -0
  692. package/src/types.ts +107 -1237
  693. package/src/update/index.ts +37 -23
  694. package/src/update/job.ts +445 -68
  695. package/src/update/notify.ts +12 -6
  696. package/src/update/npm-cache-preflight.d.mts +47 -0
  697. package/src/update/npm-cache-preflight.mjs +201 -0
  698. package/src/update/transactional-install.d.mts +22 -0
  699. package/src/update/transactional-install.mjs +266 -0
  700. package/src/usage/cost.ts +0 -0
  701. package/src/usage/expected-prices.ts +287 -16
  702. package/src/usage/log.ts +666 -50
  703. package/src/usage/summary.ts +388 -17
  704. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  705. package/src/usage/user-cost-overlays.ts +314 -0
  706. package/src/vision/anthropic-describe.ts +10 -6
  707. package/src/vision/backends.ts +97 -0
  708. package/src/vision/describe.ts +27 -16
  709. package/src/vision/eligibility.ts +250 -0
  710. package/src/vision/index.ts +238 -24
  711. package/src/vision/reasoning.ts +55 -0
  712. package/src/vision/routed-describe.ts +175 -0
  713. package/src/vision/timeout-bounds.ts +9 -0
  714. package/src/web-search/anthropic-executor.ts +13 -7
  715. package/src/web-search/backends.ts +108 -0
  716. package/src/web-search/exa-executor.ts +119 -0
  717. package/src/web-search/executor.ts +21 -6
  718. package/src/web-search/gemini-executor.ts +141 -0
  719. package/src/web-search/index.ts +150 -15
  720. package/src/web-search/loop.ts +284 -54
  721. package/src/web-search/parse.ts +125 -30
  722. package/src/web-search/sources.ts +60 -0
  723. package/src/web-search/xai-executor.ts +219 -0
  724. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  725. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  726. package/gui/dist/provider-icons/antigravity.svg +0 -1
  727. package/gui/dist/provider-icons/claude.svg +0 -1
  728. package/gui/dist/provider-icons/copilot.svg +0 -1
  729. package/gui/dist/provider-icons/cursor.svg +0 -2
  730. package/gui/dist/provider-icons/gemini.svg +0 -1
  731. package/gui/dist/provider-icons/grok-color.svg +0 -1
  732. package/gui/dist/provider-icons/kiro.svg +0 -14
  733. package/src/cli/internal-dispatch.ts +0 -20
@@ -0,0 +1,333 @@
1
+ import type { AdapterEvent, OcxConfig, OcxUsage } from "../../types";
2
+
3
+ /**
4
+ * Empty-completion guard for Responses turns (port of codex-router's
5
+ * empty-completion-guard + single retry, PR #145).
6
+ *
7
+ * Failure mode: the upstream answers 200 and completes the turn but never
8
+ * produced output text or a tool call (a reasoning-only stream that ends with
9
+ * nothing is the canonical shape). The client has no code path for "the model
10
+ * said nothing", so it silently records the turn as done — the "random stop"
11
+ * nobody can explain. The guard holds pre-content events (reasoning deltas
12
+ * are deliberately NOT content), and when a terminal event arrives with no
13
+ * content it suppresses the terminal and retries the IDENTICAL turn once
14
+ * (same request bytes, same headers). If the retry is also empty — or fails
15
+ * upstream — the client sees a stated failure instead of a second silent
16
+ * success.
17
+ *
18
+ * The retry is explicitly enabled by top-level config. The environment switch
19
+ * is a disable-only emergency override: OCX_EMPTY_COMPLETION_RETRY=0 restores
20
+ * the previous relay behavior without editing the persisted config.
21
+ */
22
+ export const EMPTY_COMPLETION_RETRY_ENV = "OCX_EMPTY_COMPLETION_RETRY";
23
+
24
+ /** Retained pre-content events are bounded independently by count and encoded size. */
25
+ export const EMPTY_COMPLETION_MAX_BUFFERED_EVENTS = 1_024;
26
+ export const EMPTY_COMPLETION_MAX_BUFFERED_BYTES = 1_048_576;
27
+
28
+ export function emptyCompletionRetryEnabled(
29
+ config: Pick<OcxConfig, "emptyCompletionRetry">,
30
+ env: Record<string, string | undefined> = process.env,
31
+ ): boolean {
32
+ return config.emptyCompletionRetry === true && env[EMPTY_COMPLETION_RETRY_ENV] !== "0";
33
+ }
34
+
35
+ /** Surfaced when the single retry was also empty or failed upstream. */
36
+ export const EMPTY_COMPLETION_RETRY_FAILED_CODE = "empty_completion_retry_failed";
37
+
38
+ /**
39
+ * Observe an event stream for the empty-completion shape WITHOUT changing it (#2472).
40
+ *
41
+ * The guard above is opt-in, so with the default configuration a turn that completes with no
42
+ * output text and no tool call passes through untouched and the client records a silent
43
+ * success. That is the reported symptom: an empty result nobody can explain, with no trace
44
+ * that the proxy saw anything unusual.
45
+ *
46
+ * This is deliberately a passthrough observer, not a second guard. Retrying by default would
47
+ * re-send a turn that may have already had billable side effects; the honest default is to
48
+ * leave the stream alone and make the occurrence visible, so a user can correlate it and
49
+ * decide whether to enable the retry.
50
+ */
51
+ export async function* observeEmptyCompletion(
52
+ events: AsyncIterable<AdapterEvent>,
53
+ onEmptyTurn: () => void,
54
+ ): AsyncGenerator<AdapterEvent> {
55
+ let sawContent = false;
56
+ let sawTerminal = false;
57
+ for await (const event of events) {
58
+ // Reasoning is deliberately NOT content, matching the guard: a reasoning-only stream that
59
+ // ends with nothing is the canonical shape of this failure.
60
+ if (isContentEvent(event)) sawContent = true;
61
+ if (isTerminalEvent(event)) {
62
+ sawTerminal = true;
63
+ // Only a successful terminal is the silent failure. `error` and `incomplete` are already
64
+ // a stated outcome the client can render, so flagging them would be noise.
65
+ if (!sawContent && event.type === "done") onEmptyTurn();
66
+ }
67
+ yield event;
68
+ }
69
+ // A stream that ends before any terminal is the pre-output EOF variant of the same failure.
70
+ if (!sawContent && !sawTerminal) onEmptyTurn();
71
+ }
72
+
73
+ /**
74
+ * Terminal stop reasons the bridge renders as a visible `response.incomplete`
75
+ * (max_tokens / content_filter). Those are already a stated failure, not the
76
+ * silent empty success this guard exists to catch, and retrying the identical
77
+ * request would burn tokens for the same truncated result.
78
+ */
79
+ const VISIBLE_INCOMPLETE_STOP_REASONS = new Set(["max_tokens", "content_filter"]);
80
+ const UTF8_ENCODER = new TextEncoder();
81
+
82
+ function retainedEventBytes(event: AdapterEvent): number {
83
+ return UTF8_ENCODER.encode(JSON.stringify(event)).byteLength;
84
+ }
85
+
86
+ function isReasoningEvent(event: AdapterEvent): boolean {
87
+ return event.type === "thinking_delta"
88
+ || event.type === "thinking_signature"
89
+ || event.type === "redacted_thinking"
90
+ || event.type === "kiro_redacted_reasoning"
91
+ || event.type === "reasoning_raw_delta";
92
+ }
93
+
94
+ function isTerminalEvent(
95
+ event: AdapterEvent,
96
+ ): event is Extract<AdapterEvent, { type: "done" | "incomplete" | "error" }> {
97
+ return event.type === "done" || event.type === "incomplete" || event.type === "error";
98
+ }
99
+
100
+ /**
101
+ * Content means something the client can act on: output text or a tool call
102
+ * (web-search cells included). Reasoning deltas are deliberately not content —
103
+ * a turn that streams only reasoning and then completes with nothing is
104
+ * exactly the empty completion this guard exists to catch. Empty text deltas
105
+ * (some batch adapters always carry `""`) are not content either.
106
+ */
107
+ export function isContentEvent(event: AdapterEvent): boolean {
108
+ switch (event.type) {
109
+ case "text_delta":
110
+ return event.text.length > 0;
111
+ case "tool_call_start":
112
+ case "tool_call_delta":
113
+ case "tool_call_end":
114
+ case "web_search_call_begin":
115
+ case "web_search_call_end":
116
+ return true;
117
+ default:
118
+ return false;
119
+ }
120
+ }
121
+
122
+ export function emptyCompletionRetryFailedEvent(
123
+ usage?: OcxUsage,
124
+ retryFailedUpstream = false,
125
+ ): Extract<AdapterEvent, { type: "error" }> {
126
+ return {
127
+ type: "error",
128
+ status: 502,
129
+ errorType: "upstream_error",
130
+ code: EMPTY_COMPLETION_RETRY_FAILED_CODE,
131
+ message: retryFailedUpstream
132
+ ? "The model returned an empty completion and the retry failed upstream."
133
+ : "The model returned an empty completion. opencodex retried once and the completion was empty again.",
134
+ ...(usage ? { usage } : {}),
135
+ };
136
+ }
137
+
138
+ /**
139
+ * Sum two usage snapshots. Same semantics as terminal-guard's mergeUsage and
140
+ * request-log's aggregateAttemptUsage: token totals add across the attempts;
141
+ * `estimated` wins when either attempt only estimated.
142
+ */
143
+ export function mergeUsage(
144
+ first: OcxUsage | undefined,
145
+ second: OcxUsage | undefined,
146
+ ): OcxUsage | undefined {
147
+ if (!first) return second;
148
+ if (!second) return first;
149
+ const sumOptional = (key: keyof OcxUsage): number | undefined => {
150
+ const left = first[key];
151
+ const right = second[key];
152
+ return typeof left === "number" || typeof right === "number"
153
+ ? (typeof left === "number" ? left : 0) + (typeof right === "number" ? right : 0)
154
+ : undefined;
155
+ };
156
+ const cachedInputTokens = sumOptional("cachedInputTokens");
157
+ const cacheReadInputTokens = sumOptional("cacheReadInputTokens");
158
+ const cacheCreationInputTokens = sumOptional("cacheCreationInputTokens");
159
+ const reasoningOutputTokens = sumOptional("reasoningOutputTokens");
160
+ const contextTotalTokens = second.contextTotalTokens ?? first.contextTotalTokens;
161
+ const inputTokens = first.inputTokens + second.inputTokens;
162
+ const outputTokens = first.outputTokens + second.outputTokens;
163
+ return {
164
+ inputTokens,
165
+ outputTokens,
166
+ totalTokens: inputTokens + outputTokens,
167
+ ...(contextTotalTokens !== undefined ? { contextTotalTokens } : {}),
168
+ ...(cachedInputTokens !== undefined ? { cachedInputTokens } : {}),
169
+ ...(cacheReadInputTokens !== undefined ? { cacheReadInputTokens } : {}),
170
+ ...(cacheCreationInputTokens !== undefined ? { cacheCreationInputTokens } : {}),
171
+ ...(reasoningOutputTokens !== undefined ? { reasoningOutputTokens } : {}),
172
+ ...(first.estimated || second.estimated ? { estimated: true } : {}),
173
+ };
174
+ }
175
+
176
+ export interface EmptyCompletionGuardOptions {
177
+ firstEvents: AsyncIterable<AdapterEvent>;
178
+ /**
179
+ * Re-run the IDENTICAL turn: same request body, same headers, same signal.
180
+ * Receives no arguments — the request must not be modified between attempts.
181
+ */
182
+ continuation: () => AsyncIterable<AdapterEvent> | Promise<AsyncIterable<AdapterEvent>>;
183
+ /** How many times an empty completion is retried; default 1 (the router's single retry). */
184
+ maxRetries?: number;
185
+ }
186
+
187
+ /**
188
+ * Watch an adapter event stream for the empty-completion failure mode. Events
189
+ * are held until the turn produces content or ends: reasoning and other
190
+ * pre-content events stay buffered (released in order on first content), the
191
+ * terminal is withheld, and an empty terminal or pre-output EOF triggers one
192
+ * identical-turn retry through `continuation`. Usage is merged across attempts
193
+ * so the bridge and request log meter the whole turn, not just the attempt that
194
+ * succeeded.
195
+ *
196
+ * Heartbeats always pass through untouched: they feed the bridge's stall
197
+ * watchdog, so holding them behind the content gate would trip false
198
+ * upstream_stall_timeout failures on slow reasoning-only turns.
199
+ */
200
+ export async function* guardEmptyCompletionEventStream(
201
+ options: EmptyCompletionGuardOptions,
202
+ ): AsyncGenerator<AdapterEvent> {
203
+ const maxRetries = Math.max(0, Math.floor(options.maxRetries ?? 1));
204
+ let source = options.firstEvents;
205
+ let held: AdapterEvent[] = [];
206
+ let heldBytes = 0;
207
+ let sawContent = false;
208
+ let passthrough = false;
209
+ let retries = 0;
210
+ let usage: OcxUsage | undefined;
211
+
212
+ const withUsage = (event: AdapterEvent & { usage?: OcxUsage }): AdapterEvent => {
213
+ const merged = mergeUsage(usage, event.usage);
214
+ return merged ? { ...event, ...(merged ? { usage: merged } : {}) } : event;
215
+ };
216
+ const releaseHeld = (): AdapterEvent[] => {
217
+ const released = held;
218
+ held = [];
219
+ heldBytes = 0;
220
+ return released;
221
+ };
222
+
223
+ while (true) {
224
+ let terminalSeen = false;
225
+ for await (const event of source) {
226
+ if (event.type === "heartbeat") {
227
+ yield event;
228
+ continue;
229
+ }
230
+ if (sawContent || passthrough) {
231
+ // Buffered content is already flowing; everything downstream passes
232
+ // through. Every terminal carries usage merged across every attempt.
233
+ if (isTerminalEvent(event)) {
234
+ yield withUsage(event);
235
+ return;
236
+ }
237
+ yield event;
238
+ continue;
239
+ }
240
+ if (isContentEvent(event)) {
241
+ sawContent = true;
242
+ yield* releaseHeld();
243
+ yield event;
244
+ continue;
245
+ }
246
+ if (event.type === "done") {
247
+ usage = mergeUsage(usage, event.usage);
248
+ if (event.stopReason !== undefined && VISIBLE_INCOMPLETE_STOP_REASONS.has(event.stopReason)) {
249
+ // Rendered as response.incomplete: a stated failure, not the silent
250
+ // empty success this guard exists to catch.
251
+ yield* releaseHeld();
252
+ yield { ...event, ...(usage ? { usage } : {}) };
253
+ return;
254
+ }
255
+ if (retries < maxRetries) {
256
+ // Suppress the terminal: the client must never see a completed event
257
+ // for a turn that produced nothing. Retry the identical turn.
258
+ retries += 1;
259
+ try {
260
+ source = await options.continuation();
261
+ } catch {
262
+ yield emptyCompletionRetryFailedEvent(usage, true);
263
+ return;
264
+ }
265
+ terminalSeen = true;
266
+ break;
267
+ }
268
+ // The retry was also empty: a stated failure, not a second silent
269
+ // success.
270
+ yield emptyCompletionRetryFailedEvent(usage);
271
+ return;
272
+ }
273
+ if (event.type === "error") {
274
+ if (retries > 0 && event.status !== 499) {
275
+ // The retry failed upstream. Its body cannot reach the client (the
276
+ // 200 head went out with the first attempt), so state the failure in
277
+ // the stream's own error framing — same move as the router's
278
+ // empty_completion_retry_failed. Client cancels (499) pass through.
279
+ yield emptyCompletionRetryFailedEvent(mergeUsage(usage, event.usage), true);
280
+ return;
281
+ }
282
+ yield* releaseHeld();
283
+ yield withUsage(event);
284
+ return;
285
+ }
286
+ if (event.type === "incomplete") {
287
+ // A structured incomplete is already a visible failure; never convert
288
+ // it into an empty completion.
289
+ yield* releaseHeld();
290
+ yield withUsage(event);
291
+ return;
292
+ }
293
+ const eventBytes = retainedEventBytes(event);
294
+ if (held.length + 1 > EMPTY_COMPLETION_MAX_BUFFERED_EVENTS
295
+ || heldBytes + eventBytes > EMPTY_COMPLETION_MAX_BUFFERED_BYTES) {
296
+ // Preserve data rather than retaining without bound: release the prefix,
297
+ // emit this event, and stop attempting an empty-completion retry for the turn.
298
+ yield* releaseHeld();
299
+ yield event;
300
+ passthrough = true;
301
+ continue;
302
+ }
303
+ held.push(event);
304
+ heldBytes += eventBytes;
305
+ // The bridge watchdog sees only yielded events. Feed it while reasoning is
306
+ // held so a long reasoning-only prefix remains live without exposing it early.
307
+ if (isReasoningEvent(event)) yield { type: "heartbeat" };
308
+ }
309
+ if (!terminalSeen) {
310
+ // A terminal-less EOF before text or a tool call is replay-safe: nothing
311
+ // actionable reached the client. Retry once, then surface a stated error
312
+ // instead of letting the bridge reduce the turn to adapter_eof.
313
+ if (!sawContent && !passthrough && retries < maxRetries) {
314
+ retries += 1;
315
+ try {
316
+ source = await options.continuation();
317
+ } catch {
318
+ yield emptyCompletionRetryFailedEvent(usage, true);
319
+ return;
320
+ }
321
+ continue;
322
+ }
323
+ if (!sawContent && retries > 0) {
324
+ yield emptyCompletionRetryFailedEvent(usage, true);
325
+ return;
326
+ }
327
+ // Post-output EOF remains incomplete; replaying could duplicate text or
328
+ // executable tool calls.
329
+ yield* releaseHeld();
330
+ return;
331
+ }
332
+ }
333
+ }
@@ -145,6 +145,10 @@ function isStructurallyValidFernetToken(token: string): boolean {
145
145
  return ciphertextLength >= 16 && ciphertextLength % 16 === 0;
146
146
  }
147
147
 
148
+ export function structurallyValidFernetTokens(payload: string): string[] {
149
+ return fernetTokenRuns(payload).map(run => run.token);
150
+ }
151
+
148
152
  /** Maximal, boundary-delimited and structurally valid Fernet runs embedded in a slot. */
149
153
  function fernetTokenRuns(payload: string): FernetTokenRun[] {
150
154
  const runs: FernetTokenRun[] = [];
@@ -261,48 +265,67 @@ export function hasEncryptedContentPart(content: unknown): boolean {
261
265
  export function sanitizeEncryptedContentInPlace(input: unknown): number {
262
266
  if (!Array.isArray(input)) return 0;
263
267
  let rewritten = 0;
264
- const visit = (node: unknown): number => {
265
- const before = rewritten;
266
- if (Array.isArray(node)) {
267
- for (let i = 0; i < node.length; i += 1) {
268
- const child = node[i] as unknown;
269
- if (
270
- child && typeof child === "object"
271
- && (child as { type?: unknown }).type === "encrypted_content"
272
- && typeof (child as { encrypted_content?: unknown }).encrypted_content === "string"
273
- ) {
274
- const payload = (child as { encrypted_content: string }).encrypted_content;
275
- if (!looksLikeBackendCiphertext(payload)) {
276
- const parts = encryptedSlotParts(payload);
277
- node.splice(i, 1, ...parts);
278
- i += parts.length - 1;
279
- rewritten += 1;
280
- continue;
281
- }
282
- }
283
- const childRewrites = visit(child);
284
- if (
285
- childRewrites > 0
286
- && child && typeof child === "object"
287
- && (child as { type?: unknown }).type === "agent_message"
288
- && !hasEncryptedContentPart((child as { content?: unknown }).content)
289
- ) {
290
- const message = child as { type: string; role?: string; id?: unknown; author?: unknown; recipient?: unknown };
291
- message.type = "message";
292
- message.role = "user";
293
- delete message.id;
294
- delete message.author;
295
- delete message.recipient;
268
+ type VisitFrame =
269
+ | { kind: "visit"; node: unknown }
270
+ | { kind: "array"; node: unknown[]; index: number }
271
+ | { kind: "object"; values: unknown[]; index: number }
272
+ | { kind: "agent"; message: Record<string, unknown>; rewrittenBefore: number };
273
+ const stack: VisitFrame[] = [{ kind: "visit", node: input }];
274
+
275
+ while (stack.length > 0) {
276
+ const frame = stack.pop()!;
277
+ if (frame.kind === "visit") {
278
+ if (Array.isArray(frame.node)) {
279
+ stack.push({ kind: "array", node: frame.node, index: 0 });
280
+ } else if (frame.node && typeof frame.node === "object") {
281
+ stack.push({ kind: "object", values: Object.values(frame.node), index: 0 });
282
+ }
283
+ continue;
284
+ }
285
+
286
+ if (frame.kind === "array") {
287
+ if (frame.index >= frame.node.length) continue;
288
+ const child = frame.node[frame.index] as unknown;
289
+ if (
290
+ child && typeof child === "object"
291
+ && (child as { type?: unknown }).type === "encrypted_content"
292
+ && typeof (child as { encrypted_content?: unknown }).encrypted_content === "string"
293
+ ) {
294
+ const payload = (child as { encrypted_content: string }).encrypted_content;
295
+ if (!looksLikeBackendCiphertext(payload)) {
296
+ const parts = encryptedSlotParts(payload);
297
+ frame.node.splice(frame.index, 1, ...parts);
298
+ rewritten += 1;
299
+ stack.push({ kind: "array", node: frame.node, index: frame.index + parts.length });
300
+ continue;
296
301
  }
297
302
  }
298
- return rewritten - before;
303
+ stack.push({ kind: "array", node: frame.node, index: frame.index + 1 });
304
+ if (child && typeof child === "object" && (child as { type?: unknown }).type === "agent_message") {
305
+ stack.push({ kind: "agent", message: child as Record<string, unknown>, rewrittenBefore: rewritten });
306
+ }
307
+ stack.push({ kind: "visit", node: child });
308
+ continue;
299
309
  }
300
- if (node && typeof node === "object") {
301
- for (const value of Object.values(node)) visit(value);
310
+
311
+ if (frame.kind === "object") {
312
+ if (frame.index >= frame.values.length) continue;
313
+ stack.push({ kind: "object", values: frame.values, index: frame.index + 1 });
314
+ stack.push({ kind: "visit", node: frame.values[frame.index] });
315
+ continue;
302
316
  }
303
- return rewritten - before;
304
- };
305
- visit(input);
317
+
318
+ if (
319
+ rewritten > frame.rewrittenBefore
320
+ && frame.message.type === "agent_message"
321
+ && !hasEncryptedContentPart(frame.message.content)
322
+ ) {
323
+ frame.message.type = "message";
324
+ frame.message.role = "user";
325
+ delete frame.message.id;
326
+ delete frame.message.author;
327
+ delete frame.message.recipient;
328
+ }
329
+ }
306
330
  return rewritten;
307
331
  }
308
-
@@ -1,102 +1,16 @@
1
1
  import type { Server } from "bun";
2
- import { bridgeToResponsesSSE, buildResponseJSON, formatErrorResponse, type ResponsesTerminalStatus } from "../../bridge";
3
2
  import {
4
- getConfigPath,
5
- multiAgentGuidanceEnabled,
6
- resolveEnvValue,
7
- } from "../../config";
8
- import { parseRequest } from "../../responses/parser";
9
- import { buildCompactV1Output, COMPACT_PROMPT, decodeCompactionSummary, extractCompactUserMessages } from "../../responses/compaction";
10
- import { FORWARD_HEADERS, sanitizeReasoningInputContent } from "../../adapters/openai-responses";
11
- import { expandPreviousResponseInput, previousResponseProviderState, rememberResponseState } from "../../responses/state";
12
- import { routeModel } from "../../router";
13
- import {
14
- advanceComboAfterFailure,
15
- comboDefaultEffort,
16
- comboFailureDecision,
17
- comboIdFromRawBody,
18
- concreteComboRequestBody,
19
- getCombo,
20
- isComboTargetInCooldown,
21
- NoAvailableComboTargetsError,
22
- noteComboSuccess,
23
- parseRetryAfterMs,
24
- pickComboTarget,
25
- targetKey,
26
- } from "../../combos";
27
- import { isInjectionDebugEnabled } from "../../lib/debug-settings";
28
- import { injectionDebugLog } from "../../lib/injection-debug-log";
29
- import { modelInList, namespacedToolName } from "../../types";
30
- import type { AdapterEvent, OcxConfig, OcxParsedRequest, OcxProviderConfig, OcxProviderContinuationState, OcxUsage } from "../../types";
31
- import {
32
- forceRefreshOAuthAccessSnapshot,
33
- getOAuthCredentialApiBaseUrl,
34
- getOAuthCredentialProjectId,
35
- getValidAccessTokenSnapshot,
36
- type OAuthAccessSnapshot,
37
- UnsupportedOAuthProviderError,
38
- } from "../../oauth";
39
- import { buildWebSearchTool, planWebSearch, runWithWebSearch, shouldResolveOpenAiWebSearchSidecar } from "../../web-search";
40
- import { describeImagesInPlace, planVisionSidecar, shouldResolveOpenAiVisionSidecar, stripImagesInPlace } from "../../vision";
41
- import { createAdapterEventQueue, preflightAdapterEvents } from "../../adapters/run-turn-queue";
42
- import {
43
- applyCodexAuthContextToProvider,
44
- CodexAccountCooldownError,
45
- CodexAuthContextError,
46
- CodexDirectAuthenticationError,
47
- CodexPoolAuthenticationError,
48
- CodexThreadAffinityExpiredError,
49
- headersForCodexAuthContext,
50
- isCodexAuthContextUsable,
51
- resolveCodexAuthContext,
52
- type CodexAuthContext,
53
- } from "../../codex/auth-context";
54
- import {
55
- formatCodexProviderForLog,
56
- recordCodexUpstreamOutcome,
57
- type CodexUpstreamOutcome,
58
- } from "../../codex/routing";
59
- import { fetchWithResetRetry, fetchWithTransientRetry, applyUpstreamRecoveryInit } from "../../lib/upstream-retry";
60
- import { ForwardAdmissionCredentialError, validateForwardAdmissionCredential } from "../auth-cors";
61
- import { listOpenAiForwardSidecarCandidates, resolveFirstUsableOpenAiSidecar, type ResolvedOpenAiForwardSidecar } from "../../providers/openai-sidecar";
62
- import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
63
- import { slugsEquivalent } from "../../providers/slug-codec";
64
- import { applyOpenAiVirtualModel, resolveOpenAiCompactModel } from "../../providers/openai-virtual-models";
65
- import { isUsageDebugEnabled } from "../../usage/debug";
66
- import { readJsonRequestBody, DecompressedBodyTooLargeError, UnsupportedContentEncodingError } from "../request-decompress";
67
- import { resolveAdapter, resolveWireProtocolOverride } from "../adapter-resolve";
68
- import { hasKeyPoolFailover, rotateProviderTransportOn429 } from "../../providers/key-failover";
69
- import { shouldAttemptImageTierRetry } from "../image-retry";
70
- import { resolveProviderTransport } from "../../providers/xai-transport";
3
+ codexWsUpstreamFetch,
4
+ currentBunRuntimeIdentity,
5
+ shouldUseCodexWsUpstream,
6
+ type BunRuntimeGateInput,
7
+ } from "./ws-upstream";
8
+ import type { OcxProviderConfig } from "../../types";
71
9
  import type { WsData } from "../ws-bridge";
72
- import { registerTurn, trackStreamLifetime, unregisterTurn } from "../lifecycle";
73
- import { redactSecretString } from "../../lib/redact";
74
- import { readBoundedResponseBody } from "../../lib/bounded-body";
75
- import { supportedLadderFor } from "../effort-policy";
76
- import {
77
- beginRequestAttempt,
78
- catalogModelSupportsServiceTier,
79
- finishRequestAttempt,
80
- inspectResponseLogJson,
81
- noteAttemptSend,
82
- readConfiguredCodexServiceTier,
83
- requestLogSpeedLabel,
84
- sealRequestAttemptIdentity,
85
- usageFromResponsesPayload,
86
- type RequestLogContext,
87
- } from "../request-log";
88
- import type { AttemptRecoveryKind } from "../../usage/log";
89
- import {
90
- consumeForInspection,
91
- consumeForResponseLogMetadata,
92
- markNativePassthroughSseResponse,
93
- relaySseWithFailedTail,
94
- relayWithAbort,
95
- sanitizePassthroughHeaders,
96
- } from "../relay";
97
- import { hasResponsesItemIdRepair, relaySseWithResponsesItemIdRepair } from "../responses-item-id-repair";
98
- import type { EffectiveSubagentRoster, SpawnAgentSurface } from "../../codex/catalog";
10
+ import { waitForProviderRequestSlot } from "../../providers/request-pacing";
11
+ import { withUpstreamHttpVersion } from "../../lib/upstream-http-version";
99
12
 
13
+ export { withUpstreamHttpVersion };
100
14
 
101
15
  export function disableResponsesRequestTimeout(req: Request, server: Pick<Server<WsData>, "timeout"> | undefined): boolean {
102
16
  if (!server) return false;
@@ -118,10 +32,78 @@ export function safeHostLabel(url: string): string {
118
32
  }
119
33
  }
120
34
 
35
+ /** Canonical origin (scheme + host) for failure-attribution keys: http and
36
+ * https for the same host must not share one ledger entry (#914 review). */
37
+ export function safeOriginLabel(url: string): string {
38
+ try {
39
+ return new URL(url).origin.toLowerCase();
40
+ } catch {
41
+ return "upstream";
42
+ }
43
+ }
44
+
121
45
 
122
46
 
123
- export function providerFetch(provider: OcxProviderConfig): typeof globalThis.fetch {
124
- return (provider as OcxProviderConfig & { fetch?: typeof globalThis.fetch }).fetch ?? globalThis.fetch;
47
+ export interface PaceAwareFetch {
48
+ waitForPacing?: (signal?: AbortSignal) => Promise<void>;
49
+ unpacedFetch?: typeof globalThis.fetch;
50
+ }
51
+
52
+ export type ProviderFetch = typeof globalThis.fetch & PaceAwareFetch;
53
+
54
+ export interface ProviderFetchOptions {
55
+ providerName?: string;
56
+ modelId?: string;
57
+ /** One pacing slot was acquired immediately before this fetch wrapper was created. */
58
+ pacingSlotAcquired?: boolean;
59
+ }
60
+
61
+ export function providerFetch(
62
+ provider: OcxProviderConfig,
63
+ runtime: BunRuntimeGateInput = currentBunRuntimeIdentity(),
64
+ options: ProviderFetchOptions = {},
65
+ ): ProviderFetch {
66
+ const base = (provider as OcxProviderConfig & { fetch?: typeof globalThis.fetch }).fetch ?? globalThis.fetch;
67
+ const preconnect = (...args: Parameters<typeof globalThis.fetch.preconnect>): void => {
68
+ base.preconnect?.(...args);
69
+ };
70
+ const httpFetch = Object.assign(
71
+ (input: Parameters<typeof globalThis.fetch>[0], init?: RequestInit) =>
72
+ base(input, { ...withUpstreamHttpVersion(input, init, provider), timeout: 0 }),
73
+ { preconnect },
74
+ ) as typeof globalThis.fetch;
75
+ // ChatGPT Codex backend: streaming turns ride the responses_websockets
76
+ // transport (measured ~3s faster TTFT than the SSE POST queue); everything
77
+ // else keeps the provider's HTTP fetch. See ws-upstream.ts for the details.
78
+ const unpaced = async (input: Parameters<typeof globalThis.fetch>[0], init?: RequestInit) => {
79
+ if (typeof input === "string" && init && shouldUseCodexWsUpstream(input, init, runtime)) {
80
+ // The fallback has to be the same HTTP fetch the non-WS branch would have
81
+ // used, protocol pin included: a WS turn that falls back is serving the
82
+ // request over HTTP, and dropping the provider's `upstreamHttpVersion`
83
+ // there would silently negotiate a transport the operator ruled out.
84
+ return codexWsUpstreamFetch(input, init, httpFetch, runtime);
85
+ }
86
+ return httpFetch(input, init);
87
+ };
88
+ let pacingSlotAcquired = options.pacingSlotAcquired === true;
89
+ const waitForPacing = (signal?: AbortSignal) => {
90
+ if (pacingSlotAcquired) {
91
+ pacingSlotAcquired = false;
92
+ return Promise.resolve();
93
+ }
94
+ return options.providerName
95
+ ? waitForProviderRequestSlot(options.providerName, provider, options.modelId, signal)
96
+ : Promise.resolve();
97
+ };
98
+ const wrapped = async (input: Parameters<typeof globalThis.fetch>[0], init?: RequestInit) => {
99
+ await waitForPacing(init?.signal ?? undefined);
100
+ return unpaced(input, init);
101
+ };
102
+ return Object.assign(wrapped, {
103
+ preconnect,
104
+ waitForPacing,
105
+ unpacedFetch: Object.assign(unpaced, { preconnect }),
106
+ });
125
107
  }
126
108
 
127
109
 
@@ -133,7 +115,11 @@ export async function fetchWithHeaderTimeout(
133
115
  timeoutMs: number,
134
116
  preferIdentityEncoding = false,
135
117
  executor: typeof globalThis.fetch = globalThis.fetch,
118
+ manualRedirect = false,
136
119
  ): Promise<Response> {
120
+ const pacing = executor as ProviderFetch;
121
+ await pacing.waitForPacing?.(abortSignal);
122
+ const fetchExecutor = pacing.unpacedFetch ?? executor;
137
123
  const timeout = new AbortController();
138
124
  const timer = setTimeout(() => {
139
125
  if (!timeout.signal.aborted) timeout.abort(new DOMException("Timeout elapsed", "TimeoutError"));
@@ -145,13 +131,17 @@ export async function fetchWithHeaderTimeout(
145
131
  headers.set("accept-encoding", "identity");
146
132
  }
147
133
  try {
148
- return await executor(url, {
134
+ return await fetchExecutor(url, {
149
135
  ...init,
150
136
  headers,
137
+ // Credential-bearing sends opt into manual redirects so a 3xx is relayed
138
+ // as a Response instead of being followed into a rejection that is
139
+ // indistinguishable from a pre-connection failure (#914).
140
+ ...(manualRedirect ? { redirect: "manual" as const } : {}),
151
141
  signal: AbortSignal.any([abortSignal, timeout.signal]),
142
+ timeout: 0,
152
143
  });
153
144
  } finally {
154
145
  clearTimeout(timer);
155
146
  }
156
147
  }
157
-