@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
@@ -1,4 +1,15 @@
1
+ import { CURSOR_CAPABILITIES } from "../adapters/cursor/catalog";
1
2
  import { normalizeCursorClaudeId } from "../adapters/cursor/claude-id";
3
+ import { isAnthropicInstanceId } from "../providers/anthropic-instance-id";
4
+
5
+ /**
6
+ * Provider key the compiled price catalogs are written under. Both Anthropic OAuth instances run
7
+ * the same models at the same published prices, so Pool 2 reads Pool 1's rows. Callers apply this
8
+ * only to the built-in catalogs; operator-supplied overlays and tiers stay keyed exactly.
9
+ */
10
+ export function pricingFamilyProvider(provider: string): string {
11
+ return isAnthropicInstanceId(provider) ? "anthropic" : provider;
12
+ }
2
13
 
3
14
  /**
4
15
  * Expected-price overlay for models whose jawcode cost rows are missing or all-zero
@@ -123,13 +134,23 @@ const CURSOR_OPUS_48 = CLAUDE_OPUS_46;
123
134
  // 0.1x most families use. 1M context and 128K output at one flat rate (no long-context tier).
124
135
  const CLAUDE_OPUS_55: Cost4 = { input: 4, output: 20, cacheRead: 0.2, cacheWrite: 5 };
125
136
  // Claude Sonnet 5.5 (claude-sonnet-5-5, released 2026-09-28): the Sonnet 5 tuple, 2 / 10, 5m cache
126
- // write 2.50, cache hit at the standard 0.1x (0.20). No fast mode and no long-context tier.
127
- const CLAUDE_SONNET_55: Cost4 = { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 };
137
+ // write 2.50; cache hits cut to 0.05x (0.10) on 2026-10-08. No fast mode or long-context tier.
138
+ const CLAUDE_SONNET_55: Cost4 = { input: 2, output: 10, cacheRead: 0.1, cacheWrite: 2.5 };
139
+ // Haiku 5.5 (2026-10-07): <=100K prompt base tuple; every rate is 5x above 100K.
140
+ const CLAUDE_HAIKU_55: Cost4 = { input: 0.1, output: 0.5, cacheRead: 0.01, cacheWrite: 0.125 };
128
141
  const ANTHROPIC_PRICING = "https://platform.claude.com/docs/en/about-claude/pricing (official; 5m cache-write tier)";
129
142
  const CLAUDE_OPUS_5_SOURCE = `anthropic official Claude Opus 5 ${ANTHROPIC_PRICING}`;
130
143
  const CLAUDE_OPUS_55_SOURCE = `anthropic official Claude Opus 5.5 ${ANTHROPIC_PRICING}; cache hit = 0.05x base input`;
131
- const CLAUDE_SONNET_55_SOURCE = `anthropic official Claude Sonnet 5.5 ${ANTHROPIC_PRICING}`;
132
- const CURSOR_SONNET_55_PRICING = "https://cursor.com/docs/models/claude-sonnet-5-5 (same list rate as Anthropic)";
144
+ const CLAUDE_SONNET_55_SOURCE = `anthropic official Claude Sonnet 5.5 ${ANTHROPIC_PRICING}; cache hit = 0.05x base input (2026-10-08 cut)`;
145
+ const CLAUDE_HAIKU_55_SOURCE = `anthropic official Claude Haiku 5.5 ${ANTHROPIC_PRICING}; >100K prompt prices every token category at 5x`;
146
+ const CURSOR_HAIKU_55_PRICING = "https://cursor.com/docs/models/claude-haiku-5-5 (same base and >100K rates as Anthropic)";
147
+ const DEVIN_HAIKU_55_SOURCE = `derived (preemptive): Devin live catalog and modelCostData do not list claude-haiku-5-5 yet; Anthropic reference price ${ANTHROPIC_PRICING}`;
148
+ const VENICE_55_SOURCE = "derived: Venice listing on https://models.dev/api.json (2026-10-08), 1.25x Anthropic list price";
149
+ // Exact lookup must cover regular effort ids as well as every spelling Cursor can emit.
150
+ const CURSOR_HAIKU_55_IDS = ["claude-haiku-5-5", "claude-haiku-5.5", "claude-5.5-haiku"].flatMap(base =>
151
+ [base, ...CURSOR_CAPABILITIES["claude-haiku-5-5"]!.variants.regular!.levels.map(level => `${base}-${level}`)],
152
+ );
153
+ const CURSOR_SONNET_55_PRICING = `https://cursor.com/docs/models/claude-sonnet-5-5 (same list rate as Anthropic; cache hit 0.05x) ${ANTHROPIC_PRICING}`;
133
154
  const DEVIN_SONNET_55_SOURCE = `derived (preemptive): Devin's live catalog and modelCostData table do not list claude-sonnet-5-5 yet; Anthropic list price shown as estimate ${ANTHROPIC_PRICING}`;
134
155
  const CURSOR_OPUS_55_PRICING = "https://cursor.com/docs/models/claude-opus-5-5 (Cursor Other Models pool; same list rate as Anthropic, Fast Mode billed separately)";
135
156
  const CURSOR_OPUS_48_FAST_PRICING = "https://cursor.com/docs/models/claude-opus-4-8";
@@ -291,11 +312,26 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [
291
312
  { provider: "anthropic-apikey", modelId: "claude-opus-5-5", cost4: CLAUDE_OPUS_55, source: CLAUDE_OPUS_55_SOURCE, verifiedAt: "2026-09-23", status: "verified" },
292
313
  // Cursor canonicalizes every Opus 5.5 spelling (thinking/effort/fast suffixes) onto this row.
293
314
  { provider: "cursor", modelId: "claude-opus-5-5", cost4: CLAUDE_OPUS_55, source: CURSOR_OPUS_55_PRICING, verifiedAt: "2026-09-23", status: "verified" },
315
+ // Haiku 5.5: official Anthropic and Cursor rates; reseller/preemptive rows remain estimates.
316
+ ...["anthropic", "anthropic-apikey"].map((provider): ExpectedPriceOverlay => ({
317
+ provider, modelId: "claude-haiku-5-5", cost4: CLAUDE_HAIKU_55,
318
+ source: CLAUDE_HAIKU_55_SOURCE, verifiedAt: "2026-10-08", status: "verified",
319
+ })),
320
+ ...CURSOR_HAIKU_55_IDS.map((modelId): ExpectedPriceOverlay => ({
321
+ provider: "cursor", modelId, cost4: CLAUDE_HAIKU_55,
322
+ source: CURSOR_HAIKU_55_PRICING, verifiedAt: "2026-10-08", status: "verified",
323
+ })),
324
+ ...["devin", "devin-cli"].map((provider): ExpectedPriceOverlay => ({
325
+ provider, modelId: "claude-haiku-5-5", cost4: CLAUDE_HAIKU_55,
326
+ source: DEVIN_HAIKU_55_SOURCE, verifiedAt: "2026-10-08", status: "verified-derived",
327
+ })),
328
+ { provider: "venice", modelId: "claude-haiku-5-5", cost4: { input: 0.125, output: 0.625, cacheRead: 0.0125, cacheWrite: 0.15625 }, source: VENICE_55_SOURCE, verifiedAt: "2026-10-08", status: "verified-derived" },
329
+ { provider: "venice", modelId: "claude-sonnet-5-5", cost4: { input: 2.5, output: 12.5, cacheRead: 0.125, cacheWrite: 3.125 }, source: VENICE_55_SOURCE, verifiedAt: "2026-10-08", status: "verified-derived" },
294
330
  // Claude Sonnet 5.5. Same layering as Opus 5.5: the anthropic bundle row wins for the bare id,
295
331
  // these cover account-label namespaces, and Cursor publishes the list rate on its model page.
296
- { provider: "anthropic", modelId: "claude-sonnet-5-5", cost4: CLAUDE_SONNET_55, source: CLAUDE_SONNET_55_SOURCE, verifiedAt: "2026-09-29", status: "verified" },
297
- { provider: "anthropic-apikey", modelId: "claude-sonnet-5-5", cost4: CLAUDE_SONNET_55, source: CLAUDE_SONNET_55_SOURCE, verifiedAt: "2026-09-29", status: "verified" },
298
- { provider: "cursor", modelId: "claude-sonnet-5-5", cost4: CLAUDE_SONNET_55, source: CURSOR_SONNET_55_PRICING, verifiedAt: "2026-09-29", status: "verified" },
332
+ { provider: "anthropic", modelId: "claude-sonnet-5-5", cost4: CLAUDE_SONNET_55, source: CLAUDE_SONNET_55_SOURCE, verifiedAt: "2026-10-08", status: "verified" },
333
+ { provider: "anthropic-apikey", modelId: "claude-sonnet-5-5", cost4: CLAUDE_SONNET_55, source: CLAUDE_SONNET_55_SOURCE, verifiedAt: "2026-10-08", status: "verified" },
334
+ { provider: "cursor", modelId: "claude-sonnet-5-5", cost4: CLAUDE_SONNET_55, source: CURSOR_SONNET_55_PRICING, verifiedAt: "2026-10-08", status: "verified" },
299
335
  // MiniMax M2.1 highspeed — published PAYG price (verified).
300
336
  { provider: "minimax", modelId: "MiniMax-M2.1-highspeed", cost4: MINIMAX_M21_HIGHSPEED, source: MINIMAX_PRICING, verifiedAt: "2026-07-20", status: "verified" },
301
337
  { provider: "minimax-cn", modelId: "MiniMax-M2.1-highspeed", cost4: MINIMAX_M21_HIGHSPEED, source: MINIMAX_PRICING, verifiedAt: "2026-07-20", status: "verified" },
@@ -371,10 +407,10 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [
371
407
  // Alias without the Antigravity "-thinking" suffix (if logs/UI ever surface it).
372
408
  { provider: "google-antigravity", modelId: "claude-opus-4-6", cost4: CLAUDE_OPUS_46, source: `anthropic official Claude Opus 4.6 ${ANTHROPIC_PRICING}`, verifiedAt: "2026-07-23", status: "verified" },
373
409
  // Antigravity uses subscription quota; these are derived Anthropic reference estimates.
374
- { provider: "google-antigravity", modelId: "claude-sonnet-5-5", cost4: CLAUDE_SONNET_55, source: `derived: Anthropic Claude Sonnet 5.5 reference price; ${ANTHROPIC_PRICING}`, verifiedAt: "2026-10-03", status: "verified-derived" },
375
- { provider: "google-antigravity", modelId: "claude-sonnet-5-5-low", cost4: CLAUDE_SONNET_55, source: `derived: Anthropic Claude Sonnet 5.5 reference price; ${ANTHROPIC_PRICING}`, verifiedAt: "2026-10-03", status: "verified-derived" },
376
- { provider: "google-antigravity", modelId: "claude-sonnet-5-5-medium", cost4: CLAUDE_SONNET_55, source: `derived: Anthropic Claude Sonnet 5.5 reference price; ${ANTHROPIC_PRICING}`, verifiedAt: "2026-10-03", status: "verified-derived" },
377
- { provider: "google-antigravity", modelId: "claude-sonnet-5-5-high", cost4: CLAUDE_SONNET_55, source: `derived: Anthropic Claude Sonnet 5.5 reference price; ${ANTHROPIC_PRICING}`, verifiedAt: "2026-10-03", status: "verified-derived" },
410
+ { provider: "google-antigravity", modelId: "claude-sonnet-5-5", cost4: CLAUDE_SONNET_55, source: `derived: Anthropic Claude Sonnet 5.5 reference price (cache hit 0.05x); ${ANTHROPIC_PRICING}`, verifiedAt: "2026-10-08", status: "verified-derived" },
411
+ { provider: "google-antigravity", modelId: "claude-sonnet-5-5-low", cost4: CLAUDE_SONNET_55, source: `derived: Anthropic Claude Sonnet 5.5 reference price (cache hit 0.05x); ${ANTHROPIC_PRICING}`, verifiedAt: "2026-10-08", status: "verified-derived" },
412
+ { provider: "google-antigravity", modelId: "claude-sonnet-5-5-medium", cost4: CLAUDE_SONNET_55, source: `derived: Anthropic Claude Sonnet 5.5 reference price (cache hit 0.05x); ${ANTHROPIC_PRICING}`, verifiedAt: "2026-10-08", status: "verified-derived" },
413
+ { provider: "google-antigravity", modelId: "claude-sonnet-5-5-high", cost4: CLAUDE_SONNET_55, source: `derived: Anthropic Claude Sonnet 5.5 reference price (cache hit 0.05x); ${ANTHROPIC_PRICING}`, verifiedAt: "2026-10-08", status: "verified-derived" },
378
414
  { provider: "google-antigravity", modelId: "claude-opus-5-5", cost4: CLAUDE_OPUS_55, source: `derived: Anthropic Claude Opus 5.5 reference price; ${ANTHROPIC_PRICING}`, verifiedAt: "2026-10-03", status: "verified-derived" },
379
415
  { provider: "google-antigravity", modelId: "claude-opus-5-5-low", cost4: CLAUDE_OPUS_55, source: `derived: Anthropic Claude Opus 5.5 reference price; ${ANTHROPIC_PRICING}`, verifiedAt: "2026-10-03", status: "verified-derived" },
380
416
  { provider: "google-antigravity", modelId: "claude-opus-5-5-medium", cost4: CLAUDE_OPUS_55, source: `derived: Anthropic Claude Opus 5.5 reference price; ${ANTHROPIC_PRICING}`, verifiedAt: "2026-10-03", status: "verified-derived" },
@@ -482,7 +518,7 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [
482
518
  { provider: "devin-cli", modelId: "claude-opus-5", cost4: CLAUDE_OPUS_46, source: DEVIN_PRICING, verifiedAt: "2026-09-13", status: "verified-derived" },
483
519
  { provider: "devin-cli", modelId: "claude-opus-5-5", cost4: CLAUDE_OPUS_55, source: `derived: live Devin catalog lists claude-opus-5-5 but Devin's modelCostData table does not yet; Anthropic list price shown as estimate ${ANTHROPIC_PRICING}`, verifiedAt: "2026-09-23", status: "verified-derived" },
484
520
  { provider: "devin-cli", modelId: "claude-fable-5-1", cost4: CLAUDE_FABLE_51, source: DEVIN_PRICING, verifiedAt: "2026-09-13", status: "verified-derived" },
485
- { provider: "devin-cli", modelId: "claude-sonnet-5-5", cost4: CLAUDE_SONNET_55, source: DEVIN_SONNET_55_SOURCE, verifiedAt: "2026-09-29", status: "verified-derived" },
521
+ { provider: "devin-cli", modelId: "claude-sonnet-5-5", cost4: CLAUDE_SONNET_55, source: DEVIN_SONNET_55_SOURCE, verifiedAt: "2026-10-08", status: "verified-derived" },
486
522
  { provider: "devin-cli", modelId: "claude-sonnet-5", cost4: DEVIN_SONNET_5, source: DEVIN_PRICING, verifiedAt: "2026-09-13", status: "verified-derived" },
487
523
  { provider: "devin-cli", modelId: "glm-5-3", cost4: GLM_53, source: DEVIN_PRICING, verifiedAt: "2026-09-13", status: "verified-derived" },
488
524
  { provider: "devin-cli", modelId: "kimi-k3", cost4: DEVIN_KIMI_K3, source: DEVIN_PRICING, verifiedAt: "2026-09-13", status: "verified-derived" },
@@ -501,7 +537,7 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [
501
537
  { provider: "devin", modelId: "claude-opus-4-8", cost4: CLAUDE_OPUS_46, source: DEVIN_PRICING, verifiedAt: "2026-09-13", status: "verified-derived" },
502
538
  { provider: "devin", modelId: "claude-opus-5-5", cost4: CLAUDE_OPUS_55, source: `derived: live Devin catalog lists claude-opus-5-5 but Devin's modelCostData table does not yet; Anthropic list price shown as estimate ${ANTHROPIC_PRICING}`, verifiedAt: "2026-09-23", status: "verified-derived" },
503
539
  { provider: "devin", modelId: "claude-fable-5-1", cost4: CLAUDE_FABLE_51, source: DEVIN_PRICING, verifiedAt: "2026-09-13", status: "verified-derived" },
504
- { provider: "devin", modelId: "claude-sonnet-5-5", cost4: CLAUDE_SONNET_55, source: DEVIN_SONNET_55_SOURCE, verifiedAt: "2026-09-29", status: "verified-derived" },
540
+ { provider: "devin", modelId: "claude-sonnet-5-5", cost4: CLAUDE_SONNET_55, source: DEVIN_SONNET_55_SOURCE, verifiedAt: "2026-10-08", status: "verified-derived" },
505
541
  { provider: "devin", modelId: "claude-sonnet-5", cost4: DEVIN_SONNET_5, source: DEVIN_PRICING, verifiedAt: "2026-09-13", status: "verified-derived" },
506
542
  { provider: "devin", modelId: "glm-5-2", cost4: GLM_52, source: `enterprise list column (self-serve shows an unannounced 0 promo); ${DEVIN_PRICING}`, verifiedAt: "2026-09-13", status: "verified-derived" },
507
543
  { provider: "devin", modelId: "kimi-k2-7", cost4: DEVIN_KIMI_K27, source: DEVIN_PRICING, verifiedAt: "2026-09-13", status: "verified-derived" },
@@ -705,6 +741,8 @@ export interface ContextTier {
705
741
  const OPENAI_LONG_CONTEXT: Cost4 = { input: 2, output: 1.5, cacheRead: 2, cacheWrite: 2 };
706
742
  /** xAI and MiniMax double every rate uniformly past their thresholds. */
707
743
  const UNIFORM_DOUBLE: Cost4 = { input: 2, output: 2, cacheRead: 2, cacheWrite: 2 };
744
+ // Haiku 5.5 reprices the whole request above 100K (official pricing, read 2026-10-08).
745
+ const HAIKU_55_LONG_CONTEXT: Cost4 = { input: 5, output: 5, cacheRead: 5, cacheWrite: 5 };
708
746
 
709
747
  const OPENAI_PRICING_DOC = "https://developers.openai.com/api/docs/pricing";
710
748
  const OPENAI_CONTEXT_MODELS = [
@@ -723,6 +761,19 @@ const OPENAI_CONTEXT_MODELS = [
723
761
  ];
724
762
 
725
763
  export const CONTEXT_TIERS: readonly ContextTier[] = [
764
+ // Reference bands on preemptive/subscription surfaces follow their reference price, not billing.
765
+ ...[
766
+ ...["anthropic", "anthropic-apikey", "opencode-go", "opencode-zen", "venice", "github-copilot", "devin", "devin-cli", "claude-cli"].map(provider => [provider, "claude-haiku-5-5"]),
767
+ ...["", "global.", "us.", "eu.", "jp.", "au."].map(prefix => ["amazon-bedrock", `${prefix}anthropic.claude-haiku-5-5`]),
768
+ ...["openrouter", "vercel-ai-gateway", "zenmux"].map(provider => [provider, "anthropic/claude-haiku-5.5"]),
769
+ ["cloudflare-ai-gateway", "anthropic/claude-haiku-5-5"], ["kiro", "claude-haiku-5.5"],
770
+ ...CURSOR_HAIKU_55_IDS.map(modelId => ["cursor", modelId]),
771
+ ].map(([provider, modelId]): ContextTier => ({
772
+ provider, modelId, thresholdInputTokens: 100_000, inclusive: false,
773
+ multiplier: HAIKU_55_LONG_CONTEXT,
774
+ source: provider === "cursor" ? CURSOR_HAIKU_55_PRICING : ANTHROPIC_PRICING,
775
+ verifiedAt: "2026-10-08",
776
+ })),
726
777
  // API-reference estimates do not apply subscription-only exemptions or multipliers.
727
778
  ...["openai", "openai-apikey"].flatMap(provider =>
728
779
  OPENAI_CONTEXT_MODELS.map((modelId): ContextTier => ({
@@ -811,7 +862,8 @@ export function findContextTier(
811
862
  modelId: string,
812
863
  tiers: readonly ContextTier[] = CONTEXT_TIERS,
813
864
  ): ContextTier | undefined {
814
- return tiers.find(tier => tier.provider === provider && tier.modelId === modelId);
865
+ const familyProvider = tiers === CONTEXT_TIERS ? pricingFamilyProvider(provider) : provider;
866
+ return tiers.find(tier => tier.provider === familyProvider && tier.modelId === modelId);
815
867
  }
816
868
 
817
869
  /** Whether a raw input-token count crosses the tier's published boundary. */
@@ -1,3 +1,6 @@
1
+ import { resolveAnthropicHelperSnapshot, fetchAnthropicHelper, anthropicHelperMessagesUrl } from "../sidecar/anthropic-binding";
2
+ import { isAnthropicInstanceId } from "../providers/anthropic-instance-id";
3
+ import type { OAuthAccessSnapshot } from "../oauth";
1
4
  import type { OcxConfig, OcxProviderConfig } from "../types";
2
5
  import { sidecarThinkingOff } from "../adapters/anthropic-model-contract";
3
6
  import { CLAUDE_CODE_HEADERS, claudeCodeSessionId } from "../adapters/client-fingerprint";
@@ -159,8 +162,15 @@ export async function describeImageAnthropic(
159
162
  if (!image.block) return { text: "", error: image.error ?? "invalid image" };
160
163
 
161
164
  let token: string;
165
+ let snapshot: OAuthAccessSnapshot | undefined;
166
+ const capturedTarget = provider.baseUrl;
162
167
  try {
163
- token = await getAnthropicSidecarAccessToken(providerName, settings.model, config);
168
+ if (config && isAnthropicInstanceId(providerName)) {
169
+ snapshot = await resolveAnthropicHelperSnapshot(config, providerName, settings.model);
170
+ token = snapshot.accessToken;
171
+ } else {
172
+ token = await getAnthropicSidecarAccessToken(providerName, settings.model, config);
173
+ }
164
174
  } catch (error) {
165
175
  return { text: "", error: `anthropic vision sidecar auth failed: ${publicOAuthAuthenticationErrorMessage(error)}` };
166
176
  }
@@ -195,13 +205,16 @@ export async function describeImageAnthropic(
195
205
 
196
206
  // Anthropic image blocks have no detail field, but detail remains part of the cache identity.
197
207
  void detail;
198
- const base = provider.baseUrl.replace(/\/v1\/?$/, "");
208
+ const url = anthropicHelperMessagesUrl(provider.baseUrl);
209
+ const dispatch = (target: string, init: RequestInit) => snapshot && config
210
+ ? fetchAnthropicHelper(config, snapshot, settings.model, capturedTarget, target, init)
211
+ : fetch(target, init);
199
212
  const linkedSignal = signalWithTimeout(settings.timeoutMs, abortSignal);
200
213
  const sidecarExit = sidecarEnter("vision");
201
214
  const startedAt = Date.now();
202
215
  try {
203
216
  const res = await fetchWithResetRetry(
204
- recovery => fetch(`${base}/v1/messages`, applyUpstreamRecoveryInit({
217
+ recovery => dispatch(url, applyUpstreamRecoveryInit({
205
218
  method: "POST",
206
219
  redirect: "manual",
207
220
  headers,
@@ -217,6 +217,9 @@ function descriptionIdentity(job: ImageJob, plan: VisionPlan): { key: string; pe
217
217
  key: JSON.stringify([
218
218
  plan.backend,
219
219
  plan.settings.model,
220
+ // Explicit identity: the resolved pool and reasoning. Settings that only gate or bound the call
221
+ // (enabled, timeoutMs, maxDescriptionsPerTurn) never split the cache.
222
+ ...(plan.backend === "anthropic" ? [plan.anthropicSidecar?.providerName ?? null, plan.settings.reasoning] : []),
220
223
  ...(plan.backend === "openai" ? [plan.settings.reasoning] : []),
221
224
  job.detail ?? "high",
222
225
  imageHash,
@@ -11,7 +11,7 @@ import {
11
11
  modelAcceptsImageInput,
12
12
  } from "./eligibility";
13
13
  import { normalizeVisionReasoningForModel } from "./reasoning";
14
- import { resolveSidecarAuth } from "../sidecar/auth";
14
+ import { inheritedAnthropicInstance, resolveSidecarAuth, resolveAnthropicHelperInstance, resolveAnthropicSidecarAuth, withAnthropicHelperRefusal } from "../sidecar/auth";
15
15
  import { DEFAULT_VISION_TIMEOUT_MS, MAX_VISION_TIMEOUT_MS, MIN_VISION_TIMEOUT_MS } from "./timeout-bounds";
16
16
  import { carriesImages } from "./image-rewrite";
17
17
 
@@ -50,8 +50,9 @@ export interface AnthropicVisionProvider {
50
50
  * First enabled Anthropic OAuth provider whose active stored account is not marked for reauth.
51
51
  * Delegates to the shared sidecar auth module (#2188) — same predicate as web-search.
52
52
  */
53
- export function findAnthropicVisionProvider(config: OcxConfig): AnthropicVisionProvider | undefined {
54
- const auth = resolveSidecarAuth(config);
53
+ export function findAnthropicVisionProvider(config: OcxConfig, parentProviderName?: string): AnthropicVisionProvider | undefined {
54
+ const instance = resolveAnthropicHelperInstance(config, { backendFamily: "anthropic", anthropicInstance: config.visionSidecar?.anthropicInstance, parentProviderName });
55
+ const auth = resolveSidecarAuth(config, instance);
55
56
  if (!auth.isAnthropicAuth || !auth.anthropicProviderName || !auth.anthropicProvider) return undefined;
56
57
  return { providerName: auth.anthropicProviderName, provider: auth.anthropicProvider, config };
57
58
  }
@@ -68,6 +69,16 @@ export function resolveVisionBackend(
68
69
  return anthropicSidecar ? "anthropic" : "openai";
69
70
  }
70
71
 
72
+ /** Existing family preference, with a builtin parent contributing its own auth availability. */
73
+ function preferredVisionBackend(config: OcxConfig, parentProviderName?: string): "openai" | "anthropic" {
74
+ const cfg = config.visionSidecar ?? {};
75
+ const requested = cfg.anthropicInstance ?? inheritedAnthropicInstance(config, parentProviderName);
76
+ const exact = requested ? resolveAnthropicSidecarAuth(config, requested) : undefined;
77
+ const legacy = findAnthropicVisionProvider({ ...config, visionSidecar: { ...cfg, anthropicInstance: undefined } });
78
+ return resolveVisionBackend(cfg.backend, exact
79
+ ? { providerName: exact.anthropicProviderName, provider: exact.anthropicProvider, config } : legacy);
80
+ }
81
+
71
82
  /** Native model used by the OpenAI vision helper, including its bounded default. */
72
83
  export function resolveOpenAiVisionModel(config: Pick<OcxConfig, "visionSidecar">): string {
73
84
  const configured = config.visionSidecar?.model;
@@ -170,7 +181,7 @@ export function shouldResolveOpenAiVisionSidecar(
170
181
  const cfg = config.visionSidecar ?? {};
171
182
  if (cfg.enabled === false) return false;
172
183
  if (usableRoutedVisionModel(config)) return false;
173
- return resolveVisionBackend(cfg.backend, findAnthropicVisionProvider(config)) === "openai";
184
+ return preferredVisionBackend(config, providerName) === "openai";
174
185
  }
175
186
 
176
187
  export interface VisionPlan {
@@ -241,8 +252,10 @@ export function planVisionSidecar(
241
252
  }
242
253
  // A non-dispatchable routed configuration keeps the legacy backend fallback below.
243
254
 
244
- const anthropicSidecar = findAnthropicVisionProvider(config);
245
- const backend = resolveVisionBackend(cfg.backend, anthropicSidecar);
255
+ const backend = preferredVisionBackend(config, options.providerName);
256
+ // An unavailable selected pool degrades to "no plan" (images stripped); it never fails the request.
257
+ const anthropicSidecar = backend === "anthropic"
258
+ ? withAnthropicHelperRefusal("vision", () => findAnthropicVisionProvider(config, options.providerName)) : undefined;
246
259
  // A namespaced routed model must never reach the forward/OAuth executors
247
260
  // (they POST the string verbatim); the effective-model resolver falls back
248
261
  // to each side's default in that case.
@@ -1,3 +1,4 @@
1
+ import { AnthropicHelperUnavailableError } from "../sidecar/auth";
1
2
  /**
2
3
  * Serve Codex's built-in `/v1/alpha/search` when no ChatGPT forward provider exists.
3
4
  *
@@ -101,7 +102,12 @@ export function resolveAlphaSearchSidecar(config: OcxConfig): AlphaSearchSidecar
101
102
  if (backend === "openai") return { status: "unconfigured" };
102
103
  switch (backend) {
103
104
  case "anthropic": {
104
- const found = findAnthropicSidecarProvider(config);
105
+ let found;
106
+ try { found = findAnthropicSidecarProvider(config); }
107
+ catch (error) {
108
+ if (error instanceof AnthropicHelperUnavailableError) return { status: "missing-credential", backend };
109
+ throw error;
110
+ }
105
111
  return found
106
112
  ? { status: "ready", sidecar: { backend, providerName: found.providerName, provider: found.provider, config } }
107
113
  : { status: "missing-credential", backend };
@@ -1,3 +1,6 @@
1
+ import { resolveAnthropicHelperSnapshot, fetchAnthropicHelper, anthropicHelperMessagesUrl } from "../sidecar/anthropic-binding";
2
+ import { isAnthropicInstanceId } from "../providers/anthropic-instance-id";
3
+ import type { OAuthAccessSnapshot } from "../oauth";
1
4
  import type { OcxConfig, OcxProviderConfig } from "../types";
2
5
  import { publicOAuthAuthenticationErrorMessage } from "../oauth";
3
6
  import { getAnthropicSidecarAccessToken } from "../oauth/anthropic-routing";
@@ -167,11 +170,17 @@ export async function runAnthropicWebSearch(
167
170
  abortSignal?: AbortSignal,
168
171
  config?: OcxConfig,
169
172
  ): Promise<SidecarOutcome> {
170
- const base = provider.baseUrl.replace(/\/v1\/?$/, "");
171
- const url = `${base}/v1/messages`;
173
+ const url = anthropicHelperMessagesUrl(provider.baseUrl);
172
174
  let token: string;
175
+ let snapshot: OAuthAccessSnapshot | undefined;
176
+ const capturedTarget = provider.baseUrl;
173
177
  try {
174
- token = await getAnthropicSidecarAccessToken(providerName, settings.model, config);
178
+ if (config && isAnthropicInstanceId(providerName)) {
179
+ snapshot = await resolveAnthropicHelperSnapshot(config, providerName, settings.model);
180
+ token = snapshot.accessToken;
181
+ } else {
182
+ token = await getAnthropicSidecarAccessToken(providerName, settings.model, config);
183
+ }
175
184
  } catch (e) {
176
185
  return { text: "", sources: [], error: `anthropic sidecar auth failed: ${publicOAuthAuthenticationErrorMessage(e)}` };
177
186
  }
@@ -205,6 +214,9 @@ export async function runAnthropicWebSearch(
205
214
  stream: true,
206
215
  };
207
216
 
217
+ const dispatch = (target: string, init: RequestInit) => snapshot && config
218
+ ? fetchAnthropicHelper(config, snapshot, settings.model, capturedTarget, target, init)
219
+ : fetch(target, init);
208
220
  const linkedSignal = signalWithTimeout(settings.timeoutMs, abortSignal);
209
221
  const sidecarExit = sidecarEnter("web-search");
210
222
  const t0 = Date.now();
@@ -212,7 +224,7 @@ export async function runAnthropicWebSearch(
212
224
  const res = await fetchWithResetRetry(
213
225
  // The replay needs `keepalive: false` to leave the half-closed pooled socket; Bun has
214
226
  // ignored a bare `Connection: close` (oven-sh/bun#20492).
215
- recovery => fetch(url, applyUpstreamRecoveryInit({
227
+ recovery => dispatch(url, applyUpstreamRecoveryInit({
216
228
  method: "POST",
217
229
  redirect: "manual",
218
230
  headers,
@@ -6,7 +6,7 @@ import type { CodexAuthPolicyConfig } from "../codex/auth-context";
6
6
  import { isCodexReserveRequestEligible } from "../codex/loopback-target";
7
7
  import type { DataPlaneAdmission } from "../server/auth-cors";
8
8
  import type { ResolvedOpenAiForwardSidecar } from "../providers/openai-sidecar";
9
- import { resolveSidecarAuth } from "../sidecar/auth";
9
+ import { resolveSidecarAuth, resolveAnthropicHelperInstance, withAnthropicHelperRefusal } from "../sidecar/auth";
10
10
  import { validateXaiSearchOptions, type XaiSearchOptions } from "./xai-executor";
11
11
  import type { OcxWebSearchSidecarConfig } from "../types";
12
12
  import { DEFAULT_STALL_TIMEOUT_SEC } from "../stall-timeout";
@@ -168,11 +168,15 @@ export function planWebSearch(
168
168
  const connectTimeoutMs = config.connectTimeoutMs ?? 200_000;
169
169
  // Shared auth state (#2188): presence only — backend PREFERENCE stays with
170
170
  // resolveSidecarBackend's explicit-or-openai contract.
171
- const auth = resolveSidecarAuth(config);
171
+ const backend = resolveSidecarBackend(cfg.backend);
172
+ // Only the anthropic backend can be refused; an unavailable selected pool means no plan, so the
173
+ // main request takes the normal path instead of failing or borrowing another pool.
174
+ const auth = withAnthropicHelperRefusal("web-search", () => resolveSidecarAuth(config, resolveAnthropicHelperInstance(config,
175
+ { backendFamily: backend, anthropicInstance: cfg.anthropicInstance, parentProviderName: options.providerName })));
176
+ if (!auth) return undefined;
172
177
  const anthropicSidecar = auth.isAnthropicAuth && auth.anthropicProviderName && auth.anthropicProvider
173
178
  ? { providerName: auth.anthropicProviderName, provider: auth.anthropicProvider, config }
174
179
  : undefined;
175
- const backend = resolveSidecarBackend(cfg.backend);
176
180
  const maxSearches = cfg.maxSearchesPerTurn ?? DEFAULT_MAX_SEARCHES;
177
181
  const stallTimeoutSec = webSearchStallTimeoutSec(
178
182
  config.stallTimeoutSec,
@@ -1,6 +1,7 @@
1
1
  import type { AdapterRequest, IncomingMeta, ProviderAdapter } from "../adapters/base";
2
2
  import type { AdapterEvent, OcxConfig, OcxMessage, OcxParsedRequest, OcxProviderConfig, OcxProviderOpaqueToolCallMetadata, OcxThinkingContent, OcxUsage, RateLimitRetryPolicy } from "../types";
3
3
  import { namespacedToolName, toolChoiceToolPredicate } from "../types";
4
+ import { isAnthropicInstanceId } from "../providers/anthropic-instance-id";
4
5
  import { cloneProviderOpaqueToolCallMetadata } from "../responses/provider-opaque-metadata";
5
6
  import type { AttemptRecoveryKind } from "../usage/log";
6
7
  import { isTruncatedStopReason } from "../responses/truncated-stop-reason";
@@ -322,7 +323,7 @@ export interface WebSearchLoopDeps {
322
323
  /** Observe the exact adapter request selected for each routed-model iteration. */
323
324
  onRequestBuilt?: (request: AdapterRequest) => void;
324
325
  /** Request-scoped executor retains the core's selection binding across loop retries. */
325
- fetchForRequest?: (request: AdapterRequest, parsed: OcxParsedRequest) => typeof globalThis.fetch;
326
+ fetchForRequest?: (request: AdapterRequest, parsed: OcxParsedRequest, adapter: ProviderAdapter) => typeof globalThis.fetch;
326
327
  /** Called before each routed-model dispatch in the loop, for attempt telemetry. Same-target 429 replays pass the `rate-limit-429` recovery kind. */
327
328
  onAttemptSend?: (recovery?: AttemptRecoveryKind) => void;
328
329
  /**
@@ -353,6 +354,8 @@ export interface WebSearchLoopDeps {
353
354
  | { adapter: ProviderAdapter; recoveryKind: AttemptRecoveryKind }
354
355
  | null
355
356
  | Promise<{ adapter: ProviderAdapter; recoveryKind: AttemptRecoveryKind } | null>;
357
+ /** Release a credential-hop permit if this iteration ended before the adapter used it. */
358
+ onIterationEnd?: () => void;
356
359
  /** Opt-in same-target 429 policy (key-auth providers). When present, 429 replays on the SAME key before on429 rotation. */
357
360
  retryOn429Policy?: Required<RateLimitRetryPolicy> | null;
358
361
  /** Called only when the final bridged Responses stream reaches completed or incomplete. */
@@ -491,7 +494,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
491
494
  cachedRequest = request;
492
495
  cachedAdapter = requestAdapter;
493
496
  }
494
- const requestFetch = headerDeadline.pacedFetch(deps.fetchForRequest?.(request, iterParsed) ?? routedProviderFetch);
497
+ const requestFetch = headerDeadline.pacedFetch(deps.fetchForRequest?.(request, iterParsed, requestAdapter) ?? routedProviderFetch);
495
498
  let response: Response;
496
499
  try {
497
500
  if (requestAdapter.fetchResponse) {
@@ -501,7 +504,10 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
501
504
  timeoutMs: connectTimeoutMs,
502
505
  returnRawErrors: true,
503
506
  stream: true,
507
+ sendBudget: deps.incomingMeta.sendBudget,
508
+ onPhysicalSend: deps.incomingMeta.onPhysicalSend,
504
509
  executor: requestFetch,
510
+ onRecoveryWithheld: deps.incomingMeta.onRecoveryWithheld,
505
511
  });
506
512
  } else {
507
513
  response = await fetchWithResetRetry(
@@ -571,18 +577,36 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
571
577
  // 429 key-failover parity with the normal routed path: rotate pool keys until one responds
572
578
  // or the pool is exhausted (deps.on429 returns null — cooldown map guarantees termination).
573
579
  while ((prepared.response.status === 429
574
- || (prepared.response.status === 403 && deps.incomingMeta?.providerName === "anthropic" && !accountRefusalOutputStarted)
580
+ || (prepared.response.status === 401 && isAnthropicInstanceId(deps.incomingMeta?.providerName)
581
+ && !accountRefusalOutputStarted)
582
+ || (prepared.response.status === 403
583
+ && (isAnthropicInstanceId(deps.incomingMeta?.providerName) || deps.incomingMeta?.providerName === "google-antigravity")
584
+ && !accountRefusalOutputStarted)
575
585
  || (iterParsed._kiroAuthContext && (prepared.response.status === 400 || prepared.response.status === 403))) && deps.on429) {
576
586
  const rotated = await deps.on429(prepared.response.headers.get("retry-after"), prepared.response.headers,
577
587
  iterParsed, prepared.response);
578
588
  if (!rotated) break;
579
- // Never let a broken body's cancel promise outlive the cumulative header deadline. Observe
580
- // it, but proceed immediately to the rotated fetch under the SAME deadline signal.
581
- try { void prepared.response.body?.cancel().catch(() => {}); } catch { /* already closed */ }
589
+ const retainRefusal = prepared.response.status === 403
590
+ && deps.incomingMeta?.providerName === "google-antigravity" && rotated.recoveryKind === "oauth-account-403";
591
+ // Keep the bounded refusal readable until the sibling actually returns headers.
592
+ // Other rotation paths retain their existing cancellation boundary.
593
+ if (!retainRefusal) {
594
+ try { void prepared.response.body?.cancel().catch(() => {}); } catch { /* already closed */ }
595
+ }
582
596
  adapter = rotated.adapter;
583
597
  // Stall-watchdog seam between bounded retry fetches (audit 011 B3).
584
598
  yield { type: "heartbeat" };
585
- prepared = await fetchOnce(adapter, rotated.recoveryKind);
599
+ let replacement: IterationResponse;
600
+ try {
601
+ replacement = await fetchOnce(adapter, rotated.recoveryKind);
602
+ } catch (error) {
603
+ if (!retainRefusal || signal.aborted || headerDeadline.didExpire() || isTranslatorBudgetExceededError(error)) throw error;
604
+ break; // Format the original 403 safely; replacement diagnostics are not client errors.
605
+ }
606
+ if (retainRefusal) {
607
+ try { void prepared.response.body?.cancel().catch(() => {}); } catch { /* already closed */ }
608
+ }
609
+ prepared = replacement;
586
610
  }
587
611
 
588
612
  // Final headers have arrived. Clear only the deadline timer before ANY body read.
@@ -623,6 +647,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
623
647
  throw new LoopError(502, `Provider unreachable: ${error instanceof Error ? error.message : String(error)}`);
624
648
  } finally {
625
649
  headerDeadline.clear();
650
+ deps.onIterationEnd?.();
626
651
  }
627
652
  };
628
653
 
@@ -76,6 +76,7 @@ import {
76
76
  import { providerDestinationConfigError } from "../lib/destination-policy";
77
77
  import { redactSecretString } from "../lib/redact";
78
78
  import { rememberBridgeSearchReplay } from "../responses/bridge-search-replay-cache";
79
+ import { withAnthropicHelperRefusal } from "../sidecar/auth";
79
80
 
80
81
  /** Canonical Ollama Cloud origin. The only origin the "ollama" backend derives on its own. */
81
82
  export const OLLAMA_CLOUD_ORIGIN = "https://ollama.com";
@@ -246,12 +247,14 @@ export function resolvePassthroughWebSearchBridgeAuth(
246
247
  backend: ProviderWebSearchBridgeBackend | undefined,
247
248
  config: OcxConfig,
248
249
  openAiSidecar?: ResolvedOpenAiForwardSidecar,
250
+ parentProviderName?: string,
249
251
  ): PassthroughWebSearchBridgeAuth {
250
252
  switch (backend) {
251
253
  case "openai":
252
254
  return openAiSidecar ? { openAiSidecar } : {};
253
255
  case "anthropic": {
254
- const anthropic = findAnthropicSidecarProvider(config);
256
+ // An unavailable selected pool leaves the bridge disarmed; it never fails the passthrough turn.
257
+ const anthropic = withAnthropicHelperRefusal("web-search", () => findAnthropicSidecarProvider(config, parentProviderName));
255
258
  return anthropic ? { anthropic } : {};
256
259
  }
257
260
  case "xai": {
@@ -5,7 +5,7 @@
5
5
  * evaluating when the bridge asks for these names).
6
6
  */
7
7
  import type { OcxConfig, OcxProviderConfig, OcxWebSearchSidecarConfig } from "../types";
8
- import { resolveSidecarAuth } from "../sidecar/auth";
8
+ import { resolveSidecarAuth, resolveAnthropicHelperInstance } from "../sidecar/auth";
9
9
  import { getAccountSet } from "../oauth/store";
10
10
  import type { XaiSearchOptions } from "./xai-executor";
11
11
 
@@ -46,8 +46,9 @@ export interface AnthropicSidecarProvider {
46
46
  * Delegates to the shared sidecar auth module (#2188) so web-search and vision
47
47
  * cannot drift on what "Anthropic auth present" means.
48
48
  */
49
- export function findAnthropicSidecarProvider(config: OcxConfig): AnthropicSidecarProvider | undefined {
50
- const auth = resolveSidecarAuth(config);
49
+ export function findAnthropicSidecarProvider(config: OcxConfig, parentProviderName?: string): AnthropicSidecarProvider | undefined {
50
+ const instance = resolveAnthropicHelperInstance(config, { backendFamily: "anthropic", anthropicInstance: config.webSearchSidecar?.anthropicInstance, parentProviderName });
51
+ const auth = resolveSidecarAuth(config, instance);
51
52
  if (!auth.isAnthropicAuth || !auth.anthropicProviderName || !auth.anthropicProvider) return undefined;
52
53
  return { providerName: auth.anthropicProviderName, provider: auth.anthropicProvider, config };
53
54
  }