@bitkyc08/opencodex 2.80.0 → 2.82.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 (376) hide show
  1. package/README.md +3 -3
  2. package/bin/ocx.mjs +166 -73
  3. package/gui/dist/assets/App-Ds_qL7o8.js +54 -0
  4. package/gui/dist/assets/Tray-BqHF5Trd.js +1 -0
  5. package/gui/dist/assets/{index-Brk6F3fn.css → index-BAbcj51J.css} +1 -1
  6. package/gui/dist/assets/index-D3FBCNiP.js +94 -0
  7. package/gui/dist/assets/quota-summary-DTBC8n7-.js +1 -0
  8. package/gui/dist/index.html +2 -2
  9. package/gui/dist/provider-icons/claude-green.svg +1 -0
  10. package/package.json +3 -2
  11. package/src/adapters/anthropic-model-contract.ts +8 -2
  12. package/src/adapters/anthropic.ts +8 -3
  13. package/src/adapters/codebuddy/mcp-server.ts +2 -25
  14. package/src/adapters/codebuddy/tool-bridge.ts +1 -7
  15. package/src/adapters/coding-agent/mcp-server.ts +33 -0
  16. package/src/adapters/coding-agent/protocol.ts +122 -17
  17. package/src/adapters/coding-agent/tool-catalog.ts +75 -0
  18. package/src/adapters/coding-agent/turn.ts +101 -44
  19. package/src/adapters/cursor/catalog.ts +8 -0
  20. package/src/adapters/cursor/effort-map.ts +2 -0
  21. package/src/adapters/devin/live-models.ts +2 -0
  22. package/src/adapters/kiro-retry.ts +9 -4
  23. package/src/adapters/openai-chat/messages.ts +7 -6
  24. package/src/adapters/openai-responses/compaction-search-history.ts +109 -0
  25. package/src/adapters/openai-responses/passthrough.ts +9 -4
  26. package/src/adapters/openai-responses/tool-output-recovery.ts +20 -0
  27. package/src/adapters/physical-send.ts +8 -5
  28. package/src/adapters/qoder/adapter.ts +41 -6
  29. package/src/adapters/qoder/scaffold-guard.ts +2 -3
  30. package/src/adapters/zed.ts +16 -1
  31. package/src/claude/agents-inject.ts +14 -3
  32. package/src/claude/context-windows.ts +19 -17
  33. package/src/claude/desktop-3p.ts +88 -12
  34. package/src/claude/desktop-profile.ts +2 -1
  35. package/src/claude/gateway-cache.ts +18 -1
  36. package/src/claude/inbound-cache-stabilize.ts +4 -9
  37. package/src/claude/inbound.ts +78 -3
  38. package/src/claude/intercept/cli-catalog.ts +3 -0
  39. package/src/claude/intercept/model-bindings.ts +1 -0
  40. package/src/claude/intercept/picker-ca-persistence.ts +7 -6
  41. package/src/claude/intercept/picker-listener.ts +71 -4
  42. package/src/claude/intercept/picker-models.ts +14 -6
  43. package/src/claude/intercept/settings.ts +3 -0
  44. package/src/claude/long-context.ts +66 -0
  45. package/src/claude/model-info.ts +19 -16
  46. package/src/claude/outbound.ts +94 -9
  47. package/src/claude/subagent-model.ts +40 -11
  48. package/src/cli/account-anthropic-threshold.ts +14 -5
  49. package/src/cli/account-api.ts +3 -1
  50. package/src/cli/account-extended.ts +13 -9
  51. package/src/cli/account-policy-dto.ts +1 -1
  52. package/src/cli/account-policy.ts +13 -4
  53. package/src/cli/account.ts +3 -3
  54. package/src/cli/agent.ts +9 -5
  55. package/src/cli/aside-profile-recovery.ts +33 -0
  56. package/src/cli/capabilities-accounts.ts +2 -2
  57. package/src/cli/capabilities-agents-routing.ts +24 -0
  58. package/src/cli/capabilities-base.ts +5 -5
  59. package/src/cli/capabilities-integrations.ts +18 -2
  60. package/src/cli/cli-path-diagnostics.ts +132 -0
  61. package/src/cli/codex-shim-autorestore.ts +3 -0
  62. package/src/cli/desktop-runtime-guidance.ts +65 -0
  63. package/src/cli/dispatch.ts +15 -1
  64. package/src/cli/doctor.ts +16 -3
  65. package/src/cli/help.ts +4 -1
  66. package/src/cli/index.ts +25 -14
  67. package/src/cli/integration-aside-sync.ts +2 -1
  68. package/src/cli/integration-plan-dto.ts +1 -0
  69. package/src/cli/integration-preview.ts +8 -1
  70. package/src/cli/integrations.ts +48 -6
  71. package/src/cli/message-args.ts +34 -0
  72. package/src/cli/message-command.ts +43 -0
  73. package/src/cli/message-runtime.ts +6 -0
  74. package/src/cli/observe.ts +3 -1
  75. package/src/cli/provider.ts +29 -6
  76. package/src/cli/registry.ts +24 -2
  77. package/src/cli/resolve.ts +25 -2
  78. package/src/cli/root.ts +4 -2
  79. package/src/cli/status.ts +64 -12
  80. package/src/cli/update-restart-child.ts +4 -2
  81. package/src/cli/update-restart-home.ts +28 -10
  82. package/src/cli/update-restart-service-record.ts +116 -0
  83. package/src/cli/update-restart-supervision.ts +96 -0
  84. package/src/cli/update-restart.ts +59 -13
  85. package/src/cli/v2.ts +27 -20
  86. package/src/cli/version-skew-notice.ts +77 -0
  87. package/src/clients/config-export/commandcode.ts +131 -0
  88. package/src/clients/config-export/contracts.ts +12 -0
  89. package/src/clients/config-export.ts +64 -1
  90. package/src/clients/lazycodex.ts +1 -1
  91. package/src/clients/omo-role-models.ts +32 -7
  92. package/src/codex/auth-api/account-list.ts +11 -3
  93. package/src/codex/auth-api/account-selection.ts +107 -0
  94. package/src/codex/auth-api/main-account-probe.ts +25 -8
  95. package/src/codex/auth-api/pool-mode-gate.ts +33 -9
  96. package/src/codex/auth-api/pool-quota-probe.ts +15 -1
  97. package/src/codex/auth-api/routes.ts +11 -49
  98. package/src/codex/auth-context.ts +20 -1
  99. package/src/codex/autostart-health.ts +12 -3
  100. package/src/codex/catalog/auto-review.ts +13 -6
  101. package/src/codex/catalog/build-entries.ts +33 -7
  102. package/src/codex/catalog/bundled.ts +155 -26
  103. package/src/codex/catalog/combo-member.ts +3 -2
  104. package/src/codex/catalog/control-plane.ts +45 -0
  105. package/src/codex/catalog/effort.ts +37 -6
  106. package/src/codex/catalog/gather-capture.ts +10 -3
  107. package/src/codex/catalog/model-hints.ts +15 -8
  108. package/src/codex/catalog/model-visibility.ts +3 -1
  109. package/src/codex/catalog/operator-tiers.ts +38 -0
  110. package/src/codex/catalog/parsing.ts +2 -1
  111. package/src/codex/catalog/provider-models.ts +58 -44
  112. package/src/codex/catalog/retained-sync.ts +7 -5
  113. package/src/codex/catalog/routed-gather.ts +15 -1
  114. package/src/codex/catalog-auto-refresh-sources.ts +3 -4
  115. package/src/codex/catalog-auto-refresh.ts +19 -0
  116. package/src/codex/catalog.ts +1 -1
  117. package/src/codex/codex-home-owner.ts +3 -0
  118. package/src/codex/config-write-lock.ts +333 -0
  119. package/src/codex/control-plane-models.ts +6 -0
  120. package/src/codex/convergence.ts +2 -1
  121. package/src/codex/features.ts +257 -200
  122. package/src/codex/inject/config-write-section.ts +34 -0
  123. package/src/codex/inject/multi-agent-v2.ts +14 -6
  124. package/src/codex/inject/remove.ts +36 -11
  125. package/src/codex/inject/restore.ts +85 -20
  126. package/src/codex/inject-coordination.ts +15 -6
  127. package/src/codex/inject.ts +62 -20
  128. package/src/codex/journal.ts +62 -17
  129. package/src/codex/main-account-cache.ts +43 -0
  130. package/src/codex/paths.ts +2 -0
  131. package/src/codex/prepared-home.ts +18 -0
  132. package/src/codex/prompt-journal.ts +25 -12
  133. package/src/codex/prompt-layers.ts +49 -26
  134. package/src/codex/prompt-lock-claim.ts +134 -0
  135. package/src/codex/prompt-lock-evidence.ts +26 -0
  136. package/src/codex/prompt-lock-io.ts +16 -0
  137. package/src/codex/prompt-lock-owner.ts +130 -0
  138. package/src/codex/prompt-lock.ts +84 -53
  139. package/src/codex/routing/failure-window.ts +146 -0
  140. package/src/codex/routing/selection.ts +4 -1
  141. package/src/codex/routing.ts +13 -17
  142. package/src/codex/runtime.ts +106 -18
  143. package/src/codex/shim.ts +45 -19
  144. package/src/combos/jev-decision-contract.ts +25 -2
  145. package/src/combos/jev-service-exchange.ts +281 -0
  146. package/src/combos/jev.ts +34 -219
  147. package/src/combos/types.ts +1 -1
  148. package/src/config/atomic-write.ts +1 -1
  149. package/src/config/codex-ws-reuse-setting.ts +10 -0
  150. package/src/config/diagnostics.ts +27 -16
  151. package/src/config/live-reconcile.ts +17 -0
  152. package/src/config/load-degrade.ts +42 -0
  153. package/src/config/rebase-provenance.ts +11 -0
  154. package/src/config/schema/anthropic-account-pool.ts +143 -0
  155. package/src/config/schema/config-schema.ts +11 -1
  156. package/src/config/schema/leaf-validators.ts +4 -0
  157. package/src/config.ts +2 -0
  158. package/src/generated/compatibility-version.json +566 -298
  159. package/src/generated/model-metadata.ts +7 -6
  160. package/src/grok/inject.ts +50 -4
  161. package/src/grok/sync.ts +2 -1
  162. package/src/images/loop.ts +30 -11
  163. package/src/integrations/aside-profiles.ts +10 -3
  164. package/src/integrations/catalog-refresh.ts +19 -3
  165. package/src/integrations/mutation-plan.ts +7 -0
  166. package/src/integrations/registry.ts +8 -0
  167. package/src/integrations/state.ts +9 -2
  168. package/src/integrations/writer.ts +17 -5
  169. package/src/lib/bun-path-runtime.d.mts +32 -0
  170. package/src/lib/bun-path-runtime.mjs +95 -0
  171. package/src/lib/bun-runtime-preflight.ts +140 -0
  172. package/src/lib/desktop-cli-handoff.d.mts +20 -0
  173. package/src/lib/desktop-cli-handoff.mjs +92 -0
  174. package/src/lib/desktop-cli-record.d.mts +29 -0
  175. package/src/lib/desktop-cli-record.mjs +102 -0
  176. package/src/lib/lab-live-route-production.ts +9 -1
  177. package/src/lib/request-execution-budget.ts +244 -58
  178. package/src/lib/spend-pool-alias-validation.ts +79 -0
  179. package/src/lib/spend-pool-continuity.ts +34 -0
  180. package/src/lib/spend-reservation-ledger.ts +412 -50
  181. package/src/lib/state-store-registrations.ts +20 -3
  182. package/src/lib/synced-state-location.ts +46 -6
  183. package/src/lib/test-home-guard.ts +87 -1
  184. package/src/lib/translator-budget.ts +17 -4
  185. package/src/lib/upstream-retry.ts +17 -5
  186. package/src/lib/windows-atomic-replace.ts +38 -16
  187. package/src/lib/windows-elevation.ts +6 -0
  188. package/src/lib/windows-secret-acl.ts +92 -25
  189. package/src/lib/winsw.ts +2 -3
  190. package/src/lib/workflow-budget.ts +53 -11
  191. package/src/messaging/budget.ts +45 -0
  192. package/src/messaging/discovery.ts +57 -0
  193. package/src/messaging/envelope.ts +49 -0
  194. package/src/messaging/input.ts +37 -0
  195. package/src/messaging/rpc.ts +196 -0
  196. package/src/messaging/send.ts +58 -0
  197. package/src/messaging/socket.ts +72 -0
  198. package/src/messaging/types.ts +29 -0
  199. package/src/oauth/anthropic-account-refusal.ts +90 -18
  200. package/src/oauth/anthropic-account-threshold.ts +8 -2
  201. package/src/oauth/anthropic-continuity.ts +15 -1
  202. package/src/oauth/anthropic-model-quota.ts +120 -48
  203. package/src/oauth/anthropic-model-routes.ts +11 -3
  204. package/src/oauth/anthropic-oauth-definitions.ts +98 -0
  205. package/src/oauth/anthropic-pool-config.ts +30 -0
  206. package/src/oauth/anthropic-rate-limit-policy.ts +64 -9
  207. package/src/oauth/anthropic-routing.ts +1202 -866
  208. package/src/oauth/anthropic-send-ownership.ts +41 -0
  209. package/src/oauth/anthropic.ts +4 -1
  210. package/src/oauth/chatgpt.ts +113 -41
  211. package/src/oauth/generic-account-failover.ts +3 -1
  212. package/src/oauth/health.ts +8 -6
  213. package/src/oauth/index.ts +96 -175
  214. package/src/oauth/kiro.ts +22 -9
  215. package/src/oauth/login-cli.ts +2 -0
  216. package/src/oauth/model-discovery-auth.ts +43 -0
  217. package/src/oauth/pool-kernel.ts +11 -5
  218. package/src/oauth/pool-settings-capability.ts +5 -3
  219. package/src/oauth/store-anthropic-instance.ts +87 -0
  220. package/src/oauth/store.ts +68 -10
  221. package/src/oauth/token-guardian.ts +2 -0
  222. package/src/oauth/types.ts +2 -0
  223. package/src/protocols/plan-snapshot.ts +5 -0
  224. package/src/protocols/settings.ts +20 -5
  225. package/src/providers/anthropic-instance-id.ts +47 -0
  226. package/src/providers/anthropic-instance.ts +48 -0
  227. package/src/providers/anthropic-reset-grant-ledger.ts +11 -1
  228. package/src/providers/azure-model-metadata.ts +115 -0
  229. package/src/providers/derive.ts +23 -0
  230. package/src/providers/kiro-models.ts +3 -0
  231. package/src/providers/label.ts +1 -1
  232. package/src/providers/model-rename-fields.ts +2 -0
  233. package/src/providers/quota/account-cache.ts +37 -11
  234. package/src/providers/quota/anthropic-account-quota.ts +93 -0
  235. package/src/providers/quota/anthropic-cooldown-recovery.ts +222 -111
  236. package/src/providers/quota/report-cache.ts +24 -1
  237. package/src/providers/quota/vendor-probes-key.ts +127 -17
  238. package/src/providers/quota/vendor-probes-oauth.ts +46 -14
  239. package/src/providers/quota.ts +23 -33
  240. package/src/providers/registry/entries-core.ts +35 -30
  241. package/src/providers/registry/model-ids.ts +1 -0
  242. package/src/providers/registry/model-seeds.ts +7 -4
  243. package/src/providers/registry/types.ts +3 -0
  244. package/src/providers/registry.ts +3 -3
  245. package/src/responses/reasoning-envelope.ts +29 -1
  246. package/src/responses/reasoning-replay-cache.ts +17 -1
  247. package/src/responses/spill-store.ts +20 -3
  248. package/src/responses/state/metrics.ts +95 -0
  249. package/src/responses/state/snapshot-codec.ts +6 -0
  250. package/src/responses/state/spill-failure.ts +12 -0
  251. package/src/responses/state/spill-queue.ts +13 -5
  252. package/src/responses/state/unforced-store-false.ts +55 -0
  253. package/src/responses/state.ts +68 -76
  254. package/src/responses/task-input.ts +17 -0
  255. package/src/router.ts +28 -4
  256. package/src/routing/compatibility/assemble.ts +1 -1
  257. package/src/routing/quota.ts +7 -3
  258. package/src/server/auth-cors.ts +5 -0
  259. package/src/server/background-lifecycle.ts +27 -0
  260. package/src/server/chat-completions.ts +7 -1
  261. package/src/server/chat-native.ts +64 -28
  262. package/src/server/claude-messages.ts +59 -8
  263. package/src/server/gui-static.ts +38 -4
  264. package/src/server/inbound-body-admission.ts +1 -0
  265. package/src/server/index/claude-intercept-lifecycle.ts +3 -1
  266. package/src/server/index/serve-options.ts +10 -2
  267. package/src/server/index/spend-ledger-lifecycle.ts +12 -5
  268. package/src/server/index/startup-warnings.ts +19 -0
  269. package/src/server/index.ts +5 -2
  270. package/src/server/inference/context.ts +1 -1
  271. package/src/server/lifecycle.ts +1 -1
  272. package/src/server/management/agent-settings-routes.ts +123 -38
  273. package/src/server/management/anthropic-account-threshold.ts +12 -9
  274. package/src/server/management/anthropic-pool-settings.ts +72 -0
  275. package/src/server/management/anthropic-reset-grant-routes.ts +98 -39
  276. package/src/server/management/codex-agent-role-routes.ts +4 -2
  277. package/src/server/management/codex-prompt-routes.ts +1 -0
  278. package/src/server/management/codex-role-auto-assign.ts +13 -2
  279. package/src/server/management/config-routes.ts +85 -14
  280. package/src/server/management/context.ts +2 -2
  281. package/src/server/management/decision-routes.ts +2 -1
  282. package/src/server/management/integration-routes.ts +25 -2
  283. package/src/server/management/oauth-account-routes.ts +79 -98
  284. package/src/server/management/provider-instance-ownership.ts +44 -0
  285. package/src/server/management/provider-routes.ts +21 -28
  286. package/src/server/management/route-registry.ts +3 -1
  287. package/src/server/management/routing-profile-routes.ts +4 -4
  288. package/src/server/management/shared.ts +3 -3
  289. package/src/server/management/system-restart.ts +44 -14
  290. package/src/server/management/system-routes.ts +24 -2
  291. package/src/server/management/vision-sidecar-options.ts +9 -6
  292. package/src/server/management/web-search-sidecar-options.ts +51 -2
  293. package/src/server/management-auth.ts +6 -0
  294. package/src/server/messages-native-eligibility.ts +3 -2
  295. package/src/server/messages-native-oauth.ts +91 -45
  296. package/src/server/messages-native-selector.ts +39 -0
  297. package/src/server/messages-native.ts +133 -38
  298. package/src/server/messages-response-headers.ts +14 -0
  299. package/src/server/request-log-conversation.ts +48 -5
  300. package/src/server/request-log.ts +36 -2
  301. package/src/server/responses/adapter-continuation.ts +31 -19
  302. package/src/server/responses/adapter-dispatch.ts +75 -24
  303. package/src/server/responses/antigravity-validation-refusal.ts +20 -6
  304. package/src/server/responses/codex-ws-exchange.ts +17 -2
  305. package/src/server/responses/codex-ws-pool.ts +100 -13
  306. package/src/server/responses/codex-ws-wire.ts +9 -0
  307. package/src/server/responses/combo-requested-effort.ts +3 -1
  308. package/src/server/responses/compact.ts +25 -8
  309. package/src/server/responses/compaction-recovery.ts +1 -1
  310. package/src/server/responses/core-codex-account.ts +22 -1
  311. package/src/server/responses/core-combo-native.ts +24 -4
  312. package/src/server/responses/core-combo.ts +38 -25
  313. package/src/server/responses/core-lifetime.ts +3 -36
  314. package/src/server/responses/core-normalize.ts +1 -0
  315. package/src/server/responses/core-options.ts +8 -1
  316. package/src/server/responses/core-replay.ts +78 -0
  317. package/src/server/responses/core.ts +3 -3
  318. package/src/server/responses/fetch-helpers.ts +26 -3
  319. package/src/server/responses/passthrough-delivery.ts +30 -4
  320. package/src/server/responses/passthrough-dispatch.ts +39 -9
  321. package/src/server/responses/request-prepare.ts +41 -5
  322. package/src/server/responses/request-send-budget.ts +150 -50
  323. package/src/server/responses/request-spend.ts +325 -33
  324. package/src/server/responses/request-transport.ts +132 -52
  325. package/src/server/responses/response-effects.ts +1 -0
  326. package/src/server/responses/run-turn-execution.ts +13 -3
  327. package/src/server/responses/sidecar-execution.ts +113 -22
  328. package/src/server/responses/sidecar-send-budget.ts +58 -0
  329. package/src/server/responses/ws-upstream.ts +19 -6
  330. package/src/server/responses-undeclared-tool-guard.ts +5 -1
  331. package/src/server/startup-health-cache.ts +2 -1
  332. package/src/server/workflow-refusal.ts +33 -0
  333. package/src/service/cli.ts +20 -4
  334. package/src/service/desktop-command-guard.ts +32 -0
  335. package/src/service/desktop-startup.ts +66 -40
  336. package/src/service/desktop-supervision.d.mts +26 -0
  337. package/src/service/desktop-supervision.mjs +140 -0
  338. package/src/service/managing-cli.ts +1 -0
  339. package/src/service/orchestration.ts +46 -20
  340. package/src/service/ownership-mutation-lease.d.mts +1 -1
  341. package/src/service/ownership-mutation-lease.mjs +5 -7
  342. package/src/service/repair.ts +25 -9
  343. package/src/service/state.ts +4 -3
  344. package/src/service/systemd.ts +17 -8
  345. package/src/service/windows-ops.ts +61 -22
  346. package/src/sidecar/anthropic-binding.ts +79 -0
  347. package/src/sidecar/auth.ts +76 -2
  348. package/src/storage/policy-job.ts +5 -1
  349. package/src/storage/policy-worker.ts +3 -1
  350. package/src/storage/policy.ts +9 -0
  351. package/src/types/anthropic-account-pool.ts +33 -0
  352. package/src/types/config.ts +33 -28
  353. package/src/types/provider.ts +5 -0
  354. package/src/types/request.ts +5 -0
  355. package/src/types/tools.ts +7 -0
  356. package/src/types/wire.ts +1 -0
  357. package/src/types.ts +4 -0
  358. package/src/update/index.ts +57 -9
  359. package/src/update/restart-ownership.ts +10 -3
  360. package/src/update/runtime-ownership.d.mts +6 -0
  361. package/src/update/runtime-ownership.mjs +23 -7
  362. package/src/usage/cost.ts +3 -2
  363. package/src/usage/expected-prices.ts +66 -14
  364. package/src/vision/anthropic-describe.ts +16 -3
  365. package/src/vision/index.ts +3 -0
  366. package/src/vision/plan.ts +19 -6
  367. package/src/web-search/alpha-search.ts +7 -1
  368. package/src/web-search/anthropic-executor.ts +16 -4
  369. package/src/web-search/index.ts +7 -3
  370. package/src/web-search/loop.ts +32 -7
  371. package/src/web-search/passthrough-bridge.ts +4 -1
  372. package/src/web-search/sidecar-providers.ts +4 -3
  373. package/gui/dist/assets/App-MbDVfUeC.js +0 -54
  374. package/gui/dist/assets/Tray-CzkxK82A.js +0 -1
  375. package/gui/dist/assets/index-UJt6npXk.js +0 -94
  376. package/gui/dist/assets/quota-summary-DU3sD6Z-.js +0 -1
@@ -19,17 +19,11 @@
19
19
  * No token, account id or body content is logged or returned in an error message.
20
20
  */
21
21
  import type { OAuthAccessSnapshot } from "../oauth";
22
- import {
23
- commitAnthropicSelectionRouting,
24
- captureAnthropicManualSelectionGeneration,
25
- getAnthropicPoolAccessSnapshot,
26
- getAnthropicAccountHealthSnapshot,
27
- resolveAnthropicDispatchAccountId,
28
- resolveAnthropicAccountForSession,
29
- getEligibleAnthropicAccounts,
30
- isAnthropicAccountPoolEnabled,
31
- } from "../oauth/anthropic-routing";
32
- import { resolveAnthropicModelRoute, routeCandidates, type AnthropicRouteDecision } from "../oauth/anthropic-model-routes";
22
+ import { anthropicRoutingFor } from "../oauth/anthropic-routing";
23
+ import { resolveAnthropicModelRouteForInstance, routeCandidates, type AnthropicRouteDecision } from "../oauth/anthropic-model-routes";
24
+ import { configuredAnthropicInstance, type AnthropicInstanceId } from "../providers/anthropic-instance";
25
+ import { resolveAnthropicMessagesUrl } from "../adapters/anthropic";
26
+ import { routedProviderConfig } from "../router";
33
27
  import {
34
28
  captureOAuthAccountSelection,
35
29
  commitOAuthAccountSelection,
@@ -40,7 +34,6 @@ import type { TranslatorBudget } from "../lib/translator-budget";
40
34
  import type { OcxConfig } from "../types";
41
35
  import { relaySseWithPayloadRewrite } from "./sse-payload-rewrite";
42
36
 
43
- const PROVIDER = "anthropic";
44
37
  const MAX_SELECTION_ATTEMPTS = 3;
45
38
 
46
39
  type Selection = NonNullable<ReturnType<typeof captureOAuthAccountSelection>>;
@@ -52,6 +45,9 @@ function isRec(value: unknown): value is Rec {
52
45
 
53
46
  /** The committed selection and the credential snapshot one native request is served with. */
54
47
  export interface NativeOAuthBinding {
48
+ readonly instance: AnthropicInstanceId;
49
+ /** Config-authorized physical destination, captured before any credential await. */
50
+ readonly routeTarget: string;
55
51
  readonly selection: Selection;
56
52
  readonly snapshot: OAuthAccessSnapshot;
57
53
  /** Anthropic account UUID from the same stored generation, distinct from snapshot.accountId. */
@@ -74,17 +70,43 @@ export class NativeOAuthSelectionChangedError extends Error {
74
70
  /** Invalid operator route configuration remains a request refusal, not an auth failure. */
75
71
  export class NativeOAuthModelRouteError extends Error {}
76
72
 
77
- function currentRoute(config: OcxConfig, model?: string): AnthropicRouteDecision | null {
78
- const resolved = model ? resolveAnthropicModelRoute(config, model) : { decision: null };
73
+ /**
74
+ * Current configured ownership and target; a revoked B never resolves orphan credentials.
75
+ *
76
+ * Adapter, auth mode and destination are read from the routed provider, the same normalization the
77
+ * settled route used to compute its target. Routing canonicalizes the primary row's adapter and auth
78
+ * mode from the registry, so a raw-field comparison would refuse an existing Pool 1 row the route
79
+ * already accepted. B's own raw-row shape is still enforced through `configuredAnthropicInstance`.
80
+ */
81
+ function nativeOAuthRouteTarget(instance: AnthropicInstanceId, config: OcxConfig): string {
82
+ const provider = config.providers[instance];
83
+ if (configuredAnthropicInstance(config, instance) !== instance || !provider || provider.disabled === true) {
84
+ throw new NativeOAuthSelectionChangedError();
85
+ }
86
+ let routed: ReturnType<typeof routedProviderConfig>;
87
+ try {
88
+ routed = routedProviderConfig(instance, provider);
89
+ } catch {
90
+ // A destination the router now refuses is a moved route, not a different failure class.
91
+ throw new NativeOAuthSelectionChangedError();
92
+ }
93
+ if (routed.authMode !== "oauth" || routed.adapter !== "anthropic") throw new NativeOAuthSelectionChangedError();
94
+ return resolveAnthropicMessagesUrl(routed);
95
+ }
96
+
97
+ function currentRoute(instance: AnthropicInstanceId, config: OcxConfig, model?: string): AnthropicRouteDecision | null {
98
+ const resolved = model ? resolveAnthropicModelRouteForInstance(instance, config, model) : { decision: null };
79
99
  if (resolved.error) throw new NativeOAuthModelRouteError(`Invalid Anthropic model routes: ${resolved.error}`);
80
100
  return resolved.decision ? { ...resolved.decision, accounts: [...resolved.decision.accounts] } : null;
81
101
  }
82
102
 
83
- function routeIsCurrent(config: OcxConfig, model: string | undefined, decision: AnthropicRouteDecision | null): boolean {
84
- return JSON.stringify(currentRoute(config, model)) === JSON.stringify(decision);
103
+ function routeIsCurrent(instance: AnthropicInstanceId, config: OcxConfig, model: string | undefined, decision: AnthropicRouteDecision | null): boolean {
104
+ return JSON.stringify(currentRoute(instance, config, model)) === JSON.stringify(decision);
85
105
  }
86
106
 
87
107
  export interface NativeOAuthBindingOptions {
108
+ /** Settled route destination; retries may never authorize a replacement destination. */
109
+ routeTarget?: string;
88
110
  sessionKey?: string | null;
89
111
  model?: string;
90
112
  /** An account proposed by the shared pre-output refusal recovery policy. */
@@ -94,51 +116,68 @@ export interface NativeOAuthBindingOptions {
94
116
  expectedRecoveryRouteDecision?: AnthropicRouteDecision | null;
95
117
  }
96
118
 
119
+ /** Compatibility entrypoint: the original instance only. */
120
+ export function resolveNativeOAuthBinding(config: OcxConfig, options: NativeOAuthBindingOptions = {}): Promise<NativeOAuthBinding> {
121
+ return resolveNativeOAuthBindingForInstance("anthropic", config, options);
122
+ }
123
+
97
124
  /** Resolve the shared pool selector and commit its exact credential generation for dispatch. */
98
- export async function resolveNativeOAuthBinding(
125
+ export async function resolveNativeOAuthBindingForInstance(
126
+ instance: AnthropicInstanceId,
99
127
  config: OcxConfig,
100
128
  options: NativeOAuthBindingOptions = {},
101
129
  ): Promise<NativeOAuthBinding> {
130
+ const routeTarget = options.routeTarget ?? nativeOAuthRouteTarget(instance, config);
131
+ const routing = anthropicRoutingFor(instance);
132
+ const assertOwner = () => {
133
+ if (nativeOAuthRouteTarget(instance, config) !== routeTarget) throw new NativeOAuthSelectionChangedError();
134
+ };
102
135
  const sessionKey = options.sessionKey ?? null;
103
136
  const model = options.model;
104
137
  for (let attempt = 0; attempt < MAX_SELECTION_ATTEMPTS; attempt++) {
105
- const routeDecision = currentRoute(config, model);
106
- const manualSelectionGeneration = captureAnthropicManualSelectionGeneration();
107
- const selection = captureOAuthAccountSelection(PROVIDER);
108
- // Always ask admission first so pause/login/cooldown retains its typed local refusal.
109
- const admittedId = await resolveAnthropicDispatchAccountId(config, sessionKey, routeDecision, model);
110
- if (!routeIsCurrent(config, model, routeDecision)) continue;
111
- const proposed = resolveAnthropicAccountForSession(sessionKey, config, Date.now(), routeDecision, model);
112
- // A lost selection commit discards a recovery proposal: a newer manual/policy choice wins.
138
+ assertOwner();
139
+ const routeDecision = currentRoute(instance, config, model);
140
+ const manualSelectionGeneration = routing.captureAnthropicManualSelectionGeneration();
141
+ const selection = captureOAuthAccountSelection(instance);
142
+ // A recovery candidate may replace a terminal active credential, including pool-off.
143
+ // A newer committed selection or route discards the proposal and uses normal admission.
113
144
  const recoverySelectionMatches = !!options.expectedRecoverySelection
114
145
  && selection?.accountId === options.expectedRecoverySelection.accountId
115
146
  && selection?.revision === options.expectedRecoverySelection.revision;
116
147
  const recoveryRouteMatches = options.expectedRecoveryRouteDecision === undefined
117
148
  || JSON.stringify(options.expectedRecoveryRouteDecision) === JSON.stringify(routeDecision);
118
- const accountId = attempt === 0 && options.candidateAccountId && recoverySelectionMatches && recoveryRouteMatches
119
- ? options.candidateAccountId : admittedId;
149
+ const recoveryAccountId = attempt === 0 && options.candidateAccountId && recoverySelectionMatches && recoveryRouteMatches
150
+ ? options.candidateAccountId : undefined;
151
+ const accountId = recoveryAccountId
152
+ ?? await routing.resolveAnthropicDispatchAccountId(config, sessionKey, routeDecision, model);
153
+ assertOwner();
154
+ if (!routeIsCurrent(instance, config, model, routeDecision)) continue;
155
+ const proposed = routing.resolveAnthropicAccountForSession(sessionKey, config, Date.now(), routeDecision, model);
120
156
  if (!selection) continue;
121
- if (!routeCandidates(getEligibleAnthropicAccounts(Date.now(), model), routeDecision).includes(accountId)) {
157
+ if (!routeCandidates(routing.getEligibleAnthropicAccounts(Date.now(), model), routeDecision).includes(accountId)) {
122
158
  throw new NativeOAuthSelectionChangedError();
123
159
  }
124
- const candidate = await getAnthropicPoolAccessSnapshot(accountId);
125
- if (!routeIsCurrent(config, model, routeDecision)) continue;
126
- const committed = await commitOAuthAccountSelection(PROVIDER, candidate.accountId, {
160
+ const candidate = await routing.getAnthropicPoolAccessSnapshot(accountId);
161
+ assertOwner();
162
+ if (candidate.provider !== instance) throw new NativeOAuthSelectionChangedError();
163
+ if (!routeIsCurrent(instance, config, model, routeDecision)) continue;
164
+ const committed = await commitOAuthAccountSelection(instance, candidate.accountId, {
127
165
  expectedSelection: selection,
128
166
  expectedCredentialGeneration: candidate.generation,
129
167
  requireUsableAccount: true,
130
168
  });
169
+ assertOwner();
131
170
  if (committed) {
132
- if (!routeIsCurrent(config, model, routeDecision)
133
- || manualSelectionGeneration !== captureAnthropicManualSelectionGeneration()) continue;
134
- if (!commitAnthropicSelectionRouting(candidate.accountId, selection, committed, {
171
+ if (!routeIsCurrent(instance, config, model, routeDecision)
172
+ || manualSelectionGeneration !== routing.captureAnthropicManualSelectionGeneration()) continue;
173
+ if (!routing.commitAnthropicSelectionRouting(candidate.accountId, selection, committed, {
135
174
  config, sessionKey, model, routeDecision,
136
175
  reason: accountId === proposed.accountId ? proposed.reason : undefined,
137
176
  expectedCredentialGeneration: candidate.generation,
138
177
  })) continue;
139
- const row = getAccountCredentialWithStatus(PROVIDER, candidate.accountId);
140
- if (!row || credentialGeneration(row.credential) !== candidate.generation) continue;
141
- const binding = { selection: committed, snapshot: candidate, providerAccountUuid: row.credential.accountId, routeDecision, sessionKey, model, config, manualSelectionGeneration };
178
+ const row = getAccountCredentialWithStatus(instance, candidate.accountId);
179
+ if (!row || row.credential.access !== candidate.accessToken || credentialGeneration(row.credential) !== candidate.generation) continue;
180
+ const binding: NativeOAuthBinding = { instance, routeTarget, selection: committed, snapshot: candidate, providerAccountUuid: row.credential.accountId, routeDecision, sessionKey, model, config, manualSelectionGeneration };
142
181
  if (nativeOAuthBindingIsCurrent(binding)) return binding;
143
182
  }
144
183
  }
@@ -150,12 +189,18 @@ export async function resolveNativeOAuthBinding(
150
189
  * selection, plus the same usable credential generation. Checked before every physical send.
151
190
  */
152
191
  export function nativeOAuthBindingIsCurrent(binding: NativeOAuthBinding): boolean {
153
- if (binding.manualSelectionGeneration !== captureAnthropicManualSelectionGeneration()
154
- || !routeIsCurrent(binding.config, binding.model, binding.routeDecision)) return false;
155
- const selected = captureOAuthAccountSelection(PROVIDER);
156
- const row = getAccountCredentialWithStatus(PROVIDER, binding.snapshot.accountId);
157
- const sessionRoute = isAnthropicAccountPoolEnabled(binding.config) && binding.sessionKey
158
- ? resolveAnthropicAccountForSession(binding.sessionKey, binding.config, Date.now(), binding.routeDecision, binding.model) : null;
192
+ const { instance } = binding;
193
+ const routing = anthropicRoutingFor(instance);
194
+ try {
195
+ if (nativeOAuthRouteTarget(instance, binding.config) !== binding.routeTarget) return false;
196
+ } catch { return false; }
197
+ if (binding.snapshot.provider !== instance) return false;
198
+ if (binding.manualSelectionGeneration !== routing.captureAnthropicManualSelectionGeneration()
199
+ || !routeIsCurrent(instance, binding.config, binding.model, binding.routeDecision)) return false;
200
+ const selected = captureOAuthAccountSelection(instance);
201
+ const row = getAccountCredentialWithStatus(instance, binding.snapshot.accountId);
202
+ const sessionRoute = routing.isAnthropicAccountPoolEnabled(binding.config) && binding.sessionKey
203
+ ? routing.resolveAnthropicAccountForSession(binding.sessionKey, binding.config, Date.now(), binding.routeDecision, binding.model) : null;
159
204
  // Another conversation may move the automatic active pointer without revoking this
160
205
  // session's affinity. Manual selection clears affinity and precedes it in the selector.
161
206
  const selectionCurrent = sessionRoute?.reason === "affinity"
@@ -163,8 +208,9 @@ export function nativeOAuthBindingIsCurrent(binding: NativeOAuthBinding): boolea
163
208
  || (selected?.accountId === binding.selection.accountId && selected?.revision === binding.selection.revision);
164
209
  return selectionCurrent
165
210
  && !!row && !row.paused && !row.needsReauth && row.credential.expires > Date.now()
166
- && !getAnthropicAccountHealthSnapshot(binding.snapshot.accountId)
167
- && routeCandidates(getEligibleAnthropicAccounts(Date.now(), binding.model), binding.routeDecision).includes(binding.snapshot.accountId)
211
+ && !routing.getAnthropicAccountHealthSnapshot(binding.snapshot.accountId)
212
+ && routeCandidates(routing.getEligibleAnthropicAccounts(Date.now(), binding.model), binding.routeDecision).includes(binding.snapshot.accountId)
213
+ && row.credential.access === binding.snapshot.accessToken
168
214
  && credentialGeneration(row.credential) === binding.snapshot.generation
169
215
  && row.credential.accountId === binding.providerAccountUuid;
170
216
  }
@@ -0,0 +1,39 @@
1
+ /** Pure caller-forward exclusion shared by Messages ingress and protocol preview. */
2
+ import { resolveInboundModel } from "../claude/inbound-model-options";
3
+ import { routeConcreteModel } from "../router";
4
+ import { configuredAnthropicInstance } from "../providers/anthropic-instance";
5
+ import { hasOwnProvider } from "../config/provider-name";
6
+ import type { OcxConfig } from "../types";
7
+
8
+ export function messagesSelectorTargetsSecondaryInstance(
9
+ config: OcxConfig,
10
+ model: string,
11
+ cc: OcxConfig["claudeCode"] = config.claudeCode,
12
+ ): boolean {
13
+ const selector = resolveInboundModel(model, cc);
14
+ const slash = selector.indexOf("/");
15
+ if (slash <= 0) return false;
16
+ const qualifier = selector.slice(0, slash);
17
+ // An orphan or unmarked B selector must also never forward a caller's credential.
18
+ if (qualifier === "anthropic2") return true;
19
+ // Provider keys are case-sensitive and outrank aliases, including when disabled.
20
+ if (hasOwnProvider(config.providers, qualifier)) return false;
21
+ try {
22
+ // Concrete resolution handles configured provider aliases without picking a combo/account.
23
+ return routeConcreteModel(config, selector).providerName === "anthropic2";
24
+ } catch {
25
+ return config.providers.anthropic2?.alias?.trim().toLowerCase() === qualifier.toLowerCase();
26
+ }
27
+ }
28
+
29
+ /** A missing B row must not fall through the router's generic default-provider path. */
30
+ export function messagesSecondaryInstanceUnavailable(
31
+ config: OcxConfig,
32
+ model: string,
33
+ cc: OcxConfig["claudeCode"] = config.claudeCode,
34
+ ): boolean {
35
+ if (!messagesSelectorTargetsSecondaryInstance(config, model, cc)) return false;
36
+ const provider = config.providers.anthropic2;
37
+ return !provider || provider.disabled === true
38
+ || provider.authMode === "oauth" && configuredAnthropicInstance(config, "anthropic2") !== "anthropic2";
39
+ }