@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
@@ -27,10 +27,10 @@ import {
27
27
  anthropicRoutingFor,
28
28
  AnthropicAccountCooldownError,
29
29
  formatAnthropicProviderForLog,
30
- anthropicSessionKeyFromParts,
31
30
  } from "../../oauth/anthropic-routing";
32
31
  import {
33
32
  OAuthAccountPausedError,
33
+ OAuthTokenRefreshBusyError,
34
34
  getValidAccessSnapshotForAccount,
35
35
  forceRefreshOAuthAccessSnapshot,
36
36
  getValidAccessTokenSnapshot,
@@ -49,6 +49,9 @@ import {
49
49
  refusalAwareInitialKiroAccount,
50
50
  noteGenericPoolSelection,
51
51
  } from "../../oauth/generic-account-failover";
52
+ import { admitGenericSessionAccount, bindGenericSessionAccount, genericSessionAccount, genericSessionAccountEligible, genericSessionAffinityEnabled, preferredGenericSessionAccount } from "../../oauth/generic-session-affinity";
53
+ import { oauthSessionKeyFromParts } from "../../oauth/session-affinity-key";
54
+ import { resolveContextPrincipal } from "../auth-cors";
52
55
  import { tryKiroAlternateAfterTerminalRefresh } from "../../oauth/kiro-terminal-failover";
53
56
  import { classifyModelFamilyForQuota } from "../../oauth/account-quota-rank";
54
57
  import { expandInferenceOAuthSendBudget } from "../inference/context";
@@ -56,6 +59,11 @@ import { stampOAuthAccountLabel, usesApiKeyAccount } from "../../providers/label
56
59
  import { resolveProviderTransport } from "../../providers/xai-transport";
57
60
  import { captureRouteStaticPolicy } from "../../router";
58
61
  import { CopilotAutoHttpError, resolveCopilotAuto } from "../../providers/github-copilot-auto";
62
+ import {
63
+ AdmissionModelDeniedError,
64
+ admissionModelDeniedResponse,
65
+ assertRouteAllowedByScope,
66
+ } from "../admission-model-scope";
59
67
  import { renameRoutedIdentityInContext } from "../../adapters/identity";
60
68
  import { resolveCopilotApiBaseUrl } from "../../oauth/github-copilot";
61
69
  import {
@@ -73,6 +81,7 @@ import { bindRouteReasoningReplayScope } from "./core-replay";
73
81
  import { sessionIdHeaderFromRequest, normalizeLogConversationId } from "../request-log-conversation";
74
82
  import { redactSecretString } from "../../lib/redact";
75
83
  import { selectProactiveApiKeyTransport } from "../../providers/key-failover";
84
+ import { distributedKeyPhysicalFetch } from "../../providers/api-key-response-ownership";
76
85
  import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
77
86
  import { providerConsumesCallerAuthorization } from "../../providers/caller-authorization";
78
87
  import { releaseCodexAuthContextProbeLease, stripCodexRuntimeProviderFields } from "../../codex/auth-context";
@@ -109,10 +118,13 @@ export async function prepareResponsesTransport(
109
118
  | "inboundWire"
110
119
  | "selectedForwardHeaders"
111
120
  | "translatorBudget"
121
+ | "inboundSelector"
122
+ | "admissionScope"
112
123
  >,
113
124
  ) {
114
125
  const { config, logCtx, options, req } = requestContext;
115
126
  const { route, parsed, inboundWire, translatorBudget } = requestState;
127
+ const genericAffinityMode = genericSessionAffinityEnabled(config, route.providerName);
116
128
  const anthropicInstance = route.provider.authMode === "oauth"
117
129
  ? configuredAnthropicInstance(config, route.providerName) : undefined;
118
130
  // Refuse custom/orphan B OAuth before any generic resolver can observe its auth row.
@@ -169,6 +181,7 @@ export async function prepareResponsesTransport(
169
181
  || route.providerName === "orcarouter-oauth"
170
182
  // runTurn transport: the replay runs on the first-event preflight in run-turn-execution.
171
183
  || route.providerName === "devin"
184
+ || route.providerName === "dsh-account"
172
185
  ) && route.provider.authMode === "oauth";
173
186
  let sentOAuthSnapshot: OAuthAccessSnapshot | undefined;
174
187
  // One inference-401 replay belongs to this request, even when Auto changes exchange wire.
@@ -210,6 +223,8 @@ export async function prepareResponsesTransport(
210
223
  for (let attempt = 0; attempt < maxSelectionAttempts; attempt++) {
211
224
  requireAnthropicAdmission();
212
225
  if (candidate.provider !== route.providerName) return null;
226
+ if (genericAffinityMode && route.provider.googleMode === "cloud-code-assist" && !candidate.projectId)
227
+ throw new OAuthLoginRequiredError(route.providerName);
213
228
  if (anthropicInstance) anthropicRouteDecision = currentAnthropicRouteDecision();
214
229
  if (!oauthSelection) return null;
215
230
  const proactiveEnabled = anthropicInstance !== undefined
@@ -227,7 +242,9 @@ export async function prepareResponsesTransport(
227
242
  if (candidate.provider !== route.providerName) return null;
228
243
  if (anthropicInstance) anthropicRouteDecision = currentAnthropicRouteDecision();
229
244
  if (anthropicRouteDecision && !routeCandidates(anthropicRouting!.getEligibleAnthropicAccounts(Date.now(), route.modelId), anthropicRouteDecision).includes(candidate.accountId)) return null;
230
- const committed = await commitOAuthAccountSelection(route.providerName, candidate.accountId, {
245
+ const committed = genericAffinityMode
246
+ ? admitGenericSessionAccount(config, route.providerName, oauthSelection, candidate, route.modelId, genericAffinityMode)
247
+ : await commitOAuthAccountSelection(route.providerName, candidate.accountId, {
231
248
  expectedSelection: oauthSelection,
232
249
  expectedCredentialGeneration: candidate.generation,
233
250
  requireUsableAccount: true,
@@ -241,14 +258,18 @@ export async function prepareResponsesTransport(
241
258
  )) return null;
242
259
  oauthSelection = committed;
243
260
  servingOAuthSnapshot = candidate;
261
+ bindGenericSessionAccount(config, route.providerName, genericSessionKey, candidate.accountId, route.modelId);
244
262
  if (anthropicInstance) replayOAuthCredentialSnapshot = { accountId: candidate.accountId, generation: candidate.generation };
245
263
  forgetGenericFailoverRoster(route.providerName);
246
264
  return candidate;
247
265
  }
248
266
  // A newer manual choice wins over this request's old proposal, including A→B→A.
249
267
  // Resolve that choice, not the rejected candidate, before trying admission again.
268
+ const previousSelection = oauthSelection;
250
269
  oauthSelection = captureOAuthAccountSelection(route.providerName);
251
270
  if (!oauthSelection) return null;
271
+ const selectionChanged = oauthSelection.accountId !== previousSelection.accountId
272
+ || oauthSelection.revision !== previousSelection.revision;
252
273
  // A revision also changes on per-account policy edits. Re-evaluate the selector
253
274
  // after credential waits even without a model route, rather than reusing stale active.
254
275
  const revisedAnthropic = anthropicInstance !== undefined
@@ -256,9 +277,15 @@ export async function prepareResponsesTransport(
256
277
  const revisedAnthropicId = revisedAnthropic?.accountId;
257
278
  if (anthropicInstance !== undefined && !revisedAnthropicId) return null;
258
279
  if (revisedAnthropic) anthropicReason = revisedAnthropic.reason;
280
+ const genericAccountId = genericAffinityMode && !selectionChanged
281
+ ? (genericSessionAccountEligible(config, route.providerName, candidate.accountId, route.modelId, Date.now(), true)
282
+ ? candidate.accountId : (preferredGenericSessionAccount(config, route.providerName, genericSessionKey, route.modelId)
283
+ ?? oauthSelection.accountId))
284
+ : oauthSelection.accountId;
285
+ if (!genericAccountId && !anthropicInstance) return null;
259
286
  candidate = anthropicInstance !== undefined
260
287
  ? await anthropicRouting!.getAnthropicPoolAccessSnapshot(revisedAnthropicId ?? oauthSelection.accountId)
261
- : await getValidAccessSnapshotForAccount(route.providerName, oauthSelection.accountId, { requireUsableAccount: true });
288
+ : await getValidAccessSnapshotForAccount(route.providerName, genericAccountId!, { requireUsableAccount: true });
262
289
  if (route.provider.googleMode === "cloud-code-assist" && !candidate.projectId) return null;
263
290
  }
264
291
  return null;
@@ -268,7 +295,10 @@ export async function prepareResponsesTransport(
268
295
  // Keyed on the account, not the selection revision: a selection that moved away and back
269
296
  // (A -> B -> A) before the 401 landed still serves the rejected credential, and skipping
270
297
  // the refresh would replay it and spend the one recovery attempt.
271
- const candidate = current?.accountId === sent.accountId ? await forceRefreshOAuthAccessSnapshot(sent) : sent;
298
+ const candidate = (current?.accountId === sent.accountId
299
+ || (genericAffinityMode && current?.revision === oauthSelection?.revision
300
+ && current?.accountId === oauthSelection?.accountId && servingOAuthSnapshot?.accountId === sent.accountId))
301
+ ? await forceRefreshOAuthAccessSnapshot(sent) : sent;
272
302
  const admitted = await commitResolvedOAuthSelection(candidate);
273
303
  if (!admitted) throw new Error("OAuth selection changed during credential recovery");
274
304
  if (kiroLoadEnabled && options.accountLoad?.lease?.accountId !== admitted.accountId) {
@@ -297,6 +327,7 @@ export async function prepareResponsesTransport(
297
327
  const copilotRequestedModel = route.modelId;
298
328
  /** Project only bounded Copilot refusal metadata; upstream bodies and session tokens never reach the client. */
299
329
  const copilotRefusalResponse = (error: unknown): Response | undefined => {
330
+ if (error instanceof AdmissionModelDeniedError) return admissionModelDeniedResponse(error);
300
331
  if (!(error instanceof CopilotAutoHttpError) || ![401, 403, 429].includes(error.status)) return;
301
332
  const response = formatErrorResponse(error.status, error.status === 429 ? "rate_limit_error" : "authentication_error",
302
333
  error.message);
@@ -357,6 +388,13 @@ export async function prepareResponsesTransport(
357
388
  replayOAuthCredentialSnapshot = { accountId: refreshed.accountId, generation: refreshed.generation };
358
389
  selected = await negotiate();
359
390
  }
391
+ assertRouteAllowedByScope(
392
+ // The scope captured at admission: a key edited or removed while negotiation was in
393
+ // flight must not widen this request. The refusal names the caller's own selector.
394
+ requestState.admissionScope,
395
+ requestState.inboundSelector,
396
+ { providerName: route.providerName, modelId: selected.modelId },
397
+ );
360
398
  copilotSessionExpiresAt = selected.expiresAt ?? Infinity;
361
399
  route.provider = selected.provider;
362
400
  route.modelId = selected.modelId;
@@ -504,6 +542,8 @@ export async function prepareResponsesTransport(
504
542
  return selected?.accountId === binding.selection.accountId && selected?.revision === binding.selection.revision
505
543
  && !!row && !row.paused && !row.needsReauth && row.credential.expires > Date.now()
506
544
  && (anthropicInstance === undefined || !anthropicRouting!.getAnthropicAccountHealthSnapshot(binding.snapshot.accountId) && !anthropicRatePolicy!.anthropicRatePauseUntil(binding.snapshot.accountId) && !anthropicQuota!.anthropicFamilyRejected(binding.snapshot.accountId, route.modelId))
545
+ && (!genericAffinityMode
546
+ || genericSessionAccountEligible(config, route.providerName, binding.snapshot.accountId, route.modelId, Date.now(), true))
507
547
  && credentialGeneration(row.credential) === binding.snapshot.generation;
508
548
  };
509
549
  /** Bind the negotiated wire adapter and its built requests to the serving credential's authority. */
@@ -577,12 +617,23 @@ export async function prepareResponsesTransport(
577
617
  const accountId = await anthropicRouting!.resolveAnthropicDispatchAccountId(config, anthropicSessionKey, anthropicRouteDecision, route.modelId);
578
618
  candidate = await anthropicRouting!.getAnthropicPoolAccessSnapshot(accountId);
579
619
  }
620
+ if (genericAffinityMode
621
+ && (!candidate || !genericSessionAccountEligible(config, route.providerName, candidate.accountId, route.modelId, Date.now(), true))) {
622
+ const accountId = preferredGenericSessionAccount(config, route.providerName, genericSessionKey, route.modelId)
623
+ ?? captureOAuthAccountSelection(route.providerName)?.accountId;
624
+ candidate = accountId ? await getValidAccessSnapshotForAccount(route.providerName, accountId, { requireUsableAccount: true }) : undefined;
625
+ }
580
626
  if (!candidate || !await applyFailoverSnapshot(candidate, requestParsed, true)) {
581
627
  if (anthropicInstance !== undefined) await anthropicRouting!.resolveAnthropicDispatchAccountId(config, anthropicSessionKey, anthropicRouteDecision, route.modelId);
582
628
  throw new Error("OAuth account selection changed before dispatch");
583
629
  }
584
630
  } else {
585
- const current = resolveCurrentProviderApiKeyTransport(config, route.providerName, route.provider);
631
+ let current = resolveCurrentProviderApiKeyTransport(config, route.providerName, route.provider);
632
+ if (!current && (route.provider._apiKeyAttempt?.distributed || (route.provider.requestPacing?.enabled
633
+ && (route.provider.requestPacing.keys || route.provider.requestPacing.keySelection === "round-robin")))) {
634
+ const fresh = resolveCurrentProviderApiKeyTransport(config, route.providerName, { ...route.provider, _apiKeyAttempt: undefined });
635
+ if (fresh) current = selectProactiveApiKeyTransport(config, route.providerName, fresh) ?? fresh;
636
+ }
586
637
  if (!current) throw new Error("API key selection is unavailable before dispatch");
587
638
  route.provider = current;
588
639
  await resolveCopilotSelection(requestParsed, true);
@@ -610,7 +661,10 @@ export async function prepareResponsesTransport(
610
661
  ? (options.sendBudget as import("../../lib/request-execution-budget").RequestExecutionBudget).beginSpendProducer?.() : undefined;
611
662
  try {
612
663
  for (let attempt = 0; attempt < 3; attempt++) {
613
- if (!selectionIsCurrent(adapterBindings.get(selectedAdapter))) selectedAdapter = await refreshRunTurnAdapter(requestParsed);
664
+ incoming.abortSignal?.throwIfAborted();
665
+ const selectionRefreshed = !selectionIsCurrent(adapterBindings.get(selectedAdapter));
666
+ if (attempt === 0 && selectionRefreshed) releaseProviderRequestSlot(incoming.pacingSlot);
667
+ if (selectionRefreshed) selectedAdapter = await refreshRunTurnAdapter(requestParsed);
614
668
  const binding = adapterBindings.get(selectedAdapter);
615
669
  const run = rawRunTurns.get(selectedAdapter);
616
670
  if (!run) throw new Error("Selected provider no longer supports this turn transport");
@@ -620,24 +674,39 @@ export async function prepareResponsesTransport(
620
674
  // Both main and image-loop callers already acquired the initial pacing slot.
621
675
  // Subsequent physical messages retain this adapter/credential and are paced normally.
622
676
  try {
623
- attemptSlot = attempt === 0
624
- ? incoming.pacingSlot ?? await waitForProviderRequestSlot(route.providerName, route.provider, route.modelId, incoming.abortSignal)
677
+ // A pre-acquired lease belongs to the original selection, never its replacement.
678
+ attemptSlot = attempt === 0 && !selectionRefreshed && incoming.pacingSlot
679
+ ? incoming.pacingSlot
625
680
  : await waitForProviderRequestSlot(route.providerName, route.provider, route.modelId, incoming.abortSignal);
681
+ incoming.abortSignal?.throwIfAborted();
682
+ // Every admission can queue, including a replacement's. Never enter an
683
+ // adapter on a lease whose credential changed while it waited.
684
+ if (!selectionIsCurrent(binding)) {
685
+ refused = true;
686
+ throw new Error("Account selection changed before turn admission");
687
+ }
688
+ const beforePhysicalSend = (): void => {
689
+ incoming.abortSignal?.throwIfAborted();
690
+ if (sent) {
691
+ if (binding?.kind === "api-key" && binding.provider._apiKeyAttempt?.distributed
692
+ && !selectionIsCurrent(binding)) throw new Error("API key became unavailable during the turn");
693
+ return;
694
+ }
695
+ if (!selectionIsCurrent(binding)) {
696
+ refused = true;
697
+ throw new Error("Account selection changed before the first turn dispatch");
698
+ }
699
+ incoming.beforePhysicalSend?.();
700
+ commitKeyAttemptSend();
701
+ sent = true;
702
+ };
626
703
  const fetch = providerFetch(route.provider, options.codexWsRuntimeIdentity, {
627
704
  providerName: route.providerName, modelId: route.modelId, pacingSlotAcquired: true,
628
705
  pacingSlot: attemptSlot,
629
706
  turnScopedPacing: true,
630
- beforeDispatch: () => {
631
- if (sent) return;
632
- if (!selectionIsCurrent(binding)) {
633
- refused = true;
634
- throw new Error("Account selection changed before the first turn dispatch");
635
- }
636
- commitKeyAttemptSend();
637
- sent = true;
638
- },
707
+ beforeDispatch: beforePhysicalSend,
639
708
  });
640
- await run(requestParsed, { ...incoming, providerFetch: fetch }, event => { if (!refused) emit(event); });
709
+ await run(requestParsed, { ...incoming, pacingSlot: attemptSlot, providerFetch: fetch, beforePhysicalSend }, event => { if (!refused) emit(event); });
641
710
  } catch (error) {
642
711
  if (!refused) throw error;
643
712
  } finally {
@@ -646,7 +715,7 @@ export async function prepareResponsesTransport(
646
715
  if (!refused) return;
647
716
  // The adapter may map the guard's exception to an error event. Neither that
648
717
  // event nor a refused send may escape before retrying the newly selected account.
649
- selectedAdapter = await refreshRunTurnAdapter(requestParsed);
718
+ if (attempt < 2) selectedAdapter = await refreshRunTurnAdapter(requestParsed);
650
719
  }
651
720
  throw new Error("Account selection changed repeatedly before turn dispatch");
652
721
  } finally { producer?.close(); }
@@ -654,10 +723,16 @@ export async function prepareResponsesTransport(
654
723
  /** Fence the physical send, with bounded credential/session rebuilds and safe negotiated refusals. */
655
724
  const oauthDispatch = (wireRequest: AdapterRequest, requestParsed = parsed, owner: "adapter" | "passthrough" = "adapter"): ProviderFetchOptions["dispatchOverride"] => {
656
725
  if (route.provider.authMode === "forward") return undefined;
657
- return /** Dispatch with the current binding and session, rebuilding at most three times before returning a safe refusal. */ async (input, init, execute) => {
726
+ return /** Dispatch with the current binding and session, rebuilding at most three times before returning a safe refusal. */ async (input, init, execute, reacquirePacing) => {
658
727
  let destination = input;
659
728
  let dispatchInit = init;
729
+ let pacingChanged = false;
660
730
  for (let attempt = 0; attempt < 3; attempt++) {
731
+ if (pacingChanged) {
732
+ if (!reacquirePacing) throw new Error("Refreshed dispatch requires pacing readmission");
733
+ await reacquirePacing(route.provider, route.modelId);
734
+ pacingChanged = false;
735
+ }
661
736
  if (selectionIsCurrent(requestBindings.get(wireRequest)) && copilotSessionExpiresAt > Date.now() + 1000) {
662
737
  const fetchImpl = (route.provider as OcxProviderConfig & { fetch?: typeof globalThis.fetch }).fetch ?? execute;
663
738
  const binding = requestBindings.get(wireRequest);
@@ -698,7 +773,7 @@ export async function prepareResponsesTransport(
698
773
  }
699
774
  commitKeyAttemptSend();
700
775
  response = await sendWithConnectionPolicy(
701
- anthropicBillingPhysicalFetch(fetchImpl, route.providerName, route.provider),
776
+ distributedKeyPhysicalFetch(anthropicBillingPhysicalFetch(fetchImpl, route.providerName, route.provider), route.providerName, route.provider, target),
702
777
  destination,
703
778
  { ...dispatchInit, redirect: "manual" },
704
779
  { providerName: route.providerName, provider: route.provider },
@@ -725,6 +800,7 @@ export async function prepareResponsesTransport(
725
800
  let nextAdapter: ProviderAdapter;
726
801
  try { nextAdapter = await refreshDispatchAdapter(requestParsed); }
727
802
  catch (error) {
803
+ if (error instanceof AdmissionModelDeniedError) throw error;
728
804
  const refusal = copilotRefusalResponse(error);
729
805
  if (refusal) return refusal;
730
806
  throw error;
@@ -759,13 +835,15 @@ export async function prepareResponsesTransport(
759
835
  sameTargetToken = transportToken;
760
836
  destination = rebuilt.url;
761
837
  dispatchInit = { ...dispatchInit, method: rebuilt.method, headers, body: rebuilt.body };
762
- // The next iteration validates synchronously and calls fetch in that same turn.
838
+ pacingChanged = route.provider.requestPacing?.enabled === true
839
+ && (!!route.provider.requestPacing.keys || route.provider.requestPacing.keySelection === "round-robin");
840
+ // A refreshed key must reacquire admission before the next synchronous send guard.
763
841
  }
764
842
  throw new Error("OAuth account selection changed repeatedly before dispatch");
765
843
  };
766
844
  };
767
- const anthropicSessionKey = anthropicInstance !== undefined && route.provider.authMode === "oauth"
768
- ? anthropicSessionKeyFromParts({
845
+ const oauthSessionKey = route.provider.authMode === "oauth"
846
+ ? oauthSessionKeyFromParts({
769
847
  sessionIdHeader: sessionIdHeaderFromRequest(req.headers),
770
848
  threadIdHeader: req.headers.get("thread-id"),
771
849
  promptCacheKey: typeof parsed.options.promptCacheKey === "string" ? parsed.options.promptCacheKey : null,
@@ -773,6 +851,9 @@ export async function prepareResponsesTransport(
773
851
  promptCacheKeyIsSharedCohort: options.promptCacheKeyIsSharedCohort === true,
774
852
  })
775
853
  : null;
854
+ const anthropicSessionKey = anthropicInstance ? oauthSessionKey : null;
855
+ const genericSessionKey = genericAffinityMode && oauthSessionKey
856
+ ? JSON.stringify([resolveContextPrincipal(req, config, options.admission) ?? "loopback", oauthSessionKey]) : null;
776
857
  if (route.provider.authMode === "oauth") {
777
858
  try {
778
859
  if (anthropicInstance !== undefined && anthropicRouting!.isAnthropicAccountPoolEnabled(config)) {
@@ -805,10 +886,17 @@ export async function prepareResponsesTransport(
805
886
  // without quota evidence keeps the resolution it has today.
806
887
  const refusalAwareId = route.providerName === "kiro" && oauthSelection?.accountId
807
888
  ? refusalAwareInitialKiroAccount(config, oauthSelection.accountId, Date.now(), route.modelId) : null;
808
- const preferredAccountId = refusalAwareId ?? (isGenericFailoverProvider(route.providerName, route.provider)
889
+ const affinityAccountId = genericSessionAccount(config, route.providerName, genericSessionKey, route.modelId);
890
+ const proposedAccountId = affinityAccountId
891
+ ?? preferredGenericSessionAccount(config, route.providerName, genericSessionKey, route.modelId)
892
+ ?? refusalAwareId ?? (isGenericFailoverProvider(route.providerName, route.provider)
809
893
  ? preferredInitialAccount(config, route.providerName, Date.now(), route.modelId)
810
894
  : null);
811
- let safetyAlternateId: string | null = refusalAwareId;
895
+ // An unchanged active choice must retain the ordinary resolver's terminal-refresh
896
+ // recovery (not the speculative-preference fallback that resolves the same dead grant).
897
+ const preferredAccountId = genericAffinityMode && proposedAccountId === oauthSelection?.accountId
898
+ ? null : proposedAccountId;
899
+ let safetyAlternateId: string | null = !affinityAccountId && preferredAccountId === refusalAwareId ? refusalAwareId : null;
812
900
  // Resolved account-scoped, NOT through failoverAccountSnapshot: that helper marks a
813
901
  // rotation site, and rotation sites must apply their credential through
814
902
  // applyFailoverSnapshot's pairing rules. This is initial resolution — the code below
@@ -910,7 +998,8 @@ export async function prepareResponsesTransport(
910
998
  // Advance the pool cursor only now that this account is actually admitted. The
911
999
  // helper returns immediately unless the kernel is on AND the strategy is
912
1000
  // round-robin, so quota and fill-first pools reach it without being touched.
913
- noteGenericPoolSelection(config, route.providerName, resolved.accountId, route.modelId);
1001
+ if (!affinityAccountId || affinityAccountId !== resolved.accountId)
1002
+ noteGenericPoolSelection(config, route.providerName, resolved.accountId, route.modelId);
914
1003
  }
915
1004
  // Anthropic is excluded from isGenericFailoverProvider -- its own pool owns affinity and
916
1005
  // a fail-closed local-cli credential rule -- so without this stamp its identity is
@@ -948,6 +1037,10 @@ export async function prepareResponsesTransport(
948
1037
  `${redactSecretString(err.message)}. Remove or reconfigure provider '${safeProviderName}' in the OpenCodex configuration.`,
949
1038
  );
950
1039
  }
1040
+ if (err instanceof OAuthTokenRefreshBusyError) {
1041
+ return options.abortSignal?.aborted || req.signal.aborted ? clientCancelledResponse()
1042
+ : formatErrorResponse(503, "server_error", err.message, { retryAfter: "1" });
1043
+ }
951
1044
  if (err instanceof OAuthAccountPausedError) {
952
1045
  return formatErrorResponse(403, "permission_error", publicOAuthAuthenticationErrorMessage(err));
953
1046
  }
@@ -89,6 +89,7 @@ export function createResponsesEffects(
89
89
  if (routedCompaction) {
90
90
  delete parsed.context.tools;
91
91
  delete parsed._webSearch;
92
+ delete parsed._anthropicServerTools;
92
93
  delete parsed.options.toolChoice;
93
94
  delete parsed.options.parallelToolCalls;
94
95
  // The compaction turn is a plain prose summary; a surviving structured-output format