@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,376 @@
1
+ import { namespacedToolName, normalizeDeclaredToolName } from "../types";
2
+ import { sseDataPayload, type SseBlockRewrite } from "./sse-payload-rewrite";
3
+
4
+ /** Item types the client executes through a request-declared wire name. */
5
+ const CLIENT_EXECUTED_CALL_TYPES = new Set(["function_call", "custom_tool_call"]);
6
+
7
+ /**
8
+ * Hosted declarations whose response items the PROVIDER executes, keyed by the request
9
+ * declaration type. These need no client answer, so their names are deliberately absent from
10
+ * the request catalog and must not be read as an undeclared client tool.
11
+ *
12
+ * xAI surfaces hosted `x_search` as `custom_tool_call`. Probed 2026-08-23 against the OAuth CLI
13
+ * destination: its hosted calls use an `xs_call-` call-id prefix. Observed call names were
14
+ * `x_keyword_search`, `x_semantic_search`, and `x_user_search` — three literals for one tool,
15
+ * which is why authorization keys on the declaration, item type, and call-id prefix, never on
16
+ * the name.
17
+ */
18
+ export type ProviderExecutedCallType = Readonly<{
19
+ itemType: string;
20
+ callIdPrefix: string;
21
+ }>;
22
+
23
+ type ProviderExecutedCallTypes = ReadonlySet<ProviderExecutedCallType>;
24
+
25
+ export const PROVIDER_EXECUTED_DECLARATION_CALL_TYPES = new Map<string, ProviderExecutedCallType>([
26
+ ["x_search", { itemType: "custom_tool_call", callIdPrefix: "xs_call-" }],
27
+ ]);
28
+
29
+ /** Nameless declaration kinds whose response items still require client execution. */
30
+ const NAMELESS_CLIENT_DECLARATION_CALL_TYPES = new Map([
31
+ ["local_shell", "local_shell_call"],
32
+ ["tool_search", "tool_search_call"],
33
+ ["computer_use_preview", "computer_call"],
34
+ ["computer_use", "computer_call"],
35
+ ]);
36
+
37
+ const NAMELESS_CLIENT_CALL_DISPLAY_NAMES = new Map([
38
+ ["local_shell_call", "local_shell"],
39
+ ["tool_search_call", "tool_search"],
40
+ ["computer_call", "computer_use"],
41
+ ]);
42
+
43
+ const EMPTY_DECLARED_NAMELESS_CLIENT_CALL_TYPES: ReadonlySet<string> = new Set();
44
+ const EMPTY_PROVIDER_EXECUTED_CALL_TYPES: ReadonlySet<ProviderExecutedCallType> = new Set();
45
+
46
+ /** Supported hosted/private declarations that carry no client-executable wire name. */
47
+ const NAMELESS_TOOL_SPEC_TYPES = new Set([
48
+ "web_search",
49
+ "web_search_preview",
50
+ "file_search",
51
+ "computer_use_preview",
52
+ "computer_use",
53
+ "code_interpreter",
54
+ "image_generation",
55
+ "image_gen",
56
+ "mcp",
57
+ "tool_search",
58
+ "local_shell",
59
+ "x_search",
60
+ ]);
61
+
62
+ /** An upstream-supplied name reaches the error message; keep it bounded. */
63
+ const MAX_REPORTED_NAME_CHARS = 100;
64
+
65
+ export const UNDECLARED_TOOL_CALL_ERROR_CODE = "undeclared_tool_call";
66
+
67
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
68
+ return !!value && typeof value === "object" && !Array.isArray(value);
69
+ }
70
+
71
+ function addWireToolName(names: Set<string>, tool: unknown, namespace?: string): void {
72
+ if (!isPlainObject(tool)) return;
73
+ const nestedFunction = tool.type === "function" && isPlainObject(tool.function)
74
+ ? tool.function
75
+ : undefined;
76
+ const name = typeof tool.name === "string" && tool.name.length > 0
77
+ ? tool.name
78
+ : typeof nestedFunction?.name === "string" && nestedFunction.name.length > 0
79
+ ? nestedFunction.name
80
+ : undefined;
81
+ if (!name) return;
82
+ names.add(name);
83
+ // Codex routes MCP calls by an explicit `namespace` field, so the same tool is reachable
84
+ // as a bare inner name or as the flattened form; accept both rather than guess which
85
+ // coordinate system this provider echoes back.
86
+ if (namespace) names.add(namespacedToolName(namespace, name));
87
+ }
88
+
89
+ /**
90
+ * Catalog view owned by the current Responses turn.
91
+ *
92
+ * `previous_response_id` expansion prepends stored input items, including historical
93
+ * `additional_tools` declarations. Those items remain conversation history but cannot grant
94
+ * execution authority to this turn. Top-level `tools` always belongs to the current request;
95
+ * only input catalogs at or after the replay boundary are current.
96
+ */
97
+ export function currentTurnWireToolCatalogBody(
98
+ body: unknown,
99
+ replayPrefixLength: number | undefined,
100
+ ): unknown {
101
+ if (!isPlainObject(body) || !Array.isArray(body.input)) return body;
102
+ if (typeof replayPrefixLength !== "number" || !Number.isFinite(replayPrefixLength)) return body;
103
+ const start = Math.min(body.input.length, Math.max(0, Math.trunc(replayPrefixLength)));
104
+ if (start === 0) return body;
105
+ return { ...body, input: body.input.slice(start) };
106
+ }
107
+
108
+ function addWireToolSpecs(names: Set<string>, specs: unknown): void {
109
+ if (!Array.isArray(specs)) return;
110
+ for (const spec of specs) {
111
+ if (!isPlainObject(spec)) continue;
112
+ if (spec.type === "namespace" && Array.isArray(spec.tools)) {
113
+ const namespace = typeof spec.name === "string" ? spec.name : undefined;
114
+ for (const inner of spec.tools) addWireToolName(names, inner, namespace);
115
+ continue;
116
+ }
117
+ addWireToolName(names, spec);
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Tool names the OUTBOUND Responses body actually declared.
123
+ *
124
+ * This reads the body that goes upstream rather than the parsed internal tool list: the
125
+ * passthrough forwards wire shapes (namespaced MCP groups, `additional_tools` items carried
126
+ * inside `input`, routed custom-tool rewrites) that the internal list flattens or renames, and
127
+ * only the wire names can be compared against what the provider echoes back.
128
+ */
129
+ export function collectDeclaredWireToolNames(body: unknown): Set<string> {
130
+ const names = new Set<string>();
131
+ if (!isPlainObject(body)) return names;
132
+ addWireToolSpecs(names, body.tools);
133
+ if (Array.isArray(body.input)) {
134
+ for (const item of body.input) {
135
+ if (
136
+ isPlainObject(item)
137
+ && (item.type === "additional_tools" || item.type === "tool_search_output")
138
+ ) addWireToolSpecs(names, item.tools);
139
+ }
140
+ }
141
+ return names;
142
+ }
143
+
144
+ function addNamelessClientCallTypes(callTypes: Set<string>, specs: unknown): void {
145
+ if (!Array.isArray(specs)) return;
146
+ for (const spec of specs) {
147
+ if (!isPlainObject(spec) || typeof spec.type !== "string") continue;
148
+ const callType = NAMELESS_CLIENT_DECLARATION_CALL_TYPES.get(spec.type);
149
+ if (callType) callTypes.add(callType);
150
+ }
151
+ }
152
+
153
+ function addProviderExecutedCallTypes(
154
+ callTypes: Set<ProviderExecutedCallType>,
155
+ specs: unknown,
156
+ ): void {
157
+ if (!Array.isArray(specs)) return;
158
+ for (const spec of specs) {
159
+ if (!isPlainObject(spec) || typeof spec.type !== "string") continue;
160
+ const callType = PROVIDER_EXECUTED_DECLARATION_CALL_TYPES.get(spec.type);
161
+ if (callType) callTypes.add(callType);
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Item types this turn's hosted declarations authorize the PROVIDER to emit unnamed.
167
+ *
168
+ * Caller must gate this on the destination actually being that provider; a declaration alone
169
+ * is not authority, or any upstream could claim a hosted shape it never serves.
170
+ */
171
+ export function collectProviderExecutedCallTypes(body: unknown): Set<ProviderExecutedCallType> {
172
+ const callTypes = new Set<ProviderExecutedCallType>();
173
+ if (!isPlainObject(body)) return callTypes;
174
+ addProviderExecutedCallTypes(callTypes, body.tools);
175
+ if (Array.isArray(body.input)) {
176
+ for (const item of body.input) {
177
+ if (
178
+ isPlainObject(item)
179
+ && (item.type === "additional_tools" || item.type === "tool_search_output")
180
+ ) addProviderExecutedCallTypes(callTypes, item.tools);
181
+ }
182
+ }
183
+ return callTypes;
184
+ }
185
+
186
+ function isAuthorizedProviderExecutedCall(
187
+ item: Record<string, unknown>,
188
+ callTypes: ProviderExecutedCallTypes,
189
+ ): boolean {
190
+ if (typeof item.call_id !== "string") return false;
191
+ for (const callType of callTypes) {
192
+ if (
193
+ item.type === callType.itemType
194
+ && item.call_id.startsWith(callType.callIdPrefix)
195
+ ) return true;
196
+ }
197
+ return false;
198
+ }
199
+
200
+ /** Nameless client-call item types authorized by supported request tool declarations. */
201
+ export function collectDeclaredNamelessClientCallTypes(body: unknown): Set<string> {
202
+ const callTypes = new Set<string>();
203
+ if (!isPlainObject(body)) return callTypes;
204
+ addNamelessClientCallTypes(callTypes, body.tools);
205
+ if (Array.isArray(body.input)) {
206
+ for (const item of body.input) {
207
+ if (
208
+ isPlainObject(item)
209
+ && (item.type === "additional_tools" || item.type === "tool_search_output")
210
+ ) {
211
+ addNamelessClientCallTypes(callTypes, item.tools);
212
+ }
213
+ }
214
+ }
215
+ return callTypes;
216
+ }
217
+
218
+ function isReadableWireToolSpec(spec: unknown): boolean {
219
+ if (!isPlainObject(spec) || typeof spec.type !== "string" || spec.type.length === 0) return false;
220
+ if (spec.type === "function") {
221
+ return (typeof spec.name === "string" && spec.name.length > 0)
222
+ || (isPlainObject(spec.function)
223
+ && typeof spec.function.name === "string"
224
+ && spec.function.name.length > 0);
225
+ }
226
+ if (spec.type === "custom") return typeof spec.name === "string" && spec.name.length > 0;
227
+ if (spec.type === "namespace") {
228
+ return typeof spec.name === "string"
229
+ && spec.name.length > 0
230
+ && Array.isArray(spec.tools)
231
+ && (spec.tools.length === 0 || spec.tools.some(inner =>
232
+ isPlainObject(inner)
233
+ && (inner.type === "function" || inner.type === "custom")
234
+ && typeof inner.name === "string"
235
+ && inner.name.length > 0
236
+ ));
237
+ }
238
+ if (NAMELESS_TOOL_SPEC_TYPES.has(spec.type)) return true;
239
+ return typeof spec.name === "string" && spec.name.length > 0;
240
+ }
241
+
242
+ function isReadableWireToolCatalog(value: unknown): boolean {
243
+ return Array.isArray(value)
244
+ && (value.length === 0 || value.some(isReadableWireToolSpec));
245
+ }
246
+
247
+ /** Whether a request contains a supported catalog, including an explicit empty deny-all array. */
248
+ export function hasExplicitWireToolCatalog(body: unknown): boolean {
249
+ if (!isPlainObject(body)) return false;
250
+ if (isReadableWireToolCatalog(body.tools)) return true;
251
+ if (!Array.isArray(body.input)) return false;
252
+ return body.input.some(item =>
253
+ isPlainObject(item)
254
+ && item.type === "additional_tools"
255
+ && isReadableWireToolCatalog(item.tools)
256
+ );
257
+ }
258
+
259
+ function undeclaredNameInItem(
260
+ item: unknown,
261
+ declared: ReadonlySet<string>,
262
+ declaredNamelessClientCallTypes: ReadonlySet<string>,
263
+ providerExecutedCallTypes: ProviderExecutedCallTypes = EMPTY_PROVIDER_EXECUTED_CALL_TYPES,
264
+ ): string | undefined {
265
+ if (!isPlainObject(item)) return undefined;
266
+ if (typeof item.type !== "string") return undefined;
267
+ // The provider executes this exact measured shape itself, so there is no client name to
268
+ // authorize. The caller supplies these signatures only for the matching destination and
269
+ // declarations; the item must additionally carry the hosted call-id prefix.
270
+ if (isAuthorizedProviderExecutedCall(item, providerExecutedCallTypes)) return undefined;
271
+ const namelessDisplayName = NAMELESS_CLIENT_CALL_DISPLAY_NAMES.get(item.type);
272
+ if (namelessDisplayName !== undefined) {
273
+ // Only Codex's explicit `execution: "client"` form delegates tool search to the client.
274
+ if (item.type === "tool_search_call" && item.execution !== "client") return undefined;
275
+ return declaredNamelessClientCallTypes.has(item.type) ? undefined : namelessDisplayName;
276
+ }
277
+ if (!CLIENT_EXECUTED_CALL_TYPES.has(item.type)) return undefined;
278
+ const name = item.name;
279
+ if (typeof name !== "string" || name.length === 0) return undefined;
280
+ if (typeof item.namespace === "string") {
281
+ // Namespaced calls are matched by their full wire name only — never legacy-normalize
282
+ // them, or an undeclared namespaced `exec_command` could slip through as bare `exec`.
283
+ if (declared.has(namespacedToolName(item.namespace, name))) return undefined;
284
+ return name;
285
+ }
286
+ const effectiveName = normalizeDeclaredToolName(name, declared);
287
+ if (declared.has(effectiveName)) return undefined;
288
+ return name;
289
+ }
290
+
291
+ /** First undeclared client tool named by a Responses SSE payload, or undefined. */
292
+ export function undeclaredToolCallName(
293
+ payload: unknown,
294
+ declared: ReadonlySet<string>,
295
+ declaredNamelessClientCallTypes: ReadonlySet<string> = EMPTY_DECLARED_NAMELESS_CLIENT_CALL_TYPES,
296
+ providerExecutedCallTypes: ProviderExecutedCallTypes = EMPTY_PROVIDER_EXECUTED_CALL_TYPES,
297
+ ): string | undefined {
298
+ if (!isPlainObject(payload)) return undefined;
299
+ if (payload.type === "response.output_item.added" || payload.type === "response.output_item.done") {
300
+ return undeclaredNameInItem(payload.item, declared, declaredNamelessClientCallTypes, providerExecutedCallTypes);
301
+ }
302
+ // Sparse gateways skip incremental items and only ever ship the terminal snapshot.
303
+ if (payload.type === "response.completed" || payload.type === "response.incomplete") {
304
+ return undeclaredToolCallNameInResponse(payload.response, declared, declaredNamelessClientCallTypes, providerExecutedCallTypes);
305
+ }
306
+ return undefined;
307
+ }
308
+
309
+ /** First undeclared client tool in a Responses object's `output` array, or undefined. */
310
+ export function undeclaredToolCallNameInResponse(
311
+ response: unknown,
312
+ declared: ReadonlySet<string>,
313
+ declaredNamelessClientCallTypes: ReadonlySet<string> = EMPTY_DECLARED_NAMELESS_CLIENT_CALL_TYPES,
314
+ providerExecutedCallTypes: ProviderExecutedCallTypes = EMPTY_PROVIDER_EXECUTED_CALL_TYPES,
315
+ ): string | undefined {
316
+ if (!isPlainObject(response) || !Array.isArray(response.output)) return undefined;
317
+ for (const item of response.output) {
318
+ const name = undeclaredNameInItem(item, declared, declaredNamelessClientCallTypes, providerExecutedCallTypes);
319
+ if (name !== undefined) return name;
320
+ }
321
+ return undefined;
322
+ }
323
+
324
+ export function undeclaredToolCallMessage(name: string): string {
325
+ const reported = name.slice(0, MAX_REPORTED_NAME_CHARS);
326
+ return `routed provider emitted undeclared client tool "${reported}"; only request-declared tools may be called`;
327
+ }
328
+
329
+ function failedBlocks(name: string, newline: string): readonly string[] {
330
+ const failure = {
331
+ type: "upstream_error",
332
+ code: UNDECLARED_TOOL_CALL_ERROR_CODE,
333
+ message: undeclaredToolCallMessage(name),
334
+ };
335
+ const payload = JSON.stringify({
336
+ type: "response.failed",
337
+ response: { status: "failed", error: failure, last_error: failure },
338
+ });
339
+ return [`event: response.failed${newline}data: ${payload}`, "data: [DONE]"];
340
+ }
341
+
342
+ /**
343
+ * Fail closed when a routed provider calls a tool the request never declared (#1700).
344
+ *
345
+ * The bridged paths already refuse such a call (`declaredToolNames` in src/bridge.ts), but the
346
+ * native Responses passthrough relayed it verbatim: Codex received a `function_call` for a tool
347
+ * it has no top-level handler for — `apply_patch`, which under code mode exists only as a nested
348
+ * `tools.apply_patch(...)` helper inside `exec` — and the turn surfaced as a bare `aborted` with
349
+ * no output and no explanation. Replacing the offending event with an explicit `response.failed`
350
+ * turns that silent dead end into a compatibility error naming the tool.
351
+ *
352
+ * Everything after the trip is dropped so a later `response.completed` cannot contradict the
353
+ * terminal already sent. Non-JSON and non-item blocks pass through untouched.
354
+ */
355
+ export function createUndeclaredToolCallGuardBlockRewrite(
356
+ declared: ReadonlySet<string>,
357
+ declaredNamelessClientCallTypes: ReadonlySet<string> = EMPTY_DECLARED_NAMELESS_CLIENT_CALL_TYPES,
358
+ providerExecutedCallTypes: ProviderExecutedCallTypes = EMPTY_PROVIDER_EXECUTED_CALL_TYPES,
359
+ ): SseBlockRewrite {
360
+ let tripped = false;
361
+ return (block: string) => {
362
+ if (tripped) return [];
363
+ const payload = sseDataPayload(block);
364
+ if (payload === null || payload === "[DONE]") return [block];
365
+ let parsed: unknown;
366
+ try {
367
+ parsed = JSON.parse(payload);
368
+ } catch {
369
+ return [block];
370
+ }
371
+ const name = undeclaredToolCallName(parsed, declared, declaredNamelessClientCallTypes, providerExecutedCallTypes);
372
+ if (name === undefined) return [block];
373
+ tripped = true;
374
+ return failedBlocks(name, block.includes("\r\n") ? "\r\n" : "\n");
375
+ };
376
+ }
@@ -1,9 +1,25 @@
1
1
  // AUTO-SPLIT facade: original responses.ts body moved into ./responses/* modules.
2
2
  // Public surface preserved exactly; importers keep using "src/server/responses".
3
+ import { handleResponsesCompact as handleResponsesCompactImpl } from "./responses/compact";
4
+ import { requestPacingOverloadResponse } from "./responses/pacing-overload";
5
+
3
6
  export { buildToolBridgeMaps, isV1CollabSurface, collabSurface, multiAgentGuidanceText, V2_GUIDANCE_CHAR_BUDGET, injectDeveloperMessage } from "./responses/collaboration";
4
7
  export type { MultiAgentGuidanceOptions, MultiAgentGuidanceDeps } from "./responses/collaboration";
5
8
  export { hasUnreadableEncryptedAgentTask, sanitizeEncryptedContentInPlace } from "./responses/encrypted-payload";
6
- export { COMPACT_RESPONSE_MAX_BYTES, bufferCompactResponse, handleResponsesCompact } from "./responses/compact";
9
+ export { COMPACT_RESPONSE_MAX_BYTES, bufferCompactResponse } from "./responses/compact";
7
10
  export { disableResponsesRequestTimeout, safeHostLabel, fetchWithHeaderTimeout } from "./responses/fetch-helpers";
8
- export { sidecarOutcomeRecorder, isShadowSourceModel, codexLogAccountId, usesCodexForwardPoolAuth, codexForwardTerminalOutcomeRecorder, decodeRequestErrorResponse, buildComboChildHeaders, handleResponses, linkAbortSignal } from "./responses/core";
11
+ export { sidecarOutcomeRecorder, isShadowSourceModel, codexLogAccountId, usesCodexForwardPoolAuth, codexForwardTerminalOutcomeRecorder, decodeRequestErrorResponse, buildComboChildHeaders, linkAbortSignal } from "./responses/core";
12
+ export { handleResponses, handleResponsesWithPolicyFallback, rankPolicyFallbackCandidates } from "./responses/policy-fallback";
9
13
  export { adapterNeedsForcedContinuation } from "./responses/core";
14
+
15
+ export async function handleResponsesCompact(
16
+ ...args: Parameters<typeof handleResponsesCompactImpl>
17
+ ): Promise<Response> {
18
+ try {
19
+ return await handleResponsesCompactImpl(...args);
20
+ } catch (error) {
21
+ const overload = requestPacingOverloadResponse(error);
22
+ if (overload) return overload;
23
+ throw error;
24
+ }
25
+ }
@@ -11,20 +11,31 @@
11
11
  import { formatErrorResponse } from "../bridge";
12
12
  import {
13
13
  CodexAccountCooldownError,
14
+ codexMainProfileDrainingResponse,
14
15
  cooldownErrorResponse,
15
16
  CodexAuthContextError,
17
+ CodexMainProfileDrainingError,
16
18
  CodexPoolAuthenticationError,
17
19
  CodexThreadAffinityExpiredError,
18
20
  } from "../codex/auth-context";
21
+ import { codexAccountNamespaceForModel } from "../codex/account-namespace-match";
19
22
  import { formatCodexProviderForLog } from "../codex/routing";
20
23
  import { signalWithTimeout } from "../lib/abort";
24
+ import { readBoundedResponseBytes } from "../lib/bounded-body";
21
25
  import { sidecarEnter } from "../lib/sidecar-tracker";
22
26
  import type { OcxConfig } from "../types";
23
- import { listOpenAiForwardSidecarCandidates, resolveFirstUsableOpenAiSidecar } from "../providers/openai-sidecar";
27
+ import {
28
+ listOpenAiForwardSidecarCandidates,
29
+ resolveFirstUsableOpenAiSidecar,
30
+ type ExactOpenAiSidecarAccount,
31
+ } from "../providers/openai-sidecar";
32
+ import { routeModel } from "../router";
24
33
  import { readJsonRequestBody } from "./request-decompress";
25
34
  import { ForwardAdmissionCredentialError, validateForwardAdmissionCredential } from "./auth-cors";
26
35
  import type { RequestLogContext } from "./request-log";
27
36
  import { codexLogAccountId, decodeRequestErrorResponse } from "./responses";
37
+ import type { AdmissionLease } from "../lib/admission";
38
+ import { codexAccountSelectionForTurn } from "./lifecycle";
28
39
 
29
40
  /**
30
41
  * Default TOTAL deadline for one search relay. alpha/search is non-streaming JSON — response
@@ -34,12 +45,13 @@ import { codexLogAccountId, decodeRequestErrorResponse } from "./responses";
34
45
  * long-running search).
35
46
  */
36
47
  const SEARCH_UPSTREAM_TIMEOUT_MS = 200_000;
37
- const SEARCH_RESPONSE_MAX_BYTES = 16 * 1024 * 1024;
48
+ export const SEARCH_RESPONSE_MAX_BYTES = 16 * 1024 * 1024;
38
49
 
39
50
  export async function handleSearch(
40
51
  req: Request,
41
52
  config: OcxConfig,
42
53
  logCtx: RequestLogContext,
54
+ turnAdmissionLease?: AdmissionLease,
43
55
  ): Promise<Response> {
44
56
  try { validateForwardAdmissionCredential(req.headers, config); }
45
57
  catch (err) {
@@ -55,6 +67,32 @@ export async function handleSearch(
55
67
  const model = (body as { model?: unknown } | null)?.model;
56
68
  if (typeof model === "string" && model) logCtx.model = model;
57
69
 
70
+ let exactAccount: ExactOpenAiSidecarAccount | undefined;
71
+ let relayBody = body;
72
+ const accountNamespace = typeof model === "string"
73
+ ? codexAccountNamespaceForModel(config.codexAccountNamespaces, model)
74
+ : undefined;
75
+ if (accountNamespace && typeof model === "string") {
76
+ try {
77
+ const route = routeModel(config, model);
78
+ if (!route.codexAccountId || route.codexAccountNamespace !== accountNamespace) {
79
+ return formatErrorResponse(400, "invalid_request_error", "Invalid Codex account-qualified search model");
80
+ }
81
+ exactAccount = { accountId: route.codexAccountId, modelId: route.modelId };
82
+ logCtx.provider = `${route.providerName}-${accountNamespace}`;
83
+ logCtx.routeDecision = route.routeDecision;
84
+ // The ChatGPT search endpoint only understands the native model slug. The
85
+ // account namespace is proxy routing syntax and must not cross the wire.
86
+ relayBody = { ...(body as Record<string, unknown>), model: route.modelId };
87
+ } catch (err) {
88
+ return formatErrorResponse(
89
+ 400,
90
+ "invalid_request_error",
91
+ err instanceof Error ? err.message : "Invalid Codex account-qualified search model",
92
+ );
93
+ }
94
+ }
95
+
58
96
  const candidates = listOpenAiForwardSidecarCandidates(config);
59
97
  if (candidates.length === 0) {
60
98
  return formatErrorResponse(
@@ -67,7 +105,10 @@ export async function handleSearch(
67
105
 
68
106
  let upstream: Awaited<ReturnType<typeof resolveFirstUsableOpenAiSidecar>>;
69
107
  try {
70
- upstream = await resolveFirstUsableOpenAiSidecar(candidates, req.headers, config);
108
+ upstream = await resolveFirstUsableOpenAiSidecar(candidates, req.headers, config, {
109
+ exactAccount,
110
+ beginCodexAccountSelection: codexAccountSelectionForTurn(turnAdmissionLease),
111
+ });
71
112
  if (!upstream) {
72
113
  return formatErrorResponse(
73
114
  401,
@@ -75,16 +116,21 @@ export async function handleSearch(
75
116
  "web search relay needs ChatGPT auth (Authorization header)",
76
117
  );
77
118
  }
78
- logCtx.provider = formatCodexProviderForLog(upstream.providerName, codexLogAccountId(upstream.authContext), config);
119
+ logCtx.provider = accountNamespace
120
+ ? `${upstream.providerName}-${accountNamespace}`
121
+ : formatCodexProviderForLog(upstream.providerName, codexLogAccountId(upstream.authContext), config);
79
122
  } catch (err) {
80
123
  if (err instanceof CodexAccountCooldownError) {
81
- return cooldownErrorResponse(err);
124
+ return cooldownErrorResponse(err, Date.now(), accountNamespace);
82
125
  }
126
+ if (err instanceof CodexMainProfileDrainingError) return codexMainProfileDrainingResponse();
83
127
  if (err instanceof CodexThreadAffinityExpiredError) {
84
128
  return formatErrorResponse(409, "invalid_request_error", "Codex thread account affinity expired; start a new session");
85
129
  }
86
130
  if (err instanceof CodexAuthContextError) {
87
- const safeAccountLabel = formatCodexProviderForLog("openai", err.accountId, config);
131
+ const safeAccountLabel = accountNamespace
132
+ ? `openai-${accountNamespace}`
133
+ : formatCodexProviderForLog("openai", err.accountId, config);
88
134
  console.error(`[search] Pool account ${safeAccountLabel} token failed; reauthentication required`);
89
135
  return formatErrorResponse(401, "authentication_error", "Selected Codex account needs reauthentication");
90
136
  }
@@ -99,27 +145,40 @@ export async function handleSearch(
99
145
  const timeoutMs = config.search?.timeoutMs ?? SEARCH_UPSTREAM_TIMEOUT_MS;
100
146
  const linkedSignal = signalWithTimeout(timeoutMs, req.signal);
101
147
  const sidecarExit = sidecarEnter("search");
148
+ let upstreamResponse: Response | undefined;
102
149
  try {
103
- const upstreamResponse = await fetch(url, {
150
+ upstreamResponse = await fetch(url, {
104
151
  method: "POST",
105
152
  headers,
106
- body: JSON.stringify(body),
153
+ body: JSON.stringify(relayBody),
154
+ signal: linkedSignal.signal,
155
+ // Credential-bearing: do not follow a cross-origin 3xx. Bun strips `Authorization`
156
+ // across origins but forwards nonstandard headers such as `chatgpt-account-id`,
157
+ // `session_id`, and `x-codex-turn-metadata` to the redirect target.
158
+ redirect: "manual",
159
+ });
160
+ const observed = await readBoundedResponseBytes(upstreamResponse, {
161
+ maxBytes: SEARCH_RESPONSE_MAX_BYTES,
107
162
  signal: linkedSignal.signal,
108
163
  });
109
- const payload = await upstreamResponse.arrayBuffer();
110
- if (payload.byteLength > SEARCH_RESPONSE_MAX_BYTES) {
111
- return formatErrorResponse(502, "upstream_error", `search response too large (${payload.byteLength} bytes)`);
164
+ if (observed.oversized) {
165
+ upstream.recordOutcome?.(upstreamResponse.status);
166
+ return formatErrorResponse(
167
+ 502,
168
+ "upstream_error",
169
+ `search response too large (exceeded ${SEARCH_RESPONSE_MAX_BYTES} bytes)`,
170
+ );
112
171
  }
113
172
  upstream.recordOutcome?.(upstreamResponse.status);
114
173
  const relayHeaders: Record<string, string> = {};
115
174
  const contentType = upstreamResponse.headers.get("content-type");
116
175
  if (contentType) relayHeaders["content-type"] = contentType;
117
- return new Response(payload, { status: upstreamResponse.status, headers: relayHeaders });
176
+ return new Response(observed.bytes, { status: upstreamResponse.status, headers: relayHeaders });
118
177
  } catch (err) {
119
178
  if (req.signal.aborted) {
120
179
  return formatErrorResponse(499, "client_closed_request", "search request canceled by client");
121
180
  }
122
- if (err instanceof Error && err.name === "TimeoutError") {
181
+ if (linkedSignal.signal.aborted || (err instanceof Error && err.name === "TimeoutError")) {
123
182
  upstream.recordOutcome?.("timeout");
124
183
  return formatErrorResponse(504, "upstream_error", "search upstream timed out");
125
184
  }
@@ -132,5 +191,11 @@ export async function handleSearch(
132
191
  } finally {
133
192
  sidecarExit();
134
193
  linkedSignal.cleanup();
194
+ // A response that aborted before the reader attached still owns its upstream socket.
195
+ // Consumed/cancelled bodies are already closed; locked bodies remain owned by the reader.
196
+ const pendingBody = upstreamResponse?.body;
197
+ if (pendingBody && !pendingBody.locked) {
198
+ try { void pendingBody.cancel().catch(() => undefined); } catch { /* already closed */ }
199
+ }
135
200
  }
136
201
  }