@bitkyc08/opencodex 2.84.0 → 2.87.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 (518) hide show
  1. package/README.md +0 -4
  2. package/bin/ocx.mjs +61 -3
  3. package/gui/dist/assets/App-ZfYbLHkB.js +58 -0
  4. package/gui/dist/assets/App-wT3mXqFz.css +1 -0
  5. package/gui/dist/assets/{Tray-C9TGtwad.js → Tray-DCd-A1az.js} +1 -1
  6. package/gui/dist/assets/index-CfYDMj34.js +94 -0
  7. package/gui/dist/assets/index-DPeB9HpV.css +1 -0
  8. package/gui/dist/assets/quota-summary-ChvmKGOk.js +1 -0
  9. package/gui/dist/index.html +2 -2
  10. package/package.json +1 -2
  11. package/src/adapters/anthropic/beta-allowlist.ts +91 -11
  12. package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
  13. package/src/adapters/anthropic/client-identity.ts +1 -1
  14. package/src/adapters/anthropic/passthrough.ts +16 -3
  15. package/src/adapters/anthropic-progress.ts +84 -0
  16. package/src/adapters/anthropic.ts +108 -21
  17. package/src/adapters/azure.ts +19 -1
  18. package/src/adapters/base.ts +2 -0
  19. package/src/adapters/claude-cli/adapter.ts +111 -23
  20. package/src/adapters/claude-cli/profiles.ts +2 -1
  21. package/src/adapters/claude-cli/stable-replay.ts +108 -0
  22. package/src/adapters/claude-cli/usage-admission.ts +230 -0
  23. package/src/adapters/client-fingerprint.ts +24 -0
  24. package/src/adapters/coding-agent/protocol.ts +1 -1
  25. package/src/adapters/coding-agent/turn.ts +40 -11
  26. package/src/adapters/cursor/arg-codec.ts +17 -5
  27. package/src/adapters/cursor/catalog.ts +8 -0
  28. package/src/adapters/cursor/effort-map.ts +3 -0
  29. package/src/adapters/cursor/freeform-args.ts +75 -0
  30. package/src/adapters/cursor/protobuf-events.ts +36 -23
  31. package/src/adapters/dsh-account.ts +97 -0
  32. package/src/adapters/google-antigravity-wire.ts +22 -1
  33. package/src/adapters/google.ts +37 -10
  34. package/src/adapters/openai-chat/messages.ts +4 -3
  35. package/src/adapters/openai-chat/passthrough.ts +8 -2
  36. package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
  37. package/src/adapters/openai-chat/response-events.ts +59 -1
  38. package/src/adapters/openai-chat.ts +8 -5
  39. package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
  40. package/src/adapters/openai-responses/passthrough.ts +57 -125
  41. package/src/adapters/openai-responses/request-strips.ts +25 -3
  42. package/src/adapters/openai-responses/transport.ts +81 -0
  43. package/src/adapters/qoder/adapter.ts +44 -3
  44. package/src/adapters/registry.ts +5 -0
  45. package/src/adapters/zed.ts +26 -1
  46. package/src/advisor/activity.ts +55 -0
  47. package/src/advisor/budget.ts +62 -0
  48. package/src/advisor/consult.ts +223 -0
  49. package/src/advisor/context.ts +238 -0
  50. package/src/advisor/disclosure.ts +20 -0
  51. package/src/advisor/evidence-tool.ts +21 -0
  52. package/src/advisor/evidence.ts +117 -0
  53. package/src/advisor/runtime.ts +353 -0
  54. package/src/advisor/settings.ts +144 -0
  55. package/src/advisor/state.ts +369 -0
  56. package/src/advisor/synthetic-tool.ts +33 -0
  57. package/src/bridge/errors.ts +24 -1
  58. package/src/bridge/internal.ts +3 -1
  59. package/src/bridge/response-json.ts +18 -3
  60. package/src/bridge/sse.ts +58 -8
  61. package/src/bridge/terminal-refusal.ts +59 -0
  62. package/src/chat/inbound.ts +30 -6
  63. package/src/chat/outbound.ts +5 -0
  64. package/src/claude/desktop-3p-library.ts +72 -7
  65. package/src/claude/desktop-3p.ts +75 -19
  66. package/src/claude/desktop-first-party.ts +1 -1
  67. package/src/claude/desktop-picker-platform.ts +4 -0
  68. package/src/claude/desktop-picker-profile.ts +11 -4
  69. package/src/claude/desktop-picker.ts +15 -8
  70. package/src/claude/desktop-profile.ts +10 -1
  71. package/src/claude/desktop-remote-store-io.ts +5 -3
  72. package/src/claude/desktop-system-proxy.ts +50 -4
  73. package/src/claude/inbound-content-options.ts +11 -0
  74. package/src/claude/inbound.ts +154 -50
  75. package/src/claude/intercept/local-ca.ts +8 -1
  76. package/src/claude/intercept/picker-ca-startup.ts +2 -1
  77. package/src/claude/intercept/picker-runtime.ts +6 -5
  78. package/src/claude/intercept/picker-trust-windows.ts +338 -0
  79. package/src/claude/intercept/picker-trust.ts +19 -0
  80. package/src/claude/intercept/port-policy.ts +30 -0
  81. package/src/claude/intercept/runtime.ts +97 -94
  82. package/src/claude/model-default-settings.ts +154 -0
  83. package/src/claude/model-info.ts +7 -7
  84. package/src/claude/outbound.ts +51 -19
  85. package/src/cli/account-api.ts +1 -0
  86. package/src/cli/account-policy-dto.ts +6 -0
  87. package/src/cli/account-policy.ts +34 -5
  88. package/src/cli/account-preservation.ts +44 -0
  89. package/src/cli/account.ts +8 -2
  90. package/src/cli/advisor.ts +165 -0
  91. package/src/cli/auxiliary-listener-preflight.ts +37 -0
  92. package/src/cli/capabilities-accounts.ts +20 -1
  93. package/src/cli/capabilities-agents-routing.ts +23 -2
  94. package/src/cli/capabilities-base.ts +39 -11
  95. package/src/cli/capabilities-observe-system.ts +63 -3
  96. package/src/cli/capabilities-provider-models.ts +11 -0
  97. package/src/cli/chatgpt-command.ts +35 -14
  98. package/src/cli/claude.ts +46 -6
  99. package/src/cli/codex-cli-update.ts +151 -41
  100. package/src/cli/codex-provider.ts +44 -0
  101. package/src/cli/combo-input.ts +10 -1
  102. package/src/cli/combo-result.ts +1 -1
  103. package/src/cli/combo.ts +2 -1
  104. package/src/cli/cross-home-owner.ts +17 -14
  105. package/src/cli/dispatch.ts +30 -5
  106. package/src/cli/doctor.ts +15 -1
  107. package/src/cli/ensure-desktop-cold-start.ts +382 -0
  108. package/src/cli/export-command.ts +1 -1
  109. package/src/cli/help.ts +4 -2
  110. package/src/cli/index.ts +14 -5
  111. package/src/cli/integration-plan-dto.ts +3 -0
  112. package/src/cli/log-history.ts +172 -0
  113. package/src/cli/models-runtime-subcommands.ts +1 -0
  114. package/src/cli/models-runtime.ts +72 -8
  115. package/src/cli/observe-stream.ts +2 -0
  116. package/src/cli/observe.ts +16 -4
  117. package/src/cli/provider.ts +27 -5
  118. package/src/cli/registry.ts +41 -9
  119. package/src/cli/restart-scope.ts +3 -1
  120. package/src/cli/runtime-api.ts +5 -3
  121. package/src/cli/system-command.ts +40 -8
  122. package/src/cli/uninstall-client-state.ts +32 -0
  123. package/src/cli/usage-compare.ts +309 -0
  124. package/src/cli/usage-model-search.ts +4 -4
  125. package/src/cli/usage-report.ts +2 -2
  126. package/src/cli/v2.ts +1 -1
  127. package/src/client/machine-listener.ts +1 -3
  128. package/src/client/runtime.ts +1 -0
  129. package/src/clients/client-path.ts +51 -0
  130. package/src/clients/config-export/contracts.ts +3 -1
  131. package/src/clients/config-export/model-metadata.ts +3 -2
  132. package/src/clients/config-export/qoder.ts +93 -0
  133. package/src/clients/config-export.ts +35 -48
  134. package/src/clients/gentle-shell-path.ts +87 -0
  135. package/src/codex/account-auto-switch.ts +2 -0
  136. package/src/codex/account-identity.ts +29 -0
  137. package/src/codex/account-label.ts +1 -1
  138. package/src/codex/account-lifecycle.ts +21 -5
  139. package/src/codex/account-preservation.ts +28 -0
  140. package/src/codex/account-use-remaining.ts +23 -0
  141. package/src/codex/app-server-processes.ts +97 -7
  142. package/src/codex/app-server-restart-service.ts +55 -4
  143. package/src/codex/auth-api/account-list.ts +4 -0
  144. package/src/codex/auth-api/account-pause-group.ts +6 -50
  145. package/src/codex/auth-api/account-preservation.ts +57 -0
  146. package/src/codex/auth-api/pool-mode-gate.ts +5 -2
  147. package/src/codex/auth-api/routes.ts +8 -0
  148. package/src/codex/auth-api/use-remaining-route.ts +36 -0
  149. package/src/codex/auth-api.ts +0 -1
  150. package/src/codex/auth-context.ts +81 -7
  151. package/src/codex/autostart-health.ts +4 -0
  152. package/src/codex/catalog/auto-review.ts +20 -3
  153. package/src/codex/catalog/build-entries.ts +19 -2
  154. package/src/codex/catalog/combo-member.ts +14 -5
  155. package/src/codex/catalog/configured-tiers.ts +50 -0
  156. package/src/codex/catalog/effort.ts +4 -1
  157. package/src/codex/catalog/gather-capture.ts +2 -0
  158. package/src/codex/catalog/metadata.ts +11 -9
  159. package/src/codex/catalog/model-hints.ts +42 -19
  160. package/src/codex/catalog/model-visibility.ts +3 -1
  161. package/src/codex/catalog/native-ultrafast.ts +106 -0
  162. package/src/codex/catalog/parsing.ts +35 -86
  163. package/src/codex/catalog/provider-models.ts +40 -34
  164. package/src/codex/catalog/retained-sync.ts +2 -0
  165. package/src/codex/catalog/routed-gather.ts +33 -16
  166. package/src/codex/catalog/vendor-context.ts +82 -0
  167. package/src/codex/cli-install-provenance.ts +105 -17
  168. package/src/codex/cli-update-plan.ts +545 -0
  169. package/src/codex/config-write-lock.ts +63 -4
  170. package/src/codex/convergence.ts +2 -0
  171. package/src/codex/desktop-app/picker-freshness.ts +96 -0
  172. package/src/codex/desktop-app/types.ts +2 -0
  173. package/src/codex/desktop-app/windows.ts +29 -16
  174. package/src/codex/desktop-app-restart.ts +25 -2
  175. package/src/codex/desktop-compatibility/certificate-service.ts +99 -0
  176. package/src/codex/desktop-compatibility/certificate-store.ts +193 -0
  177. package/src/codex/desktop-compatibility/connection-store.ts +87 -0
  178. package/src/codex/desktop-compatibility/installed-build.ts +73 -0
  179. package/src/codex/desktop-compatibility/json-body.ts +29 -0
  180. package/src/codex/desktop-compatibility/native-identity.ts +62 -0
  181. package/src/codex/desktop-compatibility/relay-listener.ts +93 -0
  182. package/src/codex/desktop-compatibility/routing-binding.ts +10 -0
  183. package/src/codex/desktop-compatibility/routing-preflight.ts +69 -0
  184. package/src/codex/desktop-compatibility/runtime-ownership.ts +28 -0
  185. package/src/codex/desktop-compatibility/runtime.ts +182 -0
  186. package/src/codex/desktop-compatibility/service.ts +30 -0
  187. package/src/codex/desktop-compatibility/startup-settings.ts +50 -0
  188. package/src/codex/desktop-compatibility/usage-activation.ts +86 -0
  189. package/src/codex/desktop-compatibility/usage-controlled-fetch.ts +59 -0
  190. package/src/codex/desktop-compatibility/usage-controller.ts +107 -0
  191. package/src/codex/desktop-compatibility/usage-policy.ts +59 -0
  192. package/src/codex/desktop-compatibility/usage-refresh.ts +33 -0
  193. package/src/codex/desktop-compatibility/usage-sse-controller.ts +86 -0
  194. package/src/codex/desktop-compatibility/windows-activation-source.ts +44 -0
  195. package/src/codex/desktop-compatibility/windows-certificate-trust.ts +81 -0
  196. package/src/codex/desktop-compatibility/windows-key-protection.ts +165 -0
  197. package/src/codex/desktop-compatibility/windows-package-command.ts +86 -0
  198. package/src/codex/desktop-compatibility/windows-package-launch.ts +28 -0
  199. package/src/codex/features.ts +85 -45
  200. package/src/codex/history-provider.ts +1 -1
  201. package/src/codex/inject/config-write-section.ts +4 -1
  202. package/src/codex/inject/history-failure.ts +27 -0
  203. package/src/codex/inject/remove.ts +18 -6
  204. package/src/codex/inject/restore.ts +24 -2
  205. package/src/codex/inject/routing-classify.ts +4 -3
  206. package/src/codex/inject.ts +18 -27
  207. package/src/codex/journal.ts +16 -1
  208. package/src/codex/main-account-hard-lock.ts +30 -1
  209. package/src/codex/main-account.ts +99 -15
  210. package/src/codex/native-main-refresh-mode.ts +217 -0
  211. package/src/codex/native-profile-startup.ts +1 -1
  212. package/src/codex/ocx-compaction-history.ts +12 -10
  213. package/src/codex/paths.ts +86 -1
  214. package/src/codex/provider-routing.ts +174 -0
  215. package/src/codex/refresh.ts +2 -2
  216. package/src/codex/routing/health-store.ts +2 -2
  217. package/src/codex/routing/pool-recovery.ts +79 -0
  218. package/src/codex/routing/selection.ts +4 -3
  219. package/src/codex/routing/thread-affinity.ts +1 -1
  220. package/src/codex/shim-diagnostics.ts +6 -0
  221. package/src/codex/shim-overlay.ts +7 -2
  222. package/src/codex/signed-in-identity.ts +43 -0
  223. package/src/codex/subagent-model-fallback.ts +1 -1
  224. package/src/codex/sync.ts +6 -1
  225. package/src/codex/user-identity.ts +16 -5
  226. package/src/codex/windows-local-appdata.ts +84 -0
  227. package/src/combos/failover.ts +6 -1
  228. package/src/combos/index.ts +1 -0
  229. package/src/combos/resolve.ts +46 -10
  230. package/src/combos/types.ts +15 -0
  231. package/src/config/diagnostics.ts +35 -7
  232. package/src/config/live-reconcile.ts +51 -2
  233. package/src/config/load-degrade.ts +23 -1
  234. package/src/config/opencode-wire-upgrade.ts +54 -0
  235. package/src/config/opencode-zen-upgrade.ts +52 -0
  236. package/src/config/owner-registry.ts +22 -17
  237. package/src/config/paths.ts +10 -3
  238. package/src/config/provider-validation.ts +45 -2
  239. package/src/config/schema/config-schema.ts +39 -0
  240. package/src/config/schema/desktop-compatibility.ts +9 -0
  241. package/src/config/schema/leaf-validators.ts +39 -9
  242. package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
  243. package/src/config/schema/native-reasoning-retention.ts +26 -0
  244. package/src/config.ts +2 -0
  245. package/src/generated/compatibility-version.json +962 -350
  246. package/src/generated/model-metadata.ts +1 -1
  247. package/src/grok/sync.ts +1 -1
  248. package/src/images/loop.ts +46 -8
  249. package/src/integrations/catalog-refresh.ts +1 -1
  250. package/src/integrations/config-io.ts +20 -2
  251. package/src/integrations/journal.ts +15 -1
  252. package/src/integrations/mutation-plan.ts +2 -0
  253. package/src/integrations/registry.ts +15 -0
  254. package/src/integrations/store.ts +3 -0
  255. package/src/integrations/writer.ts +10 -1
  256. package/src/lib/advisor-activation.ts +16 -0
  257. package/src/lib/background-work.ts +65 -0
  258. package/src/lib/bounded-file-read.ts +58 -0
  259. package/src/lib/bun-path-runtime.d.mts +10 -1
  260. package/src/lib/bun-path-runtime.mjs +62 -3
  261. package/src/lib/codex-restart-contract.ts +15 -0
  262. package/src/lib/desktop-proxy-route.ts +30 -0
  263. package/src/lib/desktop-upstream-tunnel.ts +72 -0
  264. package/src/lib/empty-tool-catalog.ts +62 -0
  265. package/src/lib/errors.ts +26 -3
  266. package/src/lib/gcp-adc.ts +96 -9
  267. package/src/lib/gcp-credential-input.ts +12 -0
  268. package/src/lib/lab-live-route-production.ts +5 -0
  269. package/src/lib/local-internal-call-capability.ts +55 -0
  270. package/src/lib/opaque-proxy-tunnel.ts +217 -0
  271. package/src/lib/package-tree-integrity.ts +2 -2
  272. package/src/lib/redact.ts +2 -2
  273. package/src/lib/socks5-fetch.ts +48 -71
  274. package/src/lib/socks5-handshake.ts +112 -0
  275. package/src/lib/translator-budget.ts +7 -1
  276. package/src/oauth/account-quota-rank.ts +69 -2
  277. package/src/oauth/anthropic-account-refusal.ts +20 -2
  278. package/src/oauth/anthropic-oauth-definitions.ts +3 -1
  279. package/src/oauth/anthropic-routing.ts +1 -21
  280. package/src/oauth/anthropic.ts +38 -7
  281. package/src/oauth/callback-server.ts +27 -1
  282. package/src/oauth/chatgpt-device.ts +1 -1
  283. package/src/oauth/chatgpt-shared.ts +83 -0
  284. package/src/oauth/chatgpt.ts +8 -72
  285. package/src/oauth/dsh.ts +203 -0
  286. package/src/oauth/generic-account-failover.ts +62 -12
  287. package/src/oauth/generic-session-affinity.ts +125 -0
  288. package/src/oauth/index.ts +188 -111
  289. package/src/oauth/kiro-device-login.ts +2 -1
  290. package/src/oauth/kiro.ts +7 -1
  291. package/src/oauth/local-token-detect.ts +21 -1
  292. package/src/oauth/login-cli-errors.ts +44 -0
  293. package/src/oauth/login-cli.ts +69 -7
  294. package/src/oauth/login-flow-state.ts +371 -23
  295. package/src/oauth/meta-muse-errors.ts +41 -0
  296. package/src/oauth/meta-muse-keychain.ts +52 -0
  297. package/src/oauth/meta-muse.ts +51 -84
  298. package/src/oauth/model-discovery-auth.ts +3 -0
  299. package/src/oauth/pool-settings-capability.ts +4 -2
  300. package/src/oauth/refresh-flight-retirement.ts +48 -0
  301. package/src/oauth/session-affinity-key.ts +23 -0
  302. package/src/oauth/store-anthropic-instance.ts +9 -2
  303. package/src/oauth/store.ts +12 -5
  304. package/src/protocols/codecs/chat-ir.ts +467 -0
  305. package/src/protocols/codecs/chat.ts +10 -1
  306. package/src/protocols/codecs/messages-ir.ts +500 -0
  307. package/src/protocols/encoders/adapter-events.ts +5 -4
  308. package/src/protocols/encoders/messages.ts +9 -6
  309. package/src/protocols/features.ts +10 -2
  310. package/src/protocols/path.ts +11 -1
  311. package/src/protocols/plan-snapshot.ts +39 -16
  312. package/src/protocols/plan.ts +9 -1
  313. package/src/protocols/provider-summary.ts +2 -1
  314. package/src/protocols/settings.ts +8 -0
  315. package/src/providers/antigravity-account-roster.ts +102 -0
  316. package/src/providers/antigravity-models.ts +85 -7
  317. package/src/providers/api-key-cooldowns.ts +49 -0
  318. package/src/providers/api-key-resolve.ts +11 -6
  319. package/src/providers/api-key-response-ownership.ts +57 -0
  320. package/src/providers/api-key-selection.ts +31 -6
  321. package/src/providers/api-keys.ts +214 -3
  322. package/src/providers/claude-cli-usage.ts +96 -0
  323. package/src/providers/dsh-account-destination.ts +31 -0
  324. package/src/providers/dsh-account-models.ts +41 -0
  325. package/src/providers/gcp-credential-cleanup.ts +59 -0
  326. package/src/providers/key-failover.ts +54 -50
  327. package/src/providers/key-store.ts +7 -2
  328. package/src/providers/model-rename-fields.ts +5 -0
  329. package/src/providers/model-rename-migration.ts +7 -5
  330. package/src/providers/openai-tiers-destination.ts +11 -2
  331. package/src/providers/openai-tiers.ts +5 -12
  332. package/src/providers/opencode-go-transport.ts +3 -1
  333. package/src/providers/quota/account-cache.ts +6 -5
  334. package/src/providers/quota/account-row.ts +34 -0
  335. package/src/providers/quota/antigravity.ts +28 -7
  336. package/src/providers/quota/dsh-account.ts +70 -0
  337. package/src/providers/quota/vendor-probes-oauth.ts +1 -93
  338. package/src/providers/quota-types.ts +2 -0
  339. package/src/providers/quota.ts +14 -20
  340. package/src/providers/registry/entries-core.ts +44 -14
  341. package/src/providers/registry/entries-extended.ts +19 -0
  342. package/src/providers/registry/types.ts +1 -1
  343. package/src/providers/request-pacing.ts +77 -19
  344. package/src/providers/routed-config.ts +339 -0
  345. package/src/providers/service-tier.ts +2 -1
  346. package/src/quota/window-mapping.ts +5 -2
  347. package/src/responses/anthropic-progress.ts +34 -0
  348. package/src/responses/assistant-replay.ts +35 -0
  349. package/src/responses/compaction-history-framing.ts +65 -0
  350. package/src/responses/compaction.ts +6 -1
  351. package/src/responses/incomplete-reason.ts +31 -0
  352. package/src/responses/parser.ts +78 -22
  353. package/src/responses/reasoning-envelope.ts +4 -0
  354. package/src/responses/reasoning-replay-cache.ts +24 -2
  355. package/src/responses/reasoning-retention.ts +173 -0
  356. package/src/responses/retained-compaction.ts +46 -0
  357. package/src/responses/schema.ts +23 -0
  358. package/src/responses/state.ts +32 -29
  359. package/src/router.ts +8 -321
  360. package/src/routing/health.ts +37 -22
  361. package/src/routing/history/build-file.ts +92 -0
  362. package/src/routing/history/indexer.ts +254 -221
  363. package/src/routing/history/ingest.ts +242 -0
  364. package/src/routing/history/rebuild-job.ts +111 -0
  365. package/src/routing/history/rebuild-protocol.ts +15 -0
  366. package/src/routing/history/rebuild-worker.ts +34 -0
  367. package/src/routing/profile.ts +1 -1
  368. package/src/server/admission-model-scope.ts +21 -1
  369. package/src/server/antigravity-direct-eligibility.ts +208 -0
  370. package/src/server/auth-cors.ts +15 -0
  371. package/src/server/chat-completions.ts +82 -10
  372. package/src/server/chat-native-reasoning.ts +123 -0
  373. package/src/server/chat-native-sse.ts +110 -7
  374. package/src/server/chat-native.ts +76 -17
  375. package/src/server/claude-messages.ts +164 -433
  376. package/src/server/effort-policy.ts +1 -1
  377. package/src/server/images.ts +15 -2
  378. package/src/server/index/desktop-compatibility-startup.ts +56 -0
  379. package/src/server/index/model-alias-id.ts +36 -0
  380. package/src/server/index/optional-listeners.ts +2 -0
  381. package/src/server/index/serve-options.ts +11 -3
  382. package/src/server/index/startup-warnings.ts +11 -0
  383. package/src/server/index/websocket-handler.ts +9 -0
  384. package/src/server/index.ts +14 -11
  385. package/src/server/inference/advisor-diagnostics.ts +31 -0
  386. package/src/server/inference/client-encoder-delivery.ts +6 -5
  387. package/src/server/login-shell-path.ts +146 -0
  388. package/src/server/management/advisor-routes.ts +202 -0
  389. package/src/server/management/agent-settings-routes.ts +22 -4
  390. package/src/server/management/claude-desktop-picker-routes.ts +1 -1
  391. package/src/server/management/combo-routes.ts +176 -50
  392. package/src/server/management/companion-routes.ts +4 -0
  393. package/src/server/management/config-routes.ts +77 -6
  394. package/src/server/management/context.ts +4 -0
  395. package/src/server/management/cursor-integration-routes.ts +1 -1
  396. package/src/server/management/desktop-compatibility-routes.ts +46 -0
  397. package/src/server/management/desktop-compatibility-runtime-routes.ts +46 -0
  398. package/src/server/management/desktop-compatibility-settings-routes.ts +27 -0
  399. package/src/server/management/model-routes.ts +13 -8
  400. package/src/server/management/model-rows.ts +14 -0
  401. package/src/server/management/native-integration-routes.ts +2 -7
  402. package/src/server/management/native-reasoning-retention-routes.ts +41 -0
  403. package/src/server/management/oauth-account-routes.ts +87 -14
  404. package/src/server/management/protocol-settings-patch.ts +1 -0
  405. package/src/server/management/provider-gcp-credential-routes.ts +29 -0
  406. package/src/server/management/provider-key-pacing-routes.ts +31 -0
  407. package/src/server/management/provider-overwrite-carry.ts +7 -0
  408. package/src/server/management/provider-routes.ts +25 -30
  409. package/src/server/management/route-registry.ts +21 -1
  410. package/src/server/management/sibling-guard.ts +1 -0
  411. package/src/server/management-api.ts +9 -1
  412. package/src/server/messages-antigravity-direct.ts +195 -0
  413. package/src/server/messages-native-tool-guard.ts +185 -0
  414. package/src/server/messages-native.ts +106 -22
  415. package/src/server/messages-shared.ts +431 -0
  416. package/src/server/relay.ts +1 -1
  417. package/src/server/request-log-terminal-status.ts +1 -1
  418. package/src/server/request-log.ts +13 -1
  419. package/src/server/responses/account-change-state.ts +8 -2
  420. package/src/server/responses/adapter-continuation.ts +48 -19
  421. package/src/server/responses/adapter-delivery.ts +43 -8
  422. package/src/server/responses/adapter-dispatch.ts +48 -4
  423. package/src/server/responses/advisor-plan-slot.ts +27 -0
  424. package/src/server/responses/advisor-retention.ts +117 -0
  425. package/src/server/responses/advisor-slot.ts +368 -0
  426. package/src/server/responses/code-mode-collab-surface.ts +116 -8
  427. package/src/server/responses/codex-ws-wire.ts +15 -8
  428. package/src/server/responses/compact.ts +303 -125
  429. package/src/server/responses/compaction-recovery.ts +18 -10
  430. package/src/server/responses/compaction-retention.ts +61 -0
  431. package/src/server/responses/context-overflow.ts +11 -6
  432. package/src/server/responses/core-auth.ts +6 -0
  433. package/src/server/responses/core-codex-account.ts +11 -1
  434. package/src/server/responses/core-combo-failure.ts +12 -1
  435. package/src/server/responses/core-combo.ts +141 -36
  436. package/src/server/responses/core-errors.ts +3 -3
  437. package/src/server/responses/core-lifetime.ts +7 -2
  438. package/src/server/responses/core-normalize.ts +11 -0
  439. package/src/server/responses/core-opaque-recovery.ts +35 -0
  440. package/src/server/responses/core-options.ts +51 -0
  441. package/src/server/responses/core-pipeline.ts +141 -0
  442. package/src/server/responses/core-replay.ts +25 -6
  443. package/src/server/responses/core.ts +3 -116
  444. package/src/server/responses/fetch-helpers.ts +73 -19
  445. package/src/server/responses/main-hard-lock-routing.ts +109 -0
  446. package/src/server/responses/native-response-control.ts +4 -2
  447. package/src/server/responses/passthrough-delivery.ts +19 -11
  448. package/src/server/responses/passthrough-dispatch.ts +29 -16
  449. package/src/server/responses/request-prepare.ts +144 -17
  450. package/src/server/responses/request-sidecar-auth.ts +2 -2
  451. package/src/server/responses/request-transport.ts +120 -27
  452. package/src/server/responses/response-effects.ts +1 -0
  453. package/src/server/responses/run-turn-execution.ts +166 -38
  454. package/src/server/responses/sidecar-execution.ts +98 -5
  455. package/src/server/responses/terminal-guard.ts +4 -17
  456. package/src/server/responses-undeclared-tool-guard.ts +30 -9
  457. package/src/server/startup-action-control.ts +16 -2
  458. package/src/server/startup-health-cache.ts +1 -0
  459. package/src/service/auth-preflight.ts +71 -0
  460. package/src/service/cli.ts +4 -2
  461. package/src/service/definition.ts +97 -0
  462. package/src/service/desktop-startup.ts +14 -0
  463. package/src/service/guards.ts +1 -61
  464. package/src/service/health-sentinel-linux.ts +112 -0
  465. package/src/service/health-sentinel-main.ts +99 -61
  466. package/src/service/health-sentinel-probe.ts +24 -7
  467. package/src/service/health.ts +5 -89
  468. package/src/service/launchd.ts +27 -7
  469. package/src/service/logging.ts +135 -0
  470. package/src/service/repair.ts +2 -1
  471. package/src/service/state.ts +7 -8
  472. package/src/service/systemd.ts +1 -1
  473. package/src/service/windows-guarded-manager.ts +2 -1
  474. package/src/service/windows-ops.ts +1 -1
  475. package/src/service/windows-scheduler.ts +2 -7
  476. package/src/service/windows-taskxml.ts +2 -2
  477. package/src/service/windows-wscript.ts +14 -0
  478. package/src/service.ts +4 -2
  479. package/src/sidecar/auth.ts +2 -0
  480. package/src/tray/windows-tray.ps1 +43 -24
  481. package/src/types/config.ts +124 -6
  482. package/src/types/opencode-go.ts +61 -0
  483. package/src/types/opencode-zen.ts +29 -0
  484. package/src/types/provider.ts +39 -6
  485. package/src/types/request.ts +38 -2
  486. package/src/types/tools.ts +2 -0
  487. package/src/types/wire.ts +29 -37
  488. package/src/types.ts +1 -0
  489. package/src/update/async-check.ts +1 -1
  490. package/src/update/badge.ts +2 -2
  491. package/src/update/index.ts +88 -15
  492. package/src/update/job-supervised-restart.ts +107 -0
  493. package/src/update/job.ts +32 -32
  494. package/src/update/notify.ts +1 -1
  495. package/src/update/npm-invocation.mjs +12 -2
  496. package/src/update/refresh-scheduler.ts +2 -2
  497. package/src/update/supervised-recovery.d.mts +27 -0
  498. package/src/update/supervised-recovery.mjs +157 -0
  499. package/src/usage/cache-diagnostic.ts +2 -0
  500. package/src/usage/cost.ts +48 -12
  501. package/src/usage/expected-prices.ts +15 -0
  502. package/src/usage/ledger-retention.ts +2 -1
  503. package/src/usage/ledger-scanner.ts +6 -5
  504. package/src/usage/log.ts +17 -8
  505. package/src/usage/summary.ts +22 -9
  506. package/src/usage/user-cost-overlays.ts +14 -19
  507. package/src/usage/user-cost-validation.ts +38 -0
  508. package/src/vision/eligibility.ts +13 -0
  509. package/src/web-search/gemini-executor.ts +2 -2
  510. package/src/web-search/loop.ts +55 -5
  511. package/src/web-search/progress-stream.ts +4 -1
  512. package/src/web-search/run-turn-loop.ts +9 -1
  513. package/assets/sponsors/packycode.png +0 -0
  514. package/gui/dist/assets/App-CSJvmjNs.css +0 -1
  515. package/gui/dist/assets/App-CZtqiGcw.js +0 -56
  516. package/gui/dist/assets/index-BB6uK6tU.css +0 -1
  517. package/gui/dist/assets/index-CTm4l-HQ.js +0 -94
  518. package/gui/dist/assets/quota-summary-BbEeRfPY.js +0 -1
@@ -23,13 +23,14 @@ import { normalizeLogConversationId } from "../request-log-conversation";
23
23
  import { normalizeDeclaredToolName, type AdapterEvent, type OcxProviderContinuationState } from "../../types";
24
24
  import { adapterFailureFromMessage, SEND_BUDGET_EXHAUSTED_CODE, SPEND_LEDGER_STORAGE_UNAVAILABLE_CODE, type OcxErrorPayload } from "../../lib/errors";
25
25
  import { SendBudgetExhaustedError, markResponseNonReplayable } from "../../lib/upstream-retry";
26
+ import { hasKeyPoolFailover, rotateProviderTransportOn401, rotateProviderTransportOn429 } from "../../providers/key-failover";
26
27
  import {
27
28
  hasEligibleGenericOAuthFailoverTarget,
28
29
  isGenericOAuthFailoverEnabled,
29
30
  rotateGenericOAuthAccountOn429,
30
31
  failoverAccountSnapshot,
31
32
  } from "../../oauth/generic-account-failover";
32
- import { OAuthAccountPausedError, publicOAuthAuthenticationErrorMessage, type OAuthAccessSnapshot } from "../../oauth/index";
33
+ import { OAuthAccountPausedError, OAuthTokenRefreshBusyError, publicOAuthAuthenticationErrorMessage, type OAuthAccessSnapshot } from "../../oauth/index";
33
34
  import { tryAlternateAfterTerminalRefresh } from "../../oauth/kiro-terminal-failover";
34
35
  import { resolveWireProtocolOverride } from "../adapter-resolve";
35
36
  import { formatErrorResponse, bridgeToResponsesSSE, buildResponseJSON } from "../../bridge";
@@ -48,12 +49,14 @@ import { rememberResponseState } from "../../responses/state";
48
49
  import { trackStreamLifetime } from "../lifecycle";
49
50
  import { awaitThoughtSignatureDurability } from "../../responses/thought-signature-replay";
50
51
  import { undeclaredToolCallMessage } from "../responses-undeclared-tool-guard";
52
+ import { emptyToolCatalogRefusal, guardEmptyToolCatalogEvents, guardEmptyToolCatalogStream } from "../../lib/empty-tool-catalog";
51
53
  // LOCAL PATCH (runturn-websearch)
52
54
  import { planWebSearch } from "../../web-search";
53
55
  import { runTurnWebSearchInitialParsed, runTurnWebSearchLoop } from "../../web-search/run-turn-loop";
54
56
  import { WEB_SEARCH_TOOL_NAME } from "../../web-search/synthetic-tool";
55
57
  import { orderDevinMessagesOutput } from "../../claude/devin-output-order";
56
58
  import { unboundPoolSpendRefusalMessage } from "../workflow-refusal";
59
+ import { claudeUsagePreflight } from "../../adapters/claude-cli/usage-admission";
57
60
 
58
61
  // LOCAL PATCH (runturn-websearch): top-level fields route binding or the
59
62
  // adapter itself may write during a turn. Iteration-local `turnParsed` objects
@@ -70,13 +73,16 @@ const RUNTURN_WS_ROUTE_STATE_KEYS = [
70
73
  "_providerContinuationOwner",
71
74
  "_providerContinuationCandidate",
72
75
  "_stripReasoningEncryptedContent",
76
+ "_stripNativeCompactionEncryptedContent",
77
+ "_nativeReasoningRetention",
78
+ "_nativeReasoningRetainedAcrossRoute",
73
79
  "_dropForeignReasoningItemIds",
74
80
  "_reasoningReplayScope",
75
81
  ] as const;
76
82
 
77
83
  /** One responsibility of the Responses request pipeline; state owners are explicit. */
78
84
  export async function executeResponsesRunTurn(
79
- requestContext: Pick<ResponsesRequestContext, "options" | "logCtx" | "config">,
85
+ requestContext: Pick<ResponsesRequestContext, "req" | "options" | "logCtx" | "config">,
80
86
  admissionState: ResponsesAdmissionState,
81
87
  requestState: Pick<
82
88
  PreparedResponsesRequest,
@@ -86,6 +92,7 @@ export async function executeResponsesRunTurn(
86
92
  | "translatorBudget"
87
93
  | "inboundWire"
88
94
  | "toolBridgeMaps"
95
+ | "clientToolCatalogRestriction"
89
96
  | "rememberKiroDeliveredFinalAnswer"
90
97
  | "responseStateOptions"
91
98
  >,
@@ -126,7 +133,7 @@ export async function executeResponsesRunTurn(
126
133
  >,
127
134
  completionPolicy: Pick<ResponsesCompletionPolicy, "emptyCompletionGuardEnabled">,
128
135
  ): Promise<Response> {
129
- const { options, logCtx, config } = requestContext;
136
+ const { req, options, logCtx, config } = requestContext;
130
137
  const {
131
138
  selectionIsCurrent,
132
139
  adapterBindings,
@@ -168,6 +175,33 @@ export async function executeResponsesRunTurn(
168
175
  const releaseSearchProbeLease = (): void => {
169
176
  sidecarState.openAiSidecar?.releaseProbeLease?.();
170
177
  };
178
+ // Publish the known subscription pause before headers or a CLI turn, preserving its reset
179
+ // across both Responses and translated Chat instead of presenting a retryable generic 502.
180
+ if (transportState.runTurnAdapter.name === "claude-cli") {
181
+ let usage: Awaited<ReturnType<typeof claudeUsagePreflight>>;
182
+ try {
183
+ usage = await claudeUsagePreflight(parsed.modelId);
184
+ } catch (error) {
185
+ // The outer cleanup does not own the sidecar's probe lease: hand it back before rethrowing.
186
+ releaseSearchProbeLease();
187
+ throw error;
188
+ }
189
+ if (usage.state === "exhausted") {
190
+ cancelResponseCompletion();
191
+ releaseSearchProbeLease();
192
+ const refusal = new Response(JSON.stringify({ error: {
193
+ type: "rate_limit_error", code: "claude_subscription_cooldown", param: null,
194
+ message: usage.message ?? "Claude subscription usage is exhausted.",
195
+ } }), {
196
+ status: 429,
197
+ headers: { "Content-Type": "application/json",
198
+ ...(usage.resetAt ? { "Retry-After": String(Math.max(1, Math.ceil((usage.resetAt - Date.now()) / 1000))) } : {}),
199
+ },
200
+ });
201
+ markResponseNonReplayable(refusal);
202
+ return refusal;
203
+ }
204
+ }
171
205
  // When Codex declared hosted web_search and a sidecar plan resolves, drive
172
206
  // the routed model through the same search interception the fetch-path loop
173
207
  // runs — injected as a function tool, calls intercepted, results appended to
@@ -185,7 +219,9 @@ export async function executeResponsesRunTurn(
185
219
  }
186
220
 
187
221
  const runTurnAbort = new AbortController();
188
- const cleanupRunTurnAbort = linkAbortSignal(runTurnAbort, options.abortSignal);
222
+ const cleanupRequestAbort = linkAbortSignal(runTurnAbort, req.signal);
223
+ const cleanupOptionAbort = linkAbortSignal(runTurnAbort, options.abortSignal);
224
+ const cleanupRunTurnAbort = (): void => { cleanupRequestAbort(); cleanupOptionAbort(); };
189
225
  const devinProducers = new Set<AbortController>();
190
226
  const orderMessagesOutput = (source: AsyncIterable<AdapterEvent>): AsyncIterable<AdapterEvent> =>
191
227
  inboundWire === "anthropic" && transportState.runTurnAdapter.name === "devin" && !routedCompaction
@@ -196,12 +232,13 @@ export async function executeResponsesRunTurn(
196
232
  const queue = createAdapterEventQueue({
197
233
  onBacklogExceeded: () => runTurnAbort.abort(),
198
234
  });
199
- const refreshRunTurnSelection = async (): Promise<void> => {
200
- if (selectionIsCurrent(adapterBindings.get(transportState.runTurnAdapter))) return;
235
+ const refreshRunTurnSelection = async (): Promise<boolean> => {
236
+ if (selectionIsCurrent(adapterBindings.get(transportState.runTurnAdapter))) return false;
201
237
  await refreshRunTurnAdapter(parsed);
202
238
  bindRouteReasoningReplayScope({ parsed, providerName: route.providerName, provider: route.provider,
203
239
  adapterName: transportState.runTurnAdapter.name, oauthCredentialSnapshot: transportState.replayOAuthCredentialSnapshot });
204
240
  sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, transportState.runTurnAdapter.name, logCtx.accountLogLabel);
241
+ return true;
205
242
  };
206
243
  // Initial admission must settle before the streaming Response commits HTTP 200.
207
244
  // Let the outer Responses facade preserve the local retryable-429 contract.
@@ -224,6 +261,11 @@ export async function executeResponsesRunTurn(
224
261
  // in-flight; only the latest dispatch may merge adapter-written route
225
262
  // state back onto `parsed`, or the superseded attempt would restore the
226
263
  // failed account's cursor/continuation over the rotation's rebind.
264
+ type KeyBinding = Extract<NonNullable<ReturnType<typeof adapterBindings.get>>, { kind: "api-key" }>;
265
+ const failedKeyBindings = new WeakMap<Extract<AdapterEvent, { type: "error" }>, KeyBinding>();
266
+ const failedKeys = new Set<string>();
267
+ const keyAttemptLimit = route.provider.apiKeyPool?.length ?? 0;
268
+ let lastKeyFailure: Extract<AdapterEvent, { type: "error" }> | undefined;
227
269
  let runTurnAttemptSeq = 0;
228
270
  const runTurnAttempt = async (
229
271
  targetQueue: AdapterEventQueue,
@@ -238,7 +280,9 @@ export async function executeResponsesRunTurn(
238
280
  const attemptSeq = ++runTurnAttemptSeq;
239
281
  if (attemptSeq === 1 && options.comboInitialSend) options.comboInitialSend.producerOwned = true;
240
282
  let pacingSlot = preacquiredSlot;
283
+ let sentKeyBinding: KeyBinding | undefined;
241
284
  const emit = (event: AdapterEvent) => {
285
+ if (event.type === "error" && sentKeyBinding) failedKeyBindings.set(event, sentKeyBinding);
242
286
  options.onCompactionRecoveryAdapterEvent?.(event);
243
287
  targetQueue.push(event);
244
288
  };
@@ -248,7 +292,18 @@ export async function executeResponsesRunTurn(
248
292
  if (!pacingSlotAcquired) {
249
293
  pacingSlot = await waitForProviderRequestSlot(route.providerName, route.provider, route.modelId, runTurnAbort.signal);
250
294
  }
251
- await refreshRunTurnSelection();
295
+ const selectionChanged = await refreshRunTurnSelection();
296
+ const selectedBinding = adapterBindings.get(transportState.runTurnAdapter);
297
+ if (lastKeyFailure && selectedBinding?.kind === "api-key"
298
+ && failedKeys.has(selectedBinding.provider.apiKey ?? "")) {
299
+ emit(lastKeyFailure);
300
+ return;
301
+ }
302
+ if (selectionChanged) {
303
+ releaseProviderRequestSlot(pacingSlot);
304
+ pacingSlot = undefined;
305
+ pacingSlot = await waitForProviderRequestSlot(route.providerName, route.provider, route.modelId, runTurnAbort.signal);
306
+ }
252
307
  // LOCAL PATCH (runturn-websearch): refreshRunTurnSelection binds route
253
308
  // state onto `parsed`; mirror it onto the iteration-local copy the
254
309
  // adapter actually receives.
@@ -293,13 +348,14 @@ export async function executeResponsesRunTurn(
293
348
  // The only way the request budget reaches a transport the adapter owns. Without it
294
349
  // a Cursor turn's inner ladder was three physical sends the cap read as one.
295
350
  ...(adapterDispatchBudget ? { sendBudget: adapterDispatchBudget } : {}),
296
- onPhysicalSend: send => noteAdapterPhysicalSend(
297
- logCtx.usageLogInputTokens,
298
- // The attempt's own recovery kind still labels its first send when the adapter
299
- // does not supply one of its own.
300
- { ...send, ...(send.recovery ?? recovery ? { recovery: send.recovery ?? recovery } : {}) },
301
- { includeFirst: reportsOwnSends },
302
- ),
351
+ onPhysicalSend: send => {
352
+ // Capture once at this attempt's send, before later selection or recovery changes it.
353
+ const binding = adapterBindings.get(transportState.runTurnAdapter);
354
+ if (!sentKeyBinding && binding?.kind === "api-key") sentKeyBinding = binding;
355
+ noteAdapterPhysicalSend(logCtx.usageLogInputTokens,
356
+ { ...send, ...(send.recovery ?? recovery ? { recovery: send.recovery ?? recovery } : {}) },
357
+ { includeFirst: reportsOwnSends });
358
+ },
303
359
  onRecoveryWithheld: noteAdapterRecoveryWithheld,
304
360
  },
305
361
  emit,
@@ -361,6 +417,7 @@ export async function executeResponsesRunTurn(
361
417
  const runTurn = async (): Promise<void> => runTurnAttempt(queue, undefined, true, wsFirstParsed, initialPacingSlot);
362
418
  const runTurnFailoverArmed = () =>
363
419
  route.provider.authMode === "oauth"
420
+ || (transportState.runTurnAdapter.name === "qoder" && hasKeyPoolFailover(route.provider))
364
421
  || !!(transportState.genericFailoverAccountId
365
422
  && isGenericOAuthFailoverEnabled(config, route.providerName));
366
423
  const dispatchSearchIteration = (iterParsed: PreparedResponsesRequest["parsed"]): AsyncIterable<AdapterEvent> => {
@@ -420,7 +477,19 @@ export async function executeResponsesRunTurn(
420
477
  // selected; the turn then moves to a surviving stored account, or, with none, the client
421
478
  // gets the login instruction instead of an opaque upstream 401.
422
479
  let oauth401ReplayAttempted = false;
423
- let pausedAuthRecovery = false;
480
+ let authRecoveryRefusal: Response | undefined;
481
+ const refuseLocalAuthRecovery = (error: Extract<AdapterEvent, { type: "error" }>, cause: unknown): boolean => {
482
+ const cancelled = runTurnAbort.signal.aborted;
483
+ if (!cancelled && !(cause instanceof OAuthTokenRefreshBusyError) && !(cause instanceof OAuthAccountPausedError)) return false;
484
+ const status = cancelled ? 499 : cause instanceof OAuthTokenRefreshBusyError ? 503 : 403;
485
+ const errorType = cancelled ? "client_cancelled" : status === 503 ? "server_error" : "permission_error";
486
+ const message = cancelled ? "Client cancelled request" : publicOAuthAuthenticationErrorMessage(cause);
487
+ // If a readiness heartbeat already committed SSE, the mapped event remains retryable
488
+ // capacity rather than retaining the rejected credential's authentication error/code.
489
+ Object.assign(error, { status, errorType, message, code: undefined, retryable: status === 503 });
490
+ authRecoveryRefusal = formatErrorResponse(status, errorType, message, status === 503 ? { retryAfter: "1" } : undefined);
491
+ return true;
492
+ };
424
493
  const recoverRunTurnAdapterOnPreflight401 = async (
425
494
  error: Extract<AdapterEvent, { type: "error" }>,
426
495
  ): Promise<boolean> => {
@@ -434,9 +503,7 @@ export async function executeResponsesRunTurn(
434
503
  try {
435
504
  admitted = await applyFailoverSnapshot(await refreshResolvedOAuthSelection(sent));
436
505
  } catch (err) {
437
- if (err instanceof OAuthAccountPausedError) {
438
- pausedAuthRecovery = true;
439
- Object.assign(error, { status: 403, errorType: "permission_error", message: publicOAuthAuthenticationErrorMessage(err) });
506
+ if (refuseLocalAuthRecovery(error, err)) {
440
507
  hop.permit?.release();
441
508
  return false;
442
509
  }
@@ -459,14 +526,58 @@ export async function executeResponsesRunTurn(
459
526
  } catch (err) {
460
527
  // Applying the alternate can still fail, e.g. when a newer manual selection points back
461
528
  // at the flagged account; the client then needs the login instruction, not the raw 401.
462
- Object.assign(error, { errorType: "authentication_error", message: publicOAuthAuthenticationErrorMessage(err) });
529
+ if (!refuseLocalAuthRecovery(error, err)) {
530
+ Object.assign(error, { errorType: "authentication_error", message: publicOAuthAuthenticationErrorMessage(err) });
531
+ }
463
532
  hop.permit?.release();
464
533
  return false;
465
534
  }
466
535
  };
536
+ const recoverRunTurnKey = async (
537
+ error: Extract<AdapterEvent, { type: "error" }>,
538
+ ): Promise<AttemptRecoveryKind | undefined> => {
539
+ const binding = failedKeyBindings.get(error);
540
+ if (error.apiKeyRecovery !== true || (error.status !== 401 && error.status !== 429)
541
+ || !binding || runTurnAbort.signal.aborted || !hasKeyPoolFailover(route.provider)
542
+ || failedKeys.size >= keyAttemptLimit) return undefined;
543
+ const failed = binding.provider;
544
+ if (!failed.apiKey || !failed._apiKeyAttempt || failedKeys.has(failed.apiKey)) return undefined;
545
+ const hop = reserveCredentialHop("auth-recovery", `${route.providerName}|${route.modelId}|runturn-key-${error.status}`);
546
+ if (!hop.allowed) return undefined;
547
+ failedKeys.add(failed.apiKey);
548
+ lastKeyFailure = error;
549
+ try {
550
+ const rotate = error.status === 401 ? rotateProviderTransportOn401 : rotateProviderTransportOn429;
551
+ const rotated = rotate(config, route.providerName, failed, {
552
+ attemptedKey: failed.apiKey, attemptedSelection: failed._apiKeyAttempt,
553
+ promptCacheKey: parsed.options.promptCacheKey,
554
+ });
555
+ if (!rotated || failedKeys.size >= keyAttemptLimit || failedKeys.has(rotated.apiKey ?? "") || runTurnAbort.signal.aborted) {
556
+ hop.permit?.release();
557
+ return undefined;
558
+ }
559
+ route.provider = rotated;
560
+ const adapter = await refreshRunTurnAdapter(parsed);
561
+ if (!adapter.runTurn || failedKeys.has(route.provider.apiKey ?? "") || runTurnAbort.signal.aborted) {
562
+ hop.permit?.release();
563
+ return undefined;
564
+ }
565
+ bindRouteReasoningReplayScope({ parsed, providerName: route.providerName, provider: route.provider,
566
+ adapterName: adapter.name, forwardHeaders: requestState.selectedForwardHeaders });
567
+ sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, adapter.name, logCtx.accountLogLabel);
568
+ recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, adapter.name);
569
+ sendBudgetState.pendingHopPermit = hop.permit;
570
+ return error.status === 401 ? "key-401" : "key-429";
571
+ } catch {
572
+ hop.permit?.release();
573
+ return undefined;
574
+ }
575
+ };
467
576
  const recoverRunTurnAdapterOnPreflightError = async (
468
577
  error: Extract<AdapterEvent, { type: "error" }>,
469
578
  ): Promise<AttemptRecoveryKind | undefined> => {
579
+ const keyRecovery = await recoverRunTurnKey(error);
580
+ if (keyRecovery) return keyRecovery;
470
581
  if (await recoverRunTurnAdapterOnPreflight401(error)) return "oauth-401";
471
582
  if (await rotateRunTurnAdapterOnPreflight429(error)) return "oauth-account-429";
472
583
  return undefined;
@@ -641,14 +752,19 @@ export async function executeResponsesRunTurn(
641
752
  return orderMessagesOutput(retryQueue.stream());
642
753
  };
643
754
 
644
- const { toolNsMap, declaredToolNames, toolParameterSchemas, freeformToolNames, bareCustomToolNames, toolSearchToolNames } = toolBridgeMaps;
755
+ const clientToolCatalog = requestState.clientToolCatalogRestriction && route.provider.authMode !== "forward"
756
+ ? requestState.clientToolCatalogRestriction : undefined;
757
+ const { toolNsMap, declaredToolNames, toolParameterSchemas, freeformToolNames, bareCustomToolNames, toolSearchToolNames } = clientToolCatalog
758
+ ? { ...toolBridgeMaps, ...clientToolCatalog } : toolBridgeMaps;
645
759
  const enforceDeclaredToolNames = inboundWire !== "chat" && inboundWire !== "anthropic";
646
760
  const classifyUndeclaredFirstTool = (
647
761
  event: AdapterEvent,
648
762
  ): Extract<AdapterEvent, { type: "error" }> | undefined => {
649
- if (!enforceDeclaredToolNames || event.type !== "tool_call_start") return undefined;
650
763
  // This tool is declared to the adapter by the private search loop.
651
- if (wsPlan && event.name === WEB_SEARCH_TOOL_NAME) return undefined;
764
+ if (wsPlan && event.type === "tool_call_start" && event.name === WEB_SEARCH_TOOL_NAME) return undefined;
765
+ const emptyCatalogRefusal = emptyToolCatalogRefusal(event, clientToolCatalog);
766
+ if (emptyCatalogRefusal) return emptyCatalogRefusal;
767
+ if (!enforceDeclaredToolNames || event.type !== "tool_call_start") return undefined;
652
768
  const effectiveName = normalizeDeclaredToolName(event.name, declaredToolNames, undefined, bareCustomToolNames);
653
769
  if (declaredToolNames.has(effectiveName)) return undefined;
654
770
  return {
@@ -683,20 +799,24 @@ export async function executeResponsesRunTurn(
683
799
  const stallTimeoutSec = wsPlan?.stallTimeoutSec ?? config.stallTimeoutSec;
684
800
  // A disabled watchdog (0) is not a zero preflight: it would commit SSE before Devin's first
685
801
  // event and deliver a pre-output 429 in-stream. Fall back to the default finite bound.
686
- const preflightDeadlineAt = grokDevinPreflight
802
+ // A key-pool preflight must not outlast the stall budget, or a silent CLI holds the SSE
803
+ // Response (heartbeats, stall watchdog) hostage; the OAuth preflight wait is unchanged.
804
+ const keyPoolPreflight = transportState.runTurnAdapter.name === "qoder"
805
+ && route.provider.authMode !== "oauth" && hasKeyPoolFailover(route.provider);
806
+ const preflightDeadlineAt = grokDevinPreflight || keyPoolPreflight
687
807
  ? Date.now() + (resolveStallTimeoutSec(stallTimeoutSec) || DEFAULT_STALL_TIMEOUT_SEC) * 1_000
688
808
  : undefined;
689
809
  if (runTurnFailoverArmed()) {
690
810
  // Preflight holds only heartbeats and the first meaningful event. A first-event 429 can be
691
811
  // replayed transparently; after any output reaches the bridge, a later error stays terminal.
692
812
  eventSource = await preflightRunTurnFailover(eventSource, wsFirstParsed, preflightDeadlineAt);
693
- if (pausedAuthRecovery) {
813
+ if (authRecoveryRefusal) {
694
814
  cancelResponseCompletion();
695
815
  runTurnAbort.abort();
696
816
  queue.close();
697
817
  cleanupRunTurnAbort();
698
818
  releaseSearchProbeLease();
699
- return formatErrorResponse(403, "permission_error", publicOAuthAuthenticationErrorMessage(new OAuthAccountPausedError()));
819
+ return authRecoveryRefusal;
700
820
  }
701
821
  }
702
822
  if (grokDevinPreflight) {
@@ -714,9 +834,13 @@ export async function executeResponsesRunTurn(
714
834
  if (preflight.error || preflight.empty) {
715
835
  runTurnAbort.abort();
716
836
  queue.close();
717
- const message = preflight.error?.message ?? "Adapter ended before producing a response";
718
- const failure = formatErrorResponse(preflight.error?.code === SPEND_LEDGER_STORAGE_UNAVAILABLE_CODE ? 503 : 502,
719
- preflight.error?.code === SPEND_LEDGER_STORAGE_UNAVAILABLE_CODE ? SPEND_LEDGER_STORAGE_UNAVAILABLE_CODE : "upstream_error", redactSecretString(message));
837
+ const classified = adapterFailureFromEvent(preflight.error ?? {
838
+ type: "error", status: 502, message: "Adapter ended before producing a response",
839
+ });
840
+ const storageRefusal = preflight.error?.code === SPEND_LEDGER_STORAGE_UNAVAILABLE_CODE;
841
+ const failure = formatErrorResponse(storageRefusal ? 503 : classified.httpStatus,
842
+ storageRefusal ? SPEND_LEDGER_STORAGE_UNAVAILABLE_CODE : classified.error.type,
843
+ redactSecretString(classified.error.message), { code: classified.error.code, classified: true });
720
844
  // A replay-unsafe heartbeat means the adapter already ran a local side effect, so the
721
845
  // combo must not send this turn to another target: the failure stays with this child.
722
846
  if (preflight.replayUnsafe) markResponseNonReplayable(failure);
@@ -762,7 +886,7 @@ export async function executeResponsesRunTurn(
762
886
  console.warn(emptyCompletionNotice(route.providerName, route.modelId));
763
887
  });
764
888
  const sseStream = bridgeToResponsesSSE(
765
- guardedSource, parsed._responseModelId ?? parsed.modelId, toolNsMap, freeformToolNames, toolSearchToolNames,
889
+ guardEmptyToolCatalogStream(guardedSource, clientToolCatalog, translatorBudget), parsed._responseModelId ?? parsed.modelId, toolNsMap, freeformToolNames, toolSearchToolNames,
766
890
  () => {
767
891
  cancelResponseCompletion();
768
892
  runTurnAbort.abort();
@@ -778,9 +902,11 @@ export async function executeResponsesRunTurn(
778
902
  declaredToolNames,
779
903
  bareCustomToolNames,
780
904
  enforceDeclaredToolNames,
905
+ // Same wire split: only a Responses client (Codex) needs the refusal as a failure.
906
+ terminalRefusalAsFailed: enforceDeclaredToolNames,
781
907
  toolParameterSchemas,
782
908
  ...(options.onFirstOutput ? { onFirstOutput: options.onFirstOutput } : {}),
783
- ...(routedCompaction ? { compaction: true } : {}),
909
+ ...(routedCompaction ? { compaction: true, compactionRetention: parsed._compactionRetention } : {}),
784
910
  // grok-build's strict decoder dies on the typed response.heartbeat frame; its
785
911
  // eventsource layer tolerates comment keep-alives. Codex needs the opposite.
786
912
  ...(logCtx.surface === "grok" ? { heartbeatStyle: "comment" as const } : {}),
@@ -835,13 +961,13 @@ export async function executeResponsesRunTurn(
835
961
  for await (const event of await preflightRunTurnFailover(
836
962
  (async function* () { yield* firstAttemptEvents; })(),
837
963
  )) runTurnEvents.push(event);
838
- if (pausedAuthRecovery) {
964
+ if (authRecoveryRefusal) {
839
965
  cancelResponseCompletion();
840
966
  runTurnAbort.abort();
841
967
  queue.close();
842
968
  cleanupRunTurnAbort();
843
969
  releaseSearchProbeLease();
844
- return formatErrorResponse(403, "permission_error", publicOAuthAuthenticationErrorMessage(new OAuthAccountPausedError()));
970
+ return authRecoveryRefusal;
845
971
  }
846
972
  }
847
973
  if (grokDevinPreflight) {
@@ -899,6 +1025,7 @@ export async function executeResponsesRunTurn(
899
1025
  }
900
1026
  events = searched;
901
1027
  }
1028
+ events = guardEmptyToolCatalogEvents(events, clientToolCatalog, translatorBudget);
902
1029
  if (options.comboAttempt) {
903
1030
  const firstMeaningfulIndex = events.findIndex(event => event.type !== "heartbeat");
904
1031
  const firstMeaningful = firstMeaningfulIndex === -1 ? undefined : events[firstMeaningfulIndex];
@@ -912,12 +1039,13 @@ export async function executeResponsesRunTurn(
912
1039
  ? classifyUndeclaredFirstTool(firstMeaningful)
913
1040
  : undefined;
914
1041
  if (!firstMeaningful || firstMeaningful.type === "error" || classifiedError) {
915
- const message = classifiedError?.message ?? (firstMeaningful?.type === "error"
916
- ? firstMeaningful.message
917
- : "Adapter ended before producing a response");
1042
+ const classified = adapterFailureFromEvent(classifiedError ?? (firstMeaningful?.type === "error"
1043
+ ? firstMeaningful
1044
+ : { type: "error", status: 502, message: "Adapter ended before producing a response" }));
918
1045
  const storageRefusal = firstMeaningful?.type === "error" && firstMeaningful.code === SPEND_LEDGER_STORAGE_UNAVAILABLE_CODE;
919
- const failure = formatErrorResponse(storageRefusal ? 503 : 502,
920
- storageRefusal ? SPEND_LEDGER_STORAGE_UNAVAILABLE_CODE : "upstream_error", redactSecretString(message));
1046
+ const failure = formatErrorResponse(storageRefusal ? 503 : classified.httpStatus,
1047
+ storageRefusal ? SPEND_LEDGER_STORAGE_UNAVAILABLE_CODE : classified.error.type,
1048
+ redactSecretString(classified.error.message), { code: classified.error.code, classified: true });
921
1049
  if (replayUnsafe) markResponseNonReplayable(failure);
922
1050
  return failure;
923
1051
  }
@@ -935,7 +1063,7 @@ export async function executeResponsesRunTurn(
935
1063
  freeformToolNames,
936
1064
  bareCustomToolNames,
937
1065
  toolSearchToolNames,
938
- ...(routedCompaction ? { compaction: true } : {}),
1066
+ ...(routedCompaction ? { compaction: true, compactionRetention: parsed._compactionRetention } : {}),
939
1067
  onProviderState: state => { providerState = state; },
940
1068
  onUsage: usage => {
941
1069
  transportState.bindKeyUsageFromBridge(usage);