@iislee/opencodex 2.11.0 → 2.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (675) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +114 -19
  3. package/bin/ocx.mjs +164 -36
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-BF38heuV.js +104 -0
  6. package/gui/dist/assets/index-DMiI18Kv.css +1 -0
  7. package/gui/dist/index.html +2 -2
  8. package/gui/dist/provider-icons/alibaba-color.svg +1 -1
  9. package/gui/dist/provider-icons/antigravity-color.svg +1 -1
  10. package/gui/dist/provider-icons/claude-color.svg +1 -1
  11. package/gui/dist/provider-icons/cline-color.svg +16 -0
  12. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
  13. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  14. package/gui/dist/provider-icons/copilot-color.svg +1 -1
  15. package/gui/dist/provider-icons/cursor-color.svg +1 -1
  16. package/gui/dist/provider-icons/deepseek-color.svg +1 -1
  17. package/gui/dist/provider-icons/firepass-color.svg +1 -1
  18. package/gui/dist/provider-icons/fireworks-color.svg +1 -1
  19. package/gui/dist/provider-icons/gemini-color.svg +1 -1
  20. package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
  21. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
  22. package/gui/dist/provider-icons/grok.svg +1 -1
  23. package/gui/dist/provider-icons/groq-color.svg +1 -1
  24. package/gui/dist/provider-icons/huggingface-color.svg +1 -1
  25. package/gui/dist/provider-icons/kimi-color.svg +1 -1
  26. package/gui/dist/provider-icons/kiro-color.svg +2 -2
  27. package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
  28. package/gui/dist/provider-icons/mistral-color.svg +1 -1
  29. package/gui/dist/provider-icons/moonshot-color.svg +1 -1
  30. package/gui/dist/provider-icons/nvidia-color.svg +1 -1
  31. package/gui/dist/provider-icons/ollama-color.svg +1 -1
  32. package/gui/dist/provider-icons/openai.svg +1 -1
  33. package/gui/dist/provider-icons/opencode.svg +2 -1
  34. package/gui/dist/provider-icons/openrouter-color.svg +1 -1
  35. package/gui/dist/provider-icons/pi.svg +2 -2
  36. package/gui/dist/provider-icons/qianfan-color.svg +1 -1
  37. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
  38. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
  39. package/gui/dist/provider-icons/vllm-color.svg +1 -1
  40. package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
  41. package/package.json +19 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +376 -52
  44. package/src/adapters/base.ts +54 -7
  45. package/src/adapters/client-fingerprint.ts +18 -12
  46. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  47. package/src/adapters/command-code.ts +601 -0
  48. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  49. package/src/adapters/cursor/cursor-errors.ts +129 -6
  50. package/src/adapters/cursor/discovery.ts +61 -4
  51. package/src/adapters/cursor/effort-map.ts +27 -3
  52. package/src/adapters/cursor/framing.ts +39 -0
  53. package/src/adapters/cursor/h2-pool.ts +123 -0
  54. package/src/adapters/cursor/http1-bidi.ts +361 -0
  55. package/src/adapters/cursor/images.ts +704 -0
  56. package/src/adapters/cursor/live-models.ts +174 -58
  57. package/src/adapters/cursor/live-transport.ts +609 -170
  58. package/src/adapters/cursor/native-exec-common.ts +23 -2
  59. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  60. package/src/adapters/cursor/native-exec-network.ts +1 -1
  61. package/src/adapters/cursor/native-exec-shell.ts +0 -1
  62. package/src/adapters/cursor/native-exec.ts +101 -14
  63. package/src/adapters/cursor/protobuf-events.ts +829 -11
  64. package/src/adapters/cursor/protobuf-request.ts +383 -65
  65. package/src/adapters/cursor/request-builder.ts +237 -31
  66. package/src/adapters/cursor/tool-definitions.ts +242 -12
  67. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  68. package/src/adapters/cursor/transport.ts +22 -0
  69. package/src/adapters/cursor/types.ts +28 -1
  70. package/src/adapters/cursor.ts +133 -7
  71. package/src/adapters/google-antigravity-replay.ts +558 -34
  72. package/src/adapters/google-antigravity-wire.ts +43 -10
  73. package/src/adapters/google-http.ts +38 -10
  74. package/src/adapters/google-tool-schema.ts +80 -15
  75. package/src/adapters/google-truncation.ts +11 -0
  76. package/src/adapters/google.ts +618 -74
  77. package/src/adapters/identity.ts +39 -6
  78. package/src/adapters/kiro-errors.ts +11 -0
  79. package/src/adapters/kiro-events.ts +19 -1
  80. package/src/adapters/kiro-thinking.ts +18 -2
  81. package/src/adapters/kiro-tools.ts +10 -1
  82. package/src/adapters/kiro.ts +92 -54
  83. package/src/adapters/mimo-free.ts +17 -0
  84. package/src/adapters/openai-chat-url.ts +11 -0
  85. package/src/adapters/openai-chat.ts +1311 -336
  86. package/src/adapters/openai-responses-url.ts +16 -0
  87. package/src/adapters/openai-responses.ts +830 -56
  88. package/src/adapters/registry.ts +175 -0
  89. package/src/adapters/responses-tool-schema.ts +67 -0
  90. package/src/adapters/tool-call-id.ts +119 -0
  91. package/src/adapters/tool-catalog-nudge.ts +104 -21
  92. package/src/adapters/xai-web-search.ts +185 -0
  93. package/src/bridge.ts +428 -61
  94. package/src/chat/inbound.ts +43 -19
  95. package/src/chat/outbound.ts +82 -26
  96. package/src/claude/agents-inject.ts +32 -9
  97. package/src/claude/context-windows.ts +21 -5
  98. package/src/claude/desktop-3p.ts +243 -9
  99. package/src/claude/gateway-cache.ts +41 -4
  100. package/src/claude/inbound.ts +72 -3
  101. package/src/claude/model-info.ts +38 -15
  102. package/src/claude/outbound.ts +70 -16
  103. package/src/cli/account-api.ts +35 -1
  104. package/src/cli/account-auth.ts +33 -6
  105. package/src/cli/account-catalog-refresh.ts +14 -0
  106. package/src/cli/account-extended.ts +389 -2
  107. package/src/cli/account-main.ts +317 -0
  108. package/src/cli/account.ts +30 -6
  109. package/src/cli/agent.ts +45 -1
  110. package/src/cli/claude-agent-startup-sync.ts +73 -0
  111. package/src/cli/claude-desktop.ts +29 -4
  112. package/src/cli/claude.ts +103 -34
  113. package/src/cli/codex-log-guard-doctor.ts +103 -0
  114. package/src/cli/codex-shim-autorestore.ts +2 -0
  115. package/src/cli/codex-shim-readiness.ts +76 -0
  116. package/src/cli/combo.ts +8 -0
  117. package/src/cli/config-command.ts +74 -10
  118. package/src/cli/dispatch.ts +593 -0
  119. package/src/cli/doctor.ts +315 -43
  120. package/src/cli/ensure-desired-integrations.ts +152 -0
  121. package/src/cli/export-command.ts +46 -20
  122. package/src/cli/help.ts +20 -271
  123. package/src/cli/index.ts +378 -519
  124. package/src/cli/init.ts +4 -17
  125. package/src/cli/integrations.ts +120 -2
  126. package/src/cli/lab.ts +607 -0
  127. package/src/cli/launcher-context.ts +77 -0
  128. package/src/cli/minimax.ts +497 -0
  129. package/src/cli/models-runtime.ts +35 -2
  130. package/src/cli/models.ts +100 -14
  131. package/src/cli/observe.ts +92 -3
  132. package/src/cli/opencode.ts +4 -2
  133. package/src/cli/provider-runtime.ts +18 -1
  134. package/src/cli/provider.ts +24 -3
  135. package/src/cli/ready.ts +301 -0
  136. package/src/cli/registry.ts +437 -0
  137. package/src/cli/root.ts +86 -0
  138. package/src/cli/route-policy.ts +92 -0
  139. package/src/cli/runtime-api.ts +6 -3
  140. package/src/cli/star-prompt.ts +71 -15
  141. package/src/cli/status.ts +10 -3
  142. package/src/cli/system-restart-client.ts +146 -0
  143. package/src/cli/tray-proxy.ts +153 -6
  144. package/src/cli/v2.ts +105 -10
  145. package/src/cli.ts +1 -1
  146. package/src/clients/config-export.ts +1358 -21
  147. package/src/codex/account-label.ts +14 -1
  148. package/src/codex/account-lifecycle.ts +130 -13
  149. package/src/codex/account-namespaces.ts +49 -3
  150. package/src/codex/account-priority.ts +83 -0
  151. package/src/codex/account-store.ts +29 -2
  152. package/src/codex/account-usability.ts +25 -2
  153. package/src/codex/admission.ts +256 -0
  154. package/src/codex/affinity-debug.ts +162 -0
  155. package/src/codex/app-server-processes.ts +493 -106
  156. package/src/codex/app-server-restart-service.ts +232 -0
  157. package/src/codex/auth-api.ts +849 -242
  158. package/src/codex/auth-collision.ts +5 -3
  159. package/src/codex/auth-context.ts +345 -32
  160. package/src/codex/autostart-health.ts +8 -1
  161. package/src/codex/catalog/account-models.ts +67 -0
  162. package/src/codex/catalog/aggregation.ts +68 -10
  163. package/src/codex/catalog/bundled.ts +331 -33
  164. package/src/codex/catalog/effort.ts +121 -30
  165. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  166. package/src/codex/catalog/kinds.ts +2 -0
  167. package/src/codex/catalog/metadata.ts +529 -45
  168. package/src/codex/catalog/native-models.ts +72 -0
  169. package/src/codex/catalog/parsing.ts +224 -30
  170. package/src/codex/catalog/provider-fetch.ts +1460 -134
  171. package/src/codex/catalog/sync.ts +1449 -186
  172. package/src/codex/catalog-admission.ts +199 -0
  173. package/src/codex/catalog-refresh-status.ts +105 -0
  174. package/src/codex/catalog-write-serialization.ts +242 -0
  175. package/src/codex/catalog.ts +6 -3
  176. package/src/codex/codex-write-lock.ts +384 -0
  177. package/src/codex/convergence-types.ts +614 -0
  178. package/src/codex/convergence.ts +651 -0
  179. package/src/codex/coordinator-doctor.ts +332 -0
  180. package/src/codex/custom-model-catalog-migration.ts +176 -0
  181. package/src/codex/desired-state.ts +230 -0
  182. package/src/codex/features.ts +636 -39
  183. package/src/codex/generation.ts +202 -0
  184. package/src/codex/history-job.ts +407 -0
  185. package/src/codex/history-lock.ts +242 -0
  186. package/src/codex/history-migration-guardian.ts +26 -20
  187. package/src/codex/history-provider.ts +231 -28
  188. package/src/codex/history-transition.ts +105 -0
  189. package/src/codex/history-worker.ts +220 -0
  190. package/src/codex/inject-coordination.ts +290 -0
  191. package/src/codex/inject.ts +1073 -152
  192. package/src/codex/injected-marker.ts +37 -3
  193. package/src/codex/integration-record.ts +266 -0
  194. package/src/codex/internal/catalog-writer.ts +203 -0
  195. package/src/codex/internal/history-writer.ts +80 -0
  196. package/src/codex/journal.ts +66 -4
  197. package/src/codex/log-guard/inspect.ts +506 -0
  198. package/src/codex/log-guard/lock.ts +150 -0
  199. package/src/codex/log-guard/maintenance.ts +403 -0
  200. package/src/codex/log-guard/path-safety.ts +88 -0
  201. package/src/codex/log-guard/policy.ts +44 -0
  202. package/src/codex/log-guard/processes.ts +205 -0
  203. package/src/codex/log-guard/protection.ts +489 -0
  204. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  205. package/src/codex/main-account-cache.ts +24 -0
  206. package/src/codex/main-account.ts +29 -1
  207. package/src/codex/management-convergence.ts +167 -0
  208. package/src/codex/model-cache.ts +56 -10
  209. package/src/codex/model-entitlements.ts +353 -0
  210. package/src/codex/native-main-admission.ts +47 -0
  211. package/src/codex/native-main-auth-temp.ts +187 -0
  212. package/src/codex/native-main-claim.ts +178 -0
  213. package/src/codex/native-main-lock-file.ts +162 -0
  214. package/src/codex/native-main-owner.ts +329 -0
  215. package/src/codex/native-profile-api.ts +247 -0
  216. package/src/codex/native-profile-manager.ts +1531 -0
  217. package/src/codex/native-profile-processes.ts +121 -0
  218. package/src/codex/native-profile-recovery.ts +99 -0
  219. package/src/codex/native-profile-stage-store.ts +387 -0
  220. package/src/codex/native-profile-startup.ts +492 -0
  221. package/src/codex/native-profile-store.ts +855 -0
  222. package/src/codex/native-profile-types.ts +120 -0
  223. package/src/codex/native-residue.ts +682 -0
  224. package/src/codex/paths.ts +80 -1
  225. package/src/codex/plan-from-token.ts +140 -0
  226. package/src/codex/plan.ts +40 -0
  227. package/src/codex/plugins-doctor.ts +1 -1
  228. package/src/codex/pool-rotation.ts +74 -4
  229. package/src/codex/project-config-warnings.ts +20 -6
  230. package/src/codex/prompt-journal.ts +352 -0
  231. package/src/codex/prompt-layers.ts +967 -0
  232. package/src/codex/prompt-lock.ts +143 -0
  233. package/src/codex/quota-rejection.ts +298 -0
  234. package/src/codex/quota.ts +175 -13
  235. package/src/codex/refresh.ts +11 -2
  236. package/src/codex/reset-credit-recovery.ts +1044 -0
  237. package/src/codex/routing.ts +505 -94
  238. package/src/codex/runtime.ts +159 -38
  239. package/src/codex/shim.ts +1009 -28
  240. package/src/codex/subagent-model-fallback.ts +350 -35
  241. package/src/codex/sync.ts +191 -2
  242. package/src/codex/transition-state.ts +612 -0
  243. package/src/codex/upstream-host-health.ts +368 -0
  244. package/src/codex/user-identity.ts +557 -0
  245. package/src/codex/warmup.ts +187 -81
  246. package/src/codex/write-coordination.ts +114 -0
  247. package/src/combos/failover.ts +20 -0
  248. package/src/combos/index.ts +4 -0
  249. package/src/combos/request.ts +32 -0
  250. package/src/combos/types.ts +81 -9
  251. package/src/config/provider-name.ts +24 -0
  252. package/src/config.ts +1762 -140
  253. package/src/generated/compatibility-version.json +3164 -0
  254. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  255. package/src/grok/inject.ts +16 -5
  256. package/src/grok/inspect.ts +45 -0
  257. package/src/grok/sync.ts +2 -2
  258. package/src/images/loop.ts +152 -29
  259. package/src/images/plan.ts +23 -13
  260. package/src/integrations/config-io.ts +269 -0
  261. package/src/integrations/journal.ts +315 -0
  262. package/src/integrations/merge.ts +135 -0
  263. package/src/integrations/mutation-flight.ts +71 -0
  264. package/src/integrations/native/ownership-preflight.ts +202 -0
  265. package/src/integrations/omp-yaml-source.ts +358 -0
  266. package/src/integrations/owned-refresh.ts +74 -0
  267. package/src/integrations/ownership.ts +111 -0
  268. package/src/integrations/registry.ts +159 -0
  269. package/src/integrations/serialize.ts +314 -0
  270. package/src/integrations/state.ts +361 -0
  271. package/src/integrations/store.ts +103 -0
  272. package/src/integrations/writer-lock.ts +98 -0
  273. package/src/integrations/writer.ts +691 -0
  274. package/src/lab/artifacts/sanitize.ts +586 -0
  275. package/src/lab/artifacts/secure-fs.ts +475 -0
  276. package/src/lab/artifacts/store.ts +310 -0
  277. package/src/lab/automation/budgets.ts +78 -0
  278. package/src/lab/automation/config-persistence.ts +256 -0
  279. package/src/lab/automation/constants.ts +39 -0
  280. package/src/lab/automation/cooldown.ts +103 -0
  281. package/src/lab/automation/dispatch.ts +211 -0
  282. package/src/lab/automation/index.ts +13 -0
  283. package/src/lab/automation/orchestrator.ts +499 -0
  284. package/src/lab/automation/persistence.ts +512 -0
  285. package/src/lab/automation/planner.ts +371 -0
  286. package/src/lab/automation/policy.ts +136 -0
  287. package/src/lab/automation/queue.ts +191 -0
  288. package/src/lab/automation/recovery.ts +24 -0
  289. package/src/lab/automation/route-context.ts +21 -0
  290. package/src/lab/automation/run-key.ts +44 -0
  291. package/src/lab/automation/runs-query.ts +34 -0
  292. package/src/lab/automation/types.ts +160 -0
  293. package/src/lab/conformance/assertion.ts +325 -0
  294. package/src/lab/conformance/digest.ts +22 -0
  295. package/src/lab/conformance/executor.ts +741 -0
  296. package/src/lab/conformance/fixture-provider.ts +27 -0
  297. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  298. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  299. package/src/lab/conformance/harness-budget.ts +47 -0
  300. package/src/lab/conformance/index.ts +5 -0
  301. package/src/lab/conformance/jcs.ts +64 -0
  302. package/src/lab/conformance/json-pointer.ts +39 -0
  303. package/src/lab/conformance/manifest.ts +180 -0
  304. package/src/lab/conformance/mcp-stub.ts +179 -0
  305. package/src/lab/conformance/negative-controls.ts +164 -0
  306. package/src/lab/conformance/observation.ts +355 -0
  307. package/src/lab/conformance/runner.ts +68 -0
  308. package/src/lab/conformance/sse-normalize.ts +59 -0
  309. package/src/lab/conformance/suite-manifest.ts +78 -0
  310. package/src/lab/conformance/types.ts +214 -0
  311. package/src/lab/constants.ts +126 -0
  312. package/src/lab/digest.ts +64 -0
  313. package/src/lab/events/errors.ts +9 -0
  314. package/src/lab/events/limits.ts +117 -0
  315. package/src/lab/events/types.ts +229 -0
  316. package/src/lab/events/validate.ts +781 -0
  317. package/src/lab/fabric/constants.ts +40 -0
  318. package/src/lab/fabric/executor.ts +492 -0
  319. package/src/lab/fabric/index.ts +80 -0
  320. package/src/lab/fabric/manifest.ts +222 -0
  321. package/src/lab/fabric/observe.ts +489 -0
  322. package/src/lab/fabric/patch.ts +79 -0
  323. package/src/lab/fabric/producer-child.ts +139 -0
  324. package/src/lab/fabric/producer-isolate.ts +276 -0
  325. package/src/lab/fabric/producer-protocol.ts +61 -0
  326. package/src/lab/fabric/scratch.ts +439 -0
  327. package/src/lab/fabric/subject.ts +106 -0
  328. package/src/lab/fabric/types.ts +134 -0
  329. package/src/lab/fabric/verifier.ts +98 -0
  330. package/src/lab/index.ts +54 -0
  331. package/src/lab/ledger/artifact-refs.ts +127 -0
  332. package/src/lab/ledger/invalidation.ts +136 -0
  333. package/src/lab/ledger/purge.ts +310 -0
  334. package/src/lab/ledger/store.ts +532 -0
  335. package/src/lab/live/credential-lease.ts +53 -0
  336. package/src/lab/live/destination.ts +155 -0
  337. package/src/lab/live/executor.ts +336 -0
  338. package/src/lab/live/inert-tools.ts +56 -0
  339. package/src/lab/live/manifest.ts +85 -0
  340. package/src/lab/live/mcp-loopback.ts +57 -0
  341. package/src/lab/live/runner.ts +19 -0
  342. package/src/lab/live/sandbox.ts +61 -0
  343. package/src/lab/live/suite-manifest.ts +41 -0
  344. package/src/lab/live/transport.ts +118 -0
  345. package/src/lab/live/types.ts +197 -0
  346. package/src/lab/observe/from-conformance.ts +301 -0
  347. package/src/lab/observe/from-live.ts +117 -0
  348. package/src/lab/paths.ts +153 -0
  349. package/src/lab/projection/rebuild.ts +495 -0
  350. package/src/lab/projection/schema.ts +135 -0
  351. package/src/lab/projection/verdicts.ts +474 -0
  352. package/src/lab/projection/verification.ts +412 -0
  353. package/src/lab/public/bundle.ts +217 -0
  354. package/src/lab/public/community-authority.ts +175 -0
  355. package/src/lab/public/community-files.ts +29 -0
  356. package/src/lab/public/community.ts +479 -0
  357. package/src/lab/public/file-safety.ts +155 -0
  358. package/src/lab/public/ids.ts +26 -0
  359. package/src/lab/public/index.ts +16 -0
  360. package/src/lab/public/mutation-lock.ts +424 -0
  361. package/src/lab/public/operator.ts +353 -0
  362. package/src/lab/public/origin-purge.ts +79 -0
  363. package/src/lab/public/origin.ts +203 -0
  364. package/src/lab/public/privacy.ts +143 -0
  365. package/src/lab/public/private-file.ts +261 -0
  366. package/src/lab/public/project.ts +124 -0
  367. package/src/lab/public/purge-test-fault.ts +21 -0
  368. package/src/lab/public/purge.ts +223 -0
  369. package/src/lab/public/registry.ts +44 -0
  370. package/src/lab/public/revocation.ts +252 -0
  371. package/src/lab/public/signature.ts +243 -0
  372. package/src/lab/public/storage.ts +105 -0
  373. package/src/lab/public/strict-json.ts +206 -0
  374. package/src/lab/public/time.ts +26 -0
  375. package/src/lab/public/types.ts +172 -0
  376. package/src/lab/public/validate.ts +391 -0
  377. package/src/lab/query/catalog.ts +101 -0
  378. package/src/lab/query/connection.ts +107 -0
  379. package/src/lab/query/constants.ts +4 -0
  380. package/src/lab/query/cursor.ts +132 -0
  381. package/src/lab/query/dto-map.ts +277 -0
  382. package/src/lab/query/errors.ts +22 -0
  383. package/src/lab/query/freshness.ts +53 -0
  384. package/src/lab/query/index.ts +45 -0
  385. package/src/lab/query/latest-observation.ts +59 -0
  386. package/src/lab/query/passive-production.ts +159 -0
  387. package/src/lab/query/queries.ts +444 -0
  388. package/src/lab/query/types.ts +266 -0
  389. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  390. package/src/lab/subject/installation-salt.ts +112 -0
  391. package/src/lab/subject/protocol-subject.ts +80 -0
  392. package/src/lab/subject/route-subject.ts +74 -0
  393. package/src/lib/app-owned-memory-stores.ts +22 -0
  394. package/src/lib/bounded-body.ts +153 -9
  395. package/src/lib/bun-runtime.ts +125 -12
  396. package/src/lib/bun-stream-caps.ts +13 -9
  397. package/src/lib/codex-restart-contract.ts +120 -0
  398. package/src/lib/config-ownership.ts +6 -2
  399. package/src/lib/destination-policy.ts +65 -1
  400. package/src/lib/errors.ts +44 -2
  401. package/src/lib/fabric-task-execution-authority.ts +7 -0
  402. package/src/lib/fabric-task-host.ts +29 -0
  403. package/src/lib/lab-activation.ts +223 -0
  404. package/src/lib/lab-live-execution-authority.ts +13 -0
  405. package/src/lib/lab-live-host.ts +30 -0
  406. package/src/lib/lab-live-pinned-sender.ts +56 -0
  407. package/src/lib/lab-live-route-production.ts +130 -0
  408. package/src/lib/lab-passive-linker-registration.ts +26 -0
  409. package/src/lib/local-management-attestation.ts +51 -0
  410. package/src/lib/local-management-capability.ts +100 -0
  411. package/src/lib/local-provider-reload-contract.ts +100 -0
  412. package/src/lib/optional-shutdown-hooks.ts +57 -0
  413. package/src/lib/pinned-http.ts +145 -26
  414. package/src/lib/process-control.ts +4 -1
  415. package/src/lib/provider-outbound.ts +49 -9
  416. package/src/lib/redact.ts +419 -3
  417. package/src/lib/self-launch-argv.ts +15 -0
  418. package/src/lib/server-resource-ownership.ts +71 -0
  419. package/src/lib/shadow-call.ts +35 -4
  420. package/src/lib/sse-decoder.ts +41 -0
  421. package/src/lib/state-store-registrations.ts +10 -2
  422. package/src/lib/system-restart-contract.ts +73 -0
  423. package/src/lib/token-estimate.ts +19 -2
  424. package/src/lib/tool-argument-integers.ts +202 -0
  425. package/src/lib/translator-budget.ts +44 -0
  426. package/src/lib/upstream-http-version.ts +57 -0
  427. package/src/lib/upstream-reachability.ts +95 -0
  428. package/src/lib/upstream-retry.ts +156 -3
  429. package/src/lib/windows-atomic-replace.ts +155 -0
  430. package/src/lib/windows-elevation.ts +70 -2
  431. package/src/lib/windows-secret-acl.ts +409 -69
  432. package/src/lib/windows-service-wrappers.ts +72 -0
  433. package/src/lib/windows-text.ts +106 -0
  434. package/src/lib/windows-user-principal.ts +341 -0
  435. package/src/lib/winsw.ts +33 -5
  436. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  437. package/src/oauth/account-import/index.ts +15 -0
  438. package/src/oauth/account-import/parser.ts +83 -0
  439. package/src/oauth/account-import/registry.ts +18 -0
  440. package/src/oauth/account-import/service.ts +75 -0
  441. package/src/oauth/account-import/types.ts +91 -0
  442. package/src/oauth/anthropic.ts +12 -1
  443. package/src/oauth/callback-server.ts +8 -2
  444. package/src/oauth/chatgpt.ts +12 -1
  445. package/src/oauth/command-code.ts +239 -0
  446. package/src/oauth/cursor.ts +46 -5
  447. package/src/oauth/google-antigravity.ts +35 -3
  448. package/src/oauth/health.ts +20 -12
  449. package/src/oauth/index.ts +398 -66
  450. package/src/oauth/key-providers.ts +16 -0
  451. package/src/oauth/kimi.ts +16 -2
  452. package/src/oauth/kiro.ts +50 -6
  453. package/src/oauth/local-token-detect.ts +11 -2
  454. package/src/oauth/log.ts +3 -1
  455. package/src/oauth/login-cli.ts +88 -28
  456. package/src/oauth/nous.ts +798 -0
  457. package/src/oauth/store.ts +119 -21
  458. package/src/oauth/token-guardian.ts +9 -3
  459. package/src/pi/models.ts +2 -2
  460. package/src/providers/alibaba-region-migration.ts +1 -1
  461. package/src/providers/antigravity-models.ts +521 -31
  462. package/src/providers/base-url-choices.ts +10 -0
  463. package/src/providers/codex-capacity.ts +292 -0
  464. package/src/providers/command-code-efforts.ts +144 -0
  465. package/src/providers/context-cap.ts +22 -5
  466. package/src/providers/cursor-pool.ts +72 -0
  467. package/src/providers/derive.ts +253 -6
  468. package/src/providers/fastwire.ts +501 -0
  469. package/src/providers/free-directory.ts +10 -7
  470. package/src/providers/google-vertex-location.ts +14 -0
  471. package/src/providers/key-failover.ts +71 -3
  472. package/src/providers/label.ts +1 -1
  473. package/src/providers/model-discovery-limits.ts +16 -0
  474. package/src/providers/model-discovery.ts +115 -22
  475. package/src/providers/model-rename-migration.ts +255 -0
  476. package/src/providers/model-rename-startup.ts +28 -0
  477. package/src/providers/openai-sidecar.ts +72 -4
  478. package/src/providers/openai-tier-startup.ts +31 -2
  479. package/src/providers/openai-tiers.ts +119 -4
  480. package/src/providers/openai-virtual-models.ts +1 -0
  481. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  482. package/src/providers/provider-id-rewrite.ts +29 -0
  483. package/src/providers/quota.ts +1319 -38
  484. package/src/providers/registry.ts +1429 -111
  485. package/src/providers/request-pacing.ts +310 -0
  486. package/src/providers/service-tier.ts +277 -0
  487. package/src/providers/slug-codec.ts +42 -6
  488. package/src/providers/static-model-discovery.ts +86 -0
  489. package/src/providers/xai-responses-opt-in.ts +15 -0
  490. package/src/providers/xai-transport.ts +11 -4
  491. package/src/reasoning-effort.ts +49 -1
  492. package/src/responses/compaction.ts +26 -1
  493. package/src/responses/custom-tool-compat.ts +266 -0
  494. package/src/responses/hosted-tool-policy.ts +9 -0
  495. package/src/responses/namespace-tool-compat.ts +355 -0
  496. package/src/responses/parser.ts +220 -38
  497. package/src/responses/provider-continuation.ts +98 -0
  498. package/src/responses/provider-opaque-metadata.ts +73 -0
  499. package/src/responses/reasoning-envelope.ts +9 -1
  500. package/src/responses/reasoning-replay-cache.ts +426 -0
  501. package/src/responses/schema.ts +7 -1
  502. package/src/responses/spill-store.ts +75 -10
  503. package/src/responses/state.ts +565 -27
  504. package/src/responses/thought-signature-replay.ts +347 -0
  505. package/src/responses/tool-search-compat.ts +301 -0
  506. package/src/responses/truncated-stop-reason.ts +60 -0
  507. package/src/router.ts +366 -30
  508. package/src/routing/analytics.ts +378 -0
  509. package/src/routing/capability.ts +244 -0
  510. package/src/routing/compatibility/assemble.ts +73 -0
  511. package/src/routing/compatibility/behavior.ts +278 -0
  512. package/src/routing/compatibility/catalog.ts +99 -0
  513. package/src/routing/compatibility/endpoint.ts +52 -0
  514. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  515. package/src/routing/compatibility/policy.ts +181 -0
  516. package/src/routing/compatibility/provider-slot.ts +56 -0
  517. package/src/routing/compatibility/reader.ts +110 -0
  518. package/src/routing/compatibility/subject.ts +191 -0
  519. package/src/routing/compatibility/types.ts +64 -0
  520. package/src/routing/compatibility/version.ts +104 -0
  521. package/src/routing/cost.ts +77 -0
  522. package/src/routing/evaluator.ts +495 -0
  523. package/src/routing/health.ts +412 -0
  524. package/src/routing/history/cursor.ts +43 -0
  525. package/src/routing/history/indexer.ts +605 -0
  526. package/src/routing/history/schema.ts +72 -0
  527. package/src/routing/profile-namespace.ts +15 -0
  528. package/src/routing/profile.ts +547 -0
  529. package/src/routing/quota.ts +145 -0
  530. package/src/routing/request-evidence.ts +45 -0
  531. package/src/routing/trace.ts +776 -0
  532. package/src/server/adapter-resolve.ts +2 -29
  533. package/src/server/auth-cors.ts +267 -46
  534. package/src/server/background-lifecycle.ts +182 -0
  535. package/src/server/chat-completions.ts +130 -56
  536. package/src/server/chat-native-sse.ts +331 -0
  537. package/src/server/chat-native.ts +426 -0
  538. package/src/server/claude-messages.ts +159 -43
  539. package/src/server/direct-local-http.ts +347 -0
  540. package/src/server/effort-policy.ts +18 -0
  541. package/src/server/github-copilot-responses-repair.ts +338 -0
  542. package/src/server/gui-static.ts +39 -10
  543. package/src/server/images.ts +94 -12
  544. package/src/server/index.ts +865 -181
  545. package/src/server/lifecycle.ts +284 -13
  546. package/src/server/live.ts +136 -17
  547. package/src/server/local-management-read-client.ts +90 -0
  548. package/src/server/local-provider-reload-client.ts +137 -0
  549. package/src/server/management/agent-settings-routes.ts +398 -103
  550. package/src/server/management/api-key-usage.ts +31 -5
  551. package/src/server/management/body.ts +6 -0
  552. package/src/server/management/combo-routes.ts +62 -24
  553. package/src/server/management/config-routes.ts +464 -51
  554. package/src/server/management/context.ts +80 -2
  555. package/src/server/management/integration-routes.ts +498 -0
  556. package/src/server/management/lab-automation-routes.ts +206 -0
  557. package/src/server/management/lab-routes.ts +563 -0
  558. package/src/server/management/logs-usage-routes.ts +101 -32
  559. package/src/server/management/model-routes.ts +189 -131
  560. package/src/server/management/model-rows.ts +163 -0
  561. package/src/server/management/native-integration-routes.ts +769 -0
  562. package/src/server/management/oauth-account-routes.ts +80 -4
  563. package/src/server/management/provider-capability-config.ts +48 -0
  564. package/src/server/management/provider-routes.ts +764 -157
  565. package/src/server/management/request-history-routes.ts +191 -0
  566. package/src/server/management/routing-analytics-routes.ts +74 -0
  567. package/src/server/management/routing-profile-routes.ts +380 -0
  568. package/src/server/management/shared.ts +27 -11
  569. package/src/server/management/sidebar-routes.ts +47 -31
  570. package/src/server/management/storage-log-guard-routes.ts +186 -0
  571. package/src/server/management/sync-response.ts +69 -0
  572. package/src/server/management/system-restart.ts +288 -32
  573. package/src/server/management/system-routes.ts +77 -0
  574. package/src/server/management/usage-summary-cache.ts +9 -1
  575. package/src/server/management/vision-sidecar-options.ts +167 -0
  576. package/src/server/management/web-search-sidecar-options.ts +120 -0
  577. package/src/server/management-api.ts +115 -14
  578. package/src/server/management-auth.ts +220 -5
  579. package/src/server/passive-route-linker.ts +66 -0
  580. package/src/server/ports.ts +41 -1
  581. package/src/server/proxy-liveness.ts +132 -5
  582. package/src/server/readiness.ts +99 -0
  583. package/src/server/relay-eager.ts +82 -42
  584. package/src/server/relay.ts +236 -76
  585. package/src/server/request-decompress.ts +113 -6
  586. package/src/server/request-log.ts +235 -22
  587. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  588. package/src/server/responses/agent-task-recovery.ts +465 -0
  589. package/src/server/responses/collaboration.ts +204 -35
  590. package/src/server/responses/compact.ts +442 -55
  591. package/src/server/responses/core.ts +2872 -331
  592. package/src/server/responses/empty-completion-guard.ts +276 -0
  593. package/src/server/responses/encrypted-payload.ts +62 -39
  594. package/src/server/responses/fetch-helpers.ts +79 -4
  595. package/src/server/responses/input-admission.ts +185 -0
  596. package/src/server/responses/pacing-overload.ts +13 -0
  597. package/src/server/responses/policy-fallback.ts +178 -0
  598. package/src/server/responses/responses-field-backfill.ts +251 -0
  599. package/src/server/responses/terminal-guard.ts +26 -5
  600. package/src/server/responses/upstream-error.ts +5 -0
  601. package/src/server/responses/ws-upstream.ts +308 -0
  602. package/src/server/responses-custom-tool-repair.ts +282 -0
  603. package/src/server/responses-item-id-repair.ts +54 -6
  604. package/src/server/responses-json-events.ts +90 -0
  605. package/src/server/responses-model-rewrite.ts +29 -0
  606. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  607. package/src/server/responses-snapshot-repair.ts +621 -0
  608. package/src/server/responses-terminal-repair.ts +342 -0
  609. package/src/server/responses-tool-search-repair.ts +267 -0
  610. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  611. package/src/server/responses.ts +18 -2
  612. package/src/server/search.ts +78 -13
  613. package/src/server/sse-frame-buffer.ts +292 -0
  614. package/src/server/sse-payload-rewrite.ts +110 -22
  615. package/src/server/startup-action-control.ts +8 -1
  616. package/src/server/startup-health-cache.ts +19 -1
  617. package/src/server/system-env.ts +80 -9
  618. package/src/server/ws-bridge.ts +39 -38
  619. package/src/service-manager-probe.ts +892 -0
  620. package/src/service.ts +1111 -90
  621. package/src/sidecar/auth.ts +92 -0
  622. package/src/sidecar/candidates.ts +83 -0
  623. package/src/storage/cleanup.ts +2 -2
  624. package/src/storage/scanner.ts +1 -1
  625. package/src/storage/worker-lifecycle.ts +14 -14
  626. package/src/tray/windows-tray.ps1 +83 -9
  627. package/src/tray/windows.ts +43 -16
  628. package/src/types/accounts.ts +37 -0
  629. package/src/types/config.ts +845 -0
  630. package/src/types/provider.ts +545 -0
  631. package/src/types/request.ts +384 -0
  632. package/src/types/tools.ts +131 -0
  633. package/src/types/wire.ts +80 -0
  634. package/src/types.ts +104 -1236
  635. package/src/update/index.ts +32 -19
  636. package/src/update/job.ts +442 -67
  637. package/src/update/notify.ts +12 -6
  638. package/src/update/npm-cache-preflight.d.mts +47 -0
  639. package/src/update/npm-cache-preflight.mjs +201 -0
  640. package/src/update/transactional-install.d.mts +22 -0
  641. package/src/update/transactional-install.mjs +259 -0
  642. package/src/usage/cost.ts +0 -0
  643. package/src/usage/expected-prices.ts +268 -16
  644. package/src/usage/log.ts +606 -41
  645. package/src/usage/summary.ts +177 -9
  646. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  647. package/src/usage/user-cost-overlays.ts +314 -0
  648. package/src/vision/anthropic-describe.ts +10 -6
  649. package/src/vision/backends.ts +97 -0
  650. package/src/vision/describe.ts +9 -3
  651. package/src/vision/eligibility.ts +250 -0
  652. package/src/vision/index.ts +238 -24
  653. package/src/vision/reasoning.ts +55 -0
  654. package/src/vision/routed-describe.ts +175 -0
  655. package/src/vision/timeout-bounds.ts +9 -0
  656. package/src/web-search/anthropic-executor.ts +13 -7
  657. package/src/web-search/backends.ts +108 -0
  658. package/src/web-search/exa-executor.ts +88 -0
  659. package/src/web-search/executor.ts +11 -3
  660. package/src/web-search/gemini-executor.ts +141 -0
  661. package/src/web-search/index.ts +150 -15
  662. package/src/web-search/loop.ts +279 -50
  663. package/src/web-search/parse.ts +125 -30
  664. package/src/web-search/sources.ts +60 -0
  665. package/src/web-search/xai-executor.ts +219 -0
  666. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  667. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  668. package/gui/dist/provider-icons/antigravity.svg +0 -1
  669. package/gui/dist/provider-icons/claude.svg +0 -1
  670. package/gui/dist/provider-icons/copilot.svg +0 -1
  671. package/gui/dist/provider-icons/cursor.svg +0 -2
  672. package/gui/dist/provider-icons/gemini.svg +0 -1
  673. package/gui/dist/provider-icons/grok-color.svg +0 -1
  674. package/gui/dist/provider-icons/kiro.svg +0 -14
  675. package/src/cli/internal-dispatch.ts +0 -20
@@ -1,13 +1,20 @@
1
1
  import type { AdapterRequest, IncomingMeta, ProviderAdapter } from "../adapters/base";
2
- import type { AdapterEvent, OcxMessage, OcxParsedRequest, OcxProviderConfig, OcxThinkingContent, OcxUsage } from "../types";
3
- import { namespacedToolName } from "../types";
2
+ import type { AdapterEvent, OcxMessage, OcxParsedRequest, OcxProviderConfig, OcxProviderOpaqueToolCallMetadata, OcxThinkingContent, OcxUsage, RateLimitRetryPolicy } from "../types";
3
+ import { namespacedToolName, toolChoiceToolPredicate } from "../types";
4
+ import { cloneProviderOpaqueToolCallMetadata } from "../responses/provider-opaque-metadata";
5
+ import type { AttemptRecoveryKind } from "../usage/log";
4
6
  import { bridgeToResponsesSSE } from "../bridge";
5
7
  import { runWebSearch, type SidecarOutcome, type SidecarOutcomeRecorder, type SidecarSettings } from "./executor";
6
8
  import { runAnthropicWebSearch } from "./anthropic-executor";
9
+ import { runXaiWebSearch, type XaiSearchOptions } from "./xai-executor";
10
+ import { runGeminiWebSearch } from "./gemini-executor";
11
+ import { runExaWebSearch } from "./exa-executor";
12
+ import type { WebSearchBackendId } from "./index";
7
13
  import { clearableDeadline } from "../lib/abort";
8
14
  import { redactSecretString } from "../lib/redact";
9
15
  import { readBoundedResponseBody } from "../lib/bounded-body";
10
- import { fetchWithResetRetry } from "../lib/upstream-retry";
16
+ import { fetchWithResetRetry, prepareSameTarget429Wait } from "../lib/upstream-retry";
17
+ import { rateLimitRetryDelayMs } from "../providers/key-failover";
11
18
  import {
12
19
  isTranslatorBudgetExceededError,
13
20
  TRANSLATOR_MAX_TURN_BYTES,
@@ -30,6 +37,11 @@ interface WebSearchCall {
30
37
  // empty array means the model called the tool with neither `query` nor `queries` (handled as an
31
38
  // empty-query placeholder).
32
39
  queries: string[];
40
+ /**
41
+ * Provider-opaque metadata from the originating part (issue #1735). Stored PER CALL so a
42
+ * signature can never migrate to a different call when the model batches several.
43
+ */
44
+ providerMetadata?: OcxProviderOpaqueToolCallMetadata;
33
45
  }
34
46
 
35
47
  /**
@@ -61,40 +73,68 @@ export function scanEventsForWebSearch(events: AdapterEvent[]): {
61
73
  calls: WebSearchCall[];
62
74
  passthrough: AdapterEvent[];
63
75
  hasRealToolCall: boolean;
76
+ hasMalformedToolCall: boolean;
64
77
  } {
65
78
  const calls: WebSearchCall[] = [];
66
79
  const passthrough: AdapterEvent[] = [];
67
80
  let hasRealToolCall = false;
68
- let pending: { name: string; id: string; argsBuf: string; events: AdapterEvent[] } | null = null;
81
+ let hasMalformedToolCall = false;
82
+ let pending: { name: string; id: string; argsBuf: string; closed: boolean; events: AdapterEvent[]; providerMetadata?: OcxProviderOpaqueToolCallMetadata } | null = null;
83
+ const isBlank = (value: string): boolean => value.trim().length === 0;
69
84
  const flushPending = (): void => {
85
+ // A pending call that never saw tool_call_end is structurally malformed.
86
+ if (pending && !pending.closed) hasMalformedToolCall = true;
70
87
  if (pending && pending.name !== WEB_SEARCH_TOOL_NAME) {
71
88
  passthrough.push(...pending.events);
72
- hasRealToolCall = true;
89
+ if (pending.closed && !isBlank(pending.id) && !isBlank(pending.name)) hasRealToolCall = true;
73
90
  }
74
91
  pending = null;
75
92
  };
76
93
  for (const e of events) {
77
94
  if (e.type === "tool_call_start") {
78
95
  flushPending();
79
- pending = { name: e.name, id: e.id, argsBuf: "", events: [e] };
80
- } else if (e.type === "tool_call_delta" && pending) {
81
- pending.argsBuf += e.arguments;
82
- pending.events.push(e);
83
- } else if (e.type === "tool_call_end" && pending) {
84
- pending.events.push(e);
85
- if (pending.name === WEB_SEARCH_TOOL_NAME) {
86
- calls.push({ id: pending.id, queries: parseQueries(pending.argsBuf) });
96
+ if (isBlank(e.id) || isBlank(e.name)) hasMalformedToolCall = true;
97
+ pending = { name: e.name, id: e.id, argsBuf: "", closed: false, events: [e], providerMetadata: e.providerMetadata };
98
+ } else if (e.type === "tool_call_delta") {
99
+ // Orphan delta (no open call) is malformed.
100
+ if (!pending) hasMalformedToolCall = true;
101
+ else {
102
+ pending.argsBuf += e.arguments;
103
+ pending.events.push(e);
104
+ }
105
+ } else if (e.type === "tool_call_end") {
106
+ // Orphan end (no open call) is malformed.
107
+ if (!pending) {
108
+ hasMalformedToolCall = true;
87
109
  } else {
88
- passthrough.push(...pending.events);
89
- hasRealToolCall = true;
110
+ pending.events.push(e);
111
+ pending.closed = true;
112
+ if (pending.name === WEB_SEARCH_TOOL_NAME) {
113
+ calls.push({ id: pending.id, queries: parseQueries(pending.argsBuf), providerMetadata: pending.providerMetadata });
114
+ } else {
115
+ passthrough.push(...pending.events);
116
+ if (!isBlank(pending.id) && !isBlank(pending.name)) hasRealToolCall = true;
117
+ }
118
+ pending = null;
90
119
  }
91
- pending = null;
92
120
  } else {
93
121
  passthrough.push(e);
94
122
  }
95
123
  }
96
124
  flushPending();
97
- return { calls, passthrough, hasRealToolCall };
125
+ return { calls, passthrough, hasRealToolCall, hasMalformedToolCall };
126
+ }
127
+
128
+ /**
129
+ * Visible final-answer text: a non-whitespace text_delta that is NOT commentary
130
+ * (#1001). Commentary streams early for progress and must not satisfy the
131
+ * forced-answer output check; thinking alone never counts either.
132
+ */
133
+ export function hasVisibleAssistantText(events: AdapterEvent[]): boolean {
134
+ return events.some(event =>
135
+ event.type === "text_delta"
136
+ && event.phase !== "commentary"
137
+ && event.text.trim().length > 0);
98
138
  }
99
139
 
100
140
  async function* replay(events: AdapterEvent[]): AsyncGenerator<AdapterEvent> {
@@ -206,16 +246,32 @@ class LoopError extends Error {
206
246
  }
207
247
  }
208
248
 
249
+ /**
250
+ * Dependencies for one web-search loop iteration: parsed request, active adapter,
251
+ * incoming metadata, and the configured search executor.
252
+ */
209
253
  export interface WebSearchLoopDeps {
210
254
  parsed: OcxParsedRequest;
211
255
  adapter: ProviderAdapter;
212
256
  incomingMeta: IncomingMeta;
213
- /** Which executor runs searches. Defaults to "openai" so existing callers keep the ChatGPT path (audit F4). */
214
- backend?: "openai" | "anthropic";
257
+ /**
258
+ * Which executor runs searches. Defaults to "openai" so existing callers keep the ChatGPT path
259
+ * (audit F4). The widened ids (xai/gemini/exa) cannot reach the loop yet: planWebSearch returns
260
+ * no plan for them (inert 060 arms), and the dispatch below only branches on "anthropic".
261
+ */
262
+ backend?: WebSearchBackendId;
215
263
  /** Required for the openai backend; unused (and typically undefined) for the anthropic backend. */
216
264
  forwardProvider?: OcxProviderConfig;
217
265
  /** Required for the anthropic backend: the stored-OAuth provider that runs web_search_20250305. */
218
266
  anthropicSidecar?: { providerName: string; provider: OcxProviderConfig };
267
+ /** Required for the xai backend: the stored Grok OAuth provider (L7). */
268
+ xaiSidecar?: { providerName: string; provider: OcxProviderConfig };
269
+ /** Required for the gemini backend: the stored Antigravity CCA provider (L8). */
270
+ geminiSidecar?: { providerName: string; provider: OcxProviderConfig };
271
+ /** Required for the exa backend: the operator key, read from config at plan unpack (L9). */
272
+ exaApiKey?: string;
273
+ /** Opt-in x_search options for the xai backend. */
274
+ xaiSearchOptions?: XaiSearchOptions;
219
275
  hostedTool: Record<string, unknown>;
220
276
  selectedForwardHeaders: Headers;
221
277
  settings: SidecarSettings;
@@ -233,17 +289,29 @@ export interface WebSearchLoopDeps {
233
289
  * sidecar search, so a legitimately slow-but-progressing unit never trips the bridge watchdog.
234
290
  */
235
291
  stallTimeoutSec?: number;
292
+ /**
293
+ * Opt-in: stream the routed model's leading text/thinking deltas live instead of holding the whole
294
+ * iteration back. The live window closes at the first buffer-only event (tool calls above all) so
295
+ * the web_search interception decision stays atomic; everything after replays in order at the end.
296
+ */
297
+ streamRoutedModelOutput?: boolean;
236
298
  /** One-shot TTFT callback: first non-empty model output observed (WP4). */
237
299
  onFirstOutput?: () => void;
238
300
  /** Raw adapter usage at the terminal event, pre wire-normalization (see bridgeToResponsesSSE onUsage). */
239
301
  onUsage?: (usage: OcxUsage | undefined) => void;
240
302
  /** Observe the exact adapter request selected for each routed-model iteration. */
241
303
  onRequestBuilt?: (request: AdapterRequest) => void;
304
+ /** Called before each routed-model dispatch in the loop, for attempt telemetry. Same-target 429 replays pass the `rate-limit-429` recovery kind. */
305
+ onAttemptSend?: (recovery?: AttemptRecoveryKind) => void;
242
306
  /**
243
307
  * 429 key-failover hook: rotate the provider's active pool key and return a rebuilt adapter,
244
308
  * or null when the pool is exhausted (same semantics as the normal routed path).
245
309
  */
246
310
  on429?: (retryAfterHeader: string | null) => ProviderAdapter | null;
311
+ /** Opt-in same-target 429 policy (key-auth providers). When present, 429 replays on the SAME key before on429 rotation. */
312
+ retryOn429Policy?: Required<RateLimitRetryPolicy> | null;
313
+ /** Called only when the final bridged Responses stream reaches completed or incomplete. */
314
+ onCompletedResponse?: (response: Record<string, unknown>) => void;
247
315
  }
248
316
 
249
317
  /**
@@ -260,6 +328,12 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
260
328
  // Mutable: 429 key-failover (deps.on429) can swap in a rebuilt adapter mid-loop.
261
329
  let adapter = deps.adapter;
262
330
 
331
+ // Bridge stall budget (seconds of silence before upstream_stall_timeout); the retry backoff
332
+ // heartbeat interval is derived from it so the watchdog is always fed during deliberate waits.
333
+ const stallTimeoutMs = typeof deps.stallTimeoutSec === "number" && Number.isFinite(deps.stallTimeoutSec) && deps.stallTimeoutSec > 0
334
+ ? Math.floor(deps.stallTimeoutSec * 1000)
335
+ : 300_000;
336
+
263
337
  const messages: OcxMessage[] = [...parsed.context.messages];
264
338
  const loopT0 = Date.now();
265
339
  const allTools = parsed.context.tools ?? [];
@@ -292,11 +366,29 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
292
366
  response: Response;
293
367
  responseAdapter: ProviderAdapter;
294
368
  }
295
- type IterationSplit = ReturnType<typeof scanEventsForWebSearch>;
369
+ type IterationSplit = ReturnType<typeof scanEventsForWebSearch> & {
370
+ /**
371
+ * How many leading passthrough events were already delivered live this iteration. They are
372
+ * exactly the first N passthrough entries (live delivery stops before the first event that
373
+ * scanEventsForWebSearch could group or reorder), so the terminal replay skips them by count.
374
+ */
375
+ streamedPassthroughCount: number;
376
+ };
377
+
378
+ // Same-target 429 budget is per REQUEST, not per model iteration: later search rounds inherit
379
+ // what earlier rounds left of `attempts`, so a bounded multi-round turn can never exceed the
380
+ // configured replay count in total (a per-round reset would multiply it by maxSearches).
381
+ const rateLimitRetryPolicy = deps.retryOn429Policy ?? null;
382
+ let rateLimitRetries = 0;
296
383
 
297
384
  // Acquire one iteration's final response headers. The first call is drained eagerly so an initial
298
385
  // connect/header/HTTP failure stays a non-2xx JSON response. Its successful BODY is deliberately
299
386
  // left unread until the downstream Responses SSE bridge exists.
387
+ /**
388
+ * Fetch one web-search iteration's final response headers, applying the response-header
389
+ * deadline and the same-target 429 retry policy (with awaited body release and deadline
390
+ * restart) before the `on429` key rotation.
391
+ */
300
392
  const prepareIterationEvents = async function* (forceAnswer: boolean): AsyncGenerator<AdapterEvent, IterationResponse> {
301
393
  // On the forced-answer pass the synthetic web_search tool is gone, so the model MUST answer
302
394
  // from the results already in `messages`. A weak model can still produce a thin answer that
@@ -313,30 +405,55 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
313
405
  // One cumulative header deadline spans every pool-key 429 rotation in this model iteration.
314
406
  // clear() stops only its timer after final headers; the direct turn signal remains attached to
315
407
  // the returned response body through AbortSignal.any().
316
- const headerDeadline = clearableDeadline(connectTimeoutMs, signal);
408
+ let headerDeadline = clearableDeadline(connectTimeoutMs, signal);
317
409
  try {
318
- const fetchOnce = async (requestAdapter: ProviderAdapter): Promise<IterationResponse> => {
319
- const request = await requestAdapter.buildRequest(iterParsed, {
320
- headers: selectedForwardHeaders,
321
- abortSignal: headerDeadline.signal,
322
- translatorBudget,
323
- });
324
- try {
325
- deps.onRequestBuilt?.(request);
326
- } catch {
327
- // Diagnostics are best-effort and must never abort a web-search iteration.
410
+ /**
411
+ * Build and fetch one web-search iteration on the given adapter, under the iteration
412
+ * header deadline. The caller owns same-target 429 replays and key rotation around it.
413
+ * The outbound request is cached per adapter so a same-target replay reuses the EXACT
414
+ * URL, serialized body, and headers (builder runs once per target sequence).
415
+ */
416
+ let cachedRequest: AdapterRequest | undefined;
417
+ let cachedAdapter: ProviderAdapter | undefined;
418
+ /**
419
+ * Build and fetch one web-search iteration on the given adapter, under the iteration
420
+ * header deadline. The caller owns same-target 429 replays and key rotation around it.
421
+ */
422
+ const fetchOnce = async (requestAdapter: ProviderAdapter, recovery?: AttemptRecoveryKind): Promise<IterationResponse> => {
423
+ let request: AdapterRequest;
424
+ if (cachedRequest !== undefined && cachedAdapter === requestAdapter) {
425
+ request = cachedRequest;
426
+ } else {
427
+ request = await requestAdapter.buildRequest(iterParsed, {
428
+ headers: selectedForwardHeaders,
429
+ abortSignal: headerDeadline.signal,
430
+ translatorBudget,
431
+ });
432
+ try {
433
+ deps.onRequestBuilt?.(request);
434
+ } catch {
435
+ // Diagnostics are best-effort and must never abort a web-search iteration.
436
+ }
437
+ cachedRequest = request;
438
+ cachedAdapter = requestAdapter;
328
439
  }
329
440
  let response: Response;
330
441
  try {
331
- response = requestAdapter.fetchResponse
332
- ? await requestAdapter.fetchResponse(request, {
442
+ if (requestAdapter.fetchResponse) {
443
+ deps.onAttemptSend?.(recovery);
444
+ response = await requestAdapter.fetchResponse(request, {
333
445
  abortSignal: headerDeadline.signal,
334
446
  timeoutMs: connectTimeoutMs,
335
447
  returnRawErrors: true,
336
448
  stream: true,
337
- })
338
- : await fetchWithResetRetry(
339
- () => {
449
+ });
450
+ } else {
451
+ response = await fetchWithResetRetry(
452
+ (retryRecovery) => {
453
+ // Record every helper-driven send (the callback runs for the first attempt and
454
+ // each connection-reset replay); preserve the caller's recovery kind
455
+ // (rate-limit-429 / key-429) when the retry layer supplies none.
456
+ deps.onAttemptSend?.(retryRecovery ?? recovery);
340
457
  const h = new Headers(request.headers);
341
458
  if (!h.has("accept-encoding")) h.set("accept-encoding", "identity");
342
459
  return fetch(request.url, {
@@ -347,7 +464,8 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
347
464
  });
348
465
  },
349
466
  { abortSignal: headerDeadline.signal, label: "web-search-loop" },
350
- );
467
+ );
468
+ }
351
469
  } finally {
352
470
  request.releaseBodyObservation?.();
353
471
  }
@@ -355,6 +473,39 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
355
473
  };
356
474
 
357
475
  let prepared = await fetchOnce(adapter);
476
+ // Same-target 429 wait-and-retry (opt-in `retryOn429`) BEFORE key rotation: a primary-key
477
+ // rate-limit blip replays on the SAME key; rotation only runs after attempts exhaust.
478
+ while (
479
+ prepared.response.status === 429
480
+ && rateLimitRetryPolicy !== null
481
+ && rateLimitRetries < rateLimitRetryPolicy.attempts
482
+ ) {
483
+ rateLimitRetries += 1;
484
+ // Release unread body + heartbeat-fed wait via the shared same-target helper.
485
+ const retryAfterHeader = prepared.response.headers.get("retry-after");
486
+ // The old header deadline must not stay armed across the deliberate wait: clear it
487
+ // before sleeping so a stale expiry can never race the client-cancel path.
488
+ headerDeadline.clear();
489
+ try {
490
+ yield* prepareSameTarget429Wait({
491
+ body: prepared.response.body,
492
+ signal,
493
+ delayMs: rateLimitRetryDelayMs(rateLimitRetryPolicy, retryAfterHeader, Date.now()),
494
+ heartbeatIntervalMs: Math.min(10_000, Math.max(250, stallTimeoutMs / 2)),
495
+ });
496
+ } catch {
497
+ throw new LoopError(499, "client closed request during web-search");
498
+ }
499
+ // Client cancellation wins over any stale-deadline edge: re-check before telemetry/replay.
500
+ if (signal.aborted) throw new LoopError(499, "client closed request during web-search");
501
+ // The deliberate backoff must not consume the cumulative response-header deadline:
502
+ // start a fresh one so the replay gets a new connect budget (504 stays reserved for real
503
+ // upstream latency).
504
+ headerDeadline = clearableDeadline(connectTimeoutMs, signal);
505
+ // Stall-watchdog seam between bounded retry fetches.
506
+ yield { type: "heartbeat" };
507
+ prepared = await fetchOnce(adapter, "rate-limit-429");
508
+ }
358
509
  // 429 key-failover parity with the normal routed path: rotate pool keys until one responds
359
510
  // or the pool is exhausted (deps.on429 returns null — cooldown map guarantees termination).
360
511
  while (prepared.response.status === 429 && deps.on429) {
@@ -366,7 +517,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
366
517
  adapter = rotated;
367
518
  // Stall-watchdog seam between bounded retry fetches (audit 011 B3).
368
519
  yield { type: "heartbeat" };
369
- prepared = await fetchOnce(adapter);
520
+ prepared = await fetchOnce(adapter, "key-429");
370
521
  }
371
522
 
372
523
  // Final headers have arrived. Clear only the deadline timer before ANY body read.
@@ -417,10 +568,23 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
417
568
  return r.value;
418
569
  };
419
570
 
571
+ // Event types that may leave the live window before the first tool-call boundary: pure
572
+ // text/thinking output the native (sidecar-less) path would deliver identically. Everything
573
+ // else — tool calls above all, and any type scanEventsForWebSearch could group or a future
574
+ // adapter could add — closes the window so live delivery can never reorder against the replay.
575
+ const LIVE_STREAMABLE = new Set<AdapterEvent["type"]>([
576
+ "text_delta", "thinking_delta", "reasoning_raw_delta",
577
+ "thinking_signature", "redacted_thinking", "kiro_redacted_reasoning",
578
+ ]);
579
+
420
580
  // Consume and validate one successful response body under a resettable raw-byte inactivity guard.
421
- // Only invisible heartbeat events escape while semantic output remains buffered for safe scanning.
581
+ // By default only invisible heartbeat events escape while semantic output remains buffered for
582
+ // safe scanning; with `streamRoutedModelOutput` the leading text/thinking deltas stream live and
583
+ // the live window closes permanently at the first buffer-only event (see LIVE_STREAMABLE).
422
584
  const consumeIterationEvents = async function* (prepared: IterationResponse): AsyncGenerator<AdapterEvent, IterationSplit> {
423
585
  const events: AdapterEvent[] = [];
586
+ let liveWindowOpen = deps.streamRoutedModelOutput === true;
587
+ let streamedPassthroughCount = 0;
424
588
  try {
425
589
  const parse = prepared.responseAdapter.parseStream.bind(prepared.responseAdapter);
426
590
  for await (const event of parseStreamWithProgress(prepared.response, parse, {
@@ -436,7 +600,16 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
436
600
  // Tool events remain buffered below, so the decision to invoke the hosted sidecar is still
437
601
  // atomic and no search call can escape before its stream has validated successfully.
438
602
  else if (event.type === "text_delta" && event.phase === "commentary") yield event;
439
- else events.push(event);
603
+ else if (liveWindowOpen && LIVE_STREAMABLE.has(event.type)) {
604
+ // Live events are ALSO buffered: the scanner still needs them for thinking extraction
605
+ // and the forced-answer output check; only the terminal replay skips them (by count).
606
+ yield event;
607
+ streamedPassthroughCount++;
608
+ events.push(event);
609
+ } else {
610
+ liveWindowOpen = false;
611
+ events.push(event);
612
+ }
440
613
  }
441
614
  } catch (error) {
442
615
  if (isTranslatorBudgetExceededError(error)) throw error;
@@ -458,7 +631,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
458
631
  }
459
632
  throw new LoopError(502, terminal.message);
460
633
  }
461
- return scanEventsForWebSearch(events);
634
+ return { ...scanEventsForWebSearch(events), streamedPassthroughCount };
462
635
  };
463
636
 
464
637
  // Execute one model-requested web_search call. The call may batch several queries (native
@@ -501,9 +674,30 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
501
674
  // signal.aborted both after the await and in the catch (a fulfilled {error} on an aborted
502
675
  // signal would otherwise look like an ordinary degradable failure).
503
676
  try {
504
- outcome = backend === "anthropic" && anthropicSidecar
505
- ? await runAnthropicWebSearch(query, anthropicSidecar.providerName, anthropicSidecar.provider, settings, signal)
506
- : await runWebSearch(query, hostedTool, forwardProvider!, selectedForwardHeaders, settings, signal, recordSidecarOutcome);
677
+ if (backend === "anthropic" && anthropicSidecar) {
678
+ outcome = await runAnthropicWebSearch(query, anthropicSidecar.providerName, anthropicSidecar.provider, settings, signal);
679
+ } else if (backend === "xai") {
680
+ // L7: stored Grok OAuth to the pinned api.x.ai Responses endpoint; same
681
+ // never-throws contract and no Codex/OpenAI pool outcome recording (F5 parity).
682
+ // A missing xaiSidecar is an invariant violation — fail CLOSED with an error
683
+ // outcome rather than falling through to the forward-header OpenAI executor
684
+ // (review High: that fallthrough would be credential-sensitive).
685
+ outcome = deps.xaiSidecar
686
+ ? await runXaiWebSearch(query, deps.xaiSidecar.providerName, deps.xaiSidecar.provider, settings, deps.xaiSearchOptions ?? {}, signal)
687
+ : { text: "", sources: [], error: "xai backend selected without a resolved Grok OAuth provider" };
688
+ } else if (backend === "gemini") {
689
+ // L8: Antigravity CCA grounding; same fail-closed invariant stance as xai.
690
+ outcome = deps.geminiSidecar
691
+ ? await runGeminiWebSearch(query, deps.geminiSidecar.providerName, deps.geminiSidecar.provider, settings, signal)
692
+ : { text: "", sources: [], error: "gemini backend selected without a resolved Antigravity provider" };
693
+ } else if (backend === "exa") {
694
+ // L9: non-LLM lane; key comes from the loop deps, never the plan. Fail closed.
695
+ outcome = deps.exaApiKey
696
+ ? await runExaWebSearch(query, deps.exaApiKey, settings, signal)
697
+ : { text: "", sources: [], error: "exa backend selected without an exaApiKey" };
698
+ } else {
699
+ outcome = await runWebSearch(query, hostedTool, forwardProvider!, selectedForwardHeaders, settings, signal, recordSidecarOutcome);
700
+ }
507
701
  if (signal.aborted) throw new LoopError(499, "client closed request during web-search");
508
702
  } catch (e) {
509
703
  if (e instanceof LoopError) throw e;
@@ -529,7 +723,17 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
529
723
  // Signed thinking must precede tool_use on replay (Anthropic extended thinking), and
530
724
  // unsigned raw reasoning has to ride along for providers that require it back (#688).
531
725
  ...precedingThinking,
532
- { type: "toolCall" as const, id: call.id, name: WEB_SEARCH_TOOL_NAME, arguments: callArgs },
726
+ {
727
+ type: "toolCall" as const,
728
+ id: call.id,
729
+ name: WEB_SEARCH_TOOL_NAME,
730
+ arguments: callArgs,
731
+ // Re-attach the signature to the rebuilt call so a sidecar turn keeps Gemini
732
+ // reasoning continuity instead of relying on the same-process replay cache.
733
+ ...(cloneProviderOpaqueToolCallMetadata(call.providerMetadata)
734
+ ? { providerMetadata: cloneProviderOpaqueToolCallMetadata(call.providerMetadata) }
735
+ : {}),
736
+ },
533
737
  ],
534
738
  timestamp: now,
535
739
  });
@@ -576,11 +780,20 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
576
780
  throw e;
577
781
  }
578
782
 
579
- const toolNsMap = new Map<string, { namespace: string; name: string }>();
783
+ const toolNsMap = new Map<string, { namespace: string; name: string; freeform?: true }>();
580
784
  const freeform = new Set<string>();
581
785
  const toolSearch = new Set<string>();
582
- for (const t of parsed.context.tools ?? []) {
583
- if (t.namespace) toolNsMap.set(namespacedToolName(t.namespace, t.name), { namespace: t.namespace, name: t.name });
786
+ const requestedTools = parsed.context.tools ?? [];
787
+ const toolAllowed = toolChoiceToolPredicate(parsed.options.toolChoice, requestedTools);
788
+ for (const t of requestedTools) {
789
+ if (!toolAllowed(t)) continue;
790
+ if (t.namespace) {
791
+ toolNsMap.set(namespacedToolName(t.namespace, t.name), {
792
+ namespace: t.namespace,
793
+ name: t.name,
794
+ ...(t.freeform ? { freeform: true } : {}),
795
+ });
796
+ }
584
797
  if (t.freeform) freeform.add(t.name);
585
798
  if (t.toolSearch) toolSearch.add(t.name);
586
799
  }
@@ -607,6 +820,18 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
607
820
  // calls reach Codex. forceAnswer also finalizes.
608
821
  const shouldLoop = split.calls.length > 0 && !split.hasRealToolCall && !forceAnswer;
609
822
  if (!shouldLoop) {
823
+ // #1001: a forced-answer pass that ends `done` must have produced
824
+ // usable output — never a malformed tool call, and never silence.
825
+ if (forceAnswer) {
826
+ // An unterminated call flushes AFTER the terminal event, so find
827
+ // the terminal rather than assuming it is last (#1001).
828
+ const terminalEvent = split.passthrough.find(event => event.type === "done");
829
+ if (terminalEvent?.type === "done"
830
+ && (split.hasMalformedToolCall
831
+ || (!split.hasRealToolCall && !hasVisibleAssistantText(split.passthrough)))) {
832
+ throw new LoopError(502, "forced-answer pass produced no usable assistant output");
833
+ }
834
+ }
610
835
  if (executedSearchCount > 0) {
611
836
  const failedCount = failedQueries.size;
612
837
  console.warn(
@@ -615,7 +840,9 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
615
840
  + `, ${i + 1} iteration${i > 0 ? "s" : ""}, ${Date.now() - loopT0}ms`,
616
841
  );
617
842
  }
618
- yield* replay(split.passthrough);
843
+ // Live-streamed leading events are exactly the first N passthrough entries — replay
844
+ // only the buffered tail so nothing reaches the client twice.
845
+ yield* replay(split.passthrough.slice(split.streamedPassthroughCount));
619
846
  return;
620
847
  }
621
848
  // The thinking that led to the search belongs to the FIRST call's assistant replay turn.
@@ -644,7 +871,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
644
871
  }
645
872
 
646
873
  const sse = bridgeToResponsesSSE(
647
- produce(), parsed.modelId, toolNsMap, freeform, toolSearch, () => {
874
+ produce(), parsed._responseModelId ?? parsed.modelId, toolNsMap, freeform, toolSearch, () => {
648
875
  const elapsed = Date.now() - loopT0;
649
876
  if (executedSearchCount > 0 || searchesExecuted > 0) {
650
877
  console.warn(`[web-search-loop] cancelled — ${executedSearchCount} real searches, ${searchesExecuted - executedSearchCount} placeholders, ${elapsed}ms`);
@@ -653,11 +880,13 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
653
880
  }, undefined,
654
881
  {
655
882
  translatorBudget,
883
+ replayCacheScope: parsed._reasoningReplayScope,
656
884
  ...(deps.forceEmptyResponseId ? { responseId: "" } : {}),
657
885
  hideThinkingSummary: parsed.options.hideThinkingSummary,
658
886
  ...(deps.stallTimeoutSec !== undefined ? { stallTimeoutSec: deps.stallTimeoutSec } : {}),
659
887
  ...(deps.onFirstOutput ? { onFirstOutput: deps.onFirstOutput } : {}),
660
888
  ...(deps.onUsage ? { onUsage: deps.onUsage } : {}),
889
+ ...(deps.onCompletedResponse ? { onCompletedResponse: deps.onCompletedResponse } : {}),
661
890
  },
662
891
  );
663
892
  return new Response(sse, { headers: SSE_HEADERS });