@bitkyc08/opencodex 2.55.0 → 2.56.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 (167) hide show
  1. package/gui/dist/assets/{index-VuoiWj9J.js → index-D4zuyIxQ.js} +1 -1
  2. package/gui/dist/index.html +1 -1
  3. package/package.json +2 -1
  4. package/src/adapters/base.ts +21 -0
  5. package/src/adapters/cursor/transport-retry.ts +46 -1
  6. package/src/adapters/cursor.ts +4 -0
  7. package/src/adapters/kiro/adapter.ts +42 -1
  8. package/src/adapters/kiro-retry.ts +23 -4
  9. package/src/adapters/openai-chat/errors.ts +116 -0
  10. package/src/adapters/openai-chat/messages.ts +346 -0
  11. package/src/adapters/openai-chat/passthrough.ts +146 -0
  12. package/src/adapters/openai-chat/response-events.ts +117 -0
  13. package/src/adapters/openai-chat/tool-call-validation.ts +200 -0
  14. package/src/adapters/openai-chat/tool-schema.ts +477 -0
  15. package/src/adapters/openai-chat/wire.ts +50 -0
  16. package/src/adapters/openai-chat.ts +33 -1445
  17. package/src/adapters/openai-responses/canonical-forward.ts +202 -0
  18. package/src/adapters/openai-responses/image-gen.ts +406 -0
  19. package/src/adapters/openai-responses/internal.ts +3 -0
  20. package/src/adapters/openai-responses/passthrough.ts +611 -0
  21. package/src/adapters/openai-responses/prompt-cache.ts +83 -0
  22. package/src/adapters/openai-responses/reasoning.ts +220 -0
  23. package/src/adapters/openai-responses/request-strips.ts +185 -0
  24. package/src/adapters/openai-responses/tool-output-recovery.ts +509 -0
  25. package/src/adapters/openai-responses/tool-schema.ts +293 -0
  26. package/src/adapters/openai-responses/web-search.ts +156 -0
  27. package/src/adapters/openai-responses.ts +4 -2625
  28. package/src/bridge/errors.ts +34 -0
  29. package/src/bridge/internal.ts +174 -0
  30. package/src/bridge/response-json.ts +624 -0
  31. package/src/bridge/sse.ts +1444 -0
  32. package/src/bridge.ts +5 -2204
  33. package/src/chat/inbound.ts +12 -1
  34. package/src/codex/account-lifecycle.ts +3 -0
  35. package/src/codex/account-store.ts +71 -9
  36. package/src/codex/auth-api/account-list.ts +507 -0
  37. package/src/codex/auth-api/http.ts +32 -0
  38. package/src/codex/auth-api/login-flow.ts +554 -0
  39. package/src/codex/auth-api/login-state.ts +64 -0
  40. package/src/codex/auth-api/main-account-probe.ts +331 -0
  41. package/src/codex/auth-api/pool-mode-gate.ts +274 -0
  42. package/src/codex/auth-api/pool-quota-probe.ts +512 -0
  43. package/src/codex/auth-api/reset-credit-service.ts +422 -0
  44. package/src/codex/auth-api/routes.ts +425 -0
  45. package/src/codex/auth-api/runtime-config.ts +48 -0
  46. package/src/codex/auth-api.ts +27 -3118
  47. package/src/codex/auth-context.ts +95 -28
  48. package/src/codex/catalog/auto-review.ts +507 -0
  49. package/src/codex/catalog/build-entries.ts +981 -0
  50. package/src/codex/catalog/combo-member.ts +375 -0
  51. package/src/codex/catalog/derive-entry.ts +229 -0
  52. package/src/codex/catalog/effort.ts +0 -1
  53. package/src/codex/catalog/gated-native-warn.ts +63 -0
  54. package/src/codex/catalog/gather-capture.ts +533 -0
  55. package/src/codex/catalog/model-hints.ts +691 -0
  56. package/src/codex/catalog/model-visibility.ts +304 -0
  57. package/src/codex/catalog/provider-fetch.ts +52 -2942
  58. package/src/codex/catalog/provider-models.ts +685 -0
  59. package/src/codex/catalog/restore.ts +132 -0
  60. package/src/codex/catalog/retained-sync.ts +706 -0
  61. package/src/codex/catalog/routed-gather.ts +858 -0
  62. package/src/codex/catalog/subagent-roster.ts +176 -0
  63. package/src/codex/catalog/sync.ts +52 -2698
  64. package/src/codex/inject/config-toml.ts +563 -0
  65. package/src/codex/inject/remove.ts +192 -0
  66. package/src/codex/inject/restore.ts +540 -0
  67. package/src/codex/inject/routing-classify.ts +109 -0
  68. package/src/codex/inject/routing-target.ts +125 -0
  69. package/src/codex/inject.ts +81 -1436
  70. package/src/codex/lineage.ts +458 -0
  71. package/src/codex/pool-refresh-backoff.ts +152 -0
  72. package/src/codex/routing/active-account.ts +194 -0
  73. package/src/codex/routing/cooldown-math.ts +275 -0
  74. package/src/codex/routing/health-store.ts +402 -0
  75. package/src/codex/routing/probe-lease.ts +358 -0
  76. package/src/codex/routing/selection.ts +703 -0
  77. package/src/codex/routing/thread-affinity.ts +538 -0
  78. package/src/codex/routing.ts +353 -2234
  79. package/src/codex/shim-fingerprint.ts +223 -0
  80. package/src/codex/shim-inspect.ts +175 -0
  81. package/src/codex/shim-probe.ts +367 -0
  82. package/src/codex/shim-restore-lock.ts +169 -0
  83. package/src/codex/shim-state-file.ts +151 -0
  84. package/src/codex/shim-templates.ts +265 -0
  85. package/src/codex/shim.ts +48 -1268
  86. package/src/config/diagnostics.ts +705 -0
  87. package/src/config/feature-flags.ts +55 -0
  88. package/src/config/live-reconcile.ts +403 -0
  89. package/src/config/load-degrade.ts +880 -0
  90. package/src/config/mutation-lock.ts +244 -0
  91. package/src/config/openai-tier-backup.ts +268 -0
  92. package/src/config/persist-unlocked.ts +92 -0
  93. package/src/config/proxy-env.ts +188 -0
  94. package/src/config/salvage.ts +244 -0
  95. package/src/config/schema/config-schema.ts +640 -0
  96. package/src/config/schema/leaf-validators.ts +855 -0
  97. package/src/config/warn-memo.ts +28 -0
  98. package/src/config.ts +234 -4481
  99. package/src/generated/compatibility-version.json +539 -39
  100. package/src/lib/request-execution-budget.ts +69 -20
  101. package/src/lib/spend-reservation-ledger.ts +940 -0
  102. package/src/lib/upstream-retry.ts +55 -11
  103. package/src/lib/workflow-budget.ts +553 -30
  104. package/src/providers/quota/account-cache.ts +441 -0
  105. package/src/providers/quota/antigravity.ts +295 -0
  106. package/src/providers/quota/report-cache.ts +320 -0
  107. package/src/providers/quota/vendor-probes-key.ts +1243 -0
  108. package/src/providers/quota/vendor-probes-oauth.ts +590 -0
  109. package/src/providers/quota.ts +324 -3079
  110. package/src/providers/registry/entries-core.ts +1221 -0
  111. package/src/providers/registry/entries-extended.ts +1204 -0
  112. package/src/providers/registry/model-seeds.ts +908 -0
  113. package/src/providers/registry/types.ts +352 -0
  114. package/src/providers/registry.ts +24 -3536
  115. package/src/responses/continuation-ownership.ts +29 -0
  116. package/src/responses/state/replay-fingerprint.ts +80 -0
  117. package/src/responses/state/snapshot-codec.ts +104 -0
  118. package/src/responses/state/spill-failure.ts +118 -0
  119. package/src/responses/state/spill-queue.ts +665 -0
  120. package/src/responses/state/temp-recovery.ts +257 -0
  121. package/src/responses/state.ts +82 -1143
  122. package/src/routing/identity-domains.ts +449 -0
  123. package/src/routing/probe-lease.ts +511 -0
  124. package/src/server/index/bounded-request.ts +88 -0
  125. package/src/server/index/live-sideband.ts +565 -0
  126. package/src/server/index/serve-options.ts +1766 -0
  127. package/src/server/index/startup-warnings.ts +213 -0
  128. package/src/server/index/websocket-handler.ts +335 -0
  129. package/src/server/index.ts +40 -2547
  130. package/src/server/management/route-registry.ts +26 -23
  131. package/src/server/management/shared.ts +8 -5
  132. package/src/server/management/workflow-budget-routes.ts +133 -0
  133. package/src/server/management-api.ts +12 -0
  134. package/src/server/request-log-conversation.ts +9 -7
  135. package/src/server/request-log.ts +245 -1
  136. package/src/server/responses/account-change-state.ts +233 -0
  137. package/src/server/responses/adapter-continuation.ts +514 -0
  138. package/src/server/responses/adapter-delivery.ts +214 -0
  139. package/src/server/responses/adapter-dispatch.ts +971 -0
  140. package/src/server/responses/compact.ts +59 -4
  141. package/src/server/responses/completion-policy.ts +33 -0
  142. package/src/server/responses/core-auth.ts +527 -0
  143. package/src/server/responses/core-codex-account.ts +859 -0
  144. package/src/server/responses/core-combo-failure.ts +210 -0
  145. package/src/server/responses/core-combo.ts +707 -0
  146. package/src/server/responses/core-errors.ts +152 -0
  147. package/src/server/responses/core-lifetime.ts +95 -0
  148. package/src/server/responses/core-normalize.ts +350 -0
  149. package/src/server/responses/core-opaque-recovery.ts +380 -0
  150. package/src/server/responses/core-options.ts +159 -0
  151. package/src/server/responses/core-replay.ts +225 -0
  152. package/src/server/responses/core.ts +192 -8893
  153. package/src/server/responses/passthrough-delivery.ts +856 -0
  154. package/src/server/responses/passthrough-dispatch.ts +1476 -0
  155. package/src/server/responses/passthrough-execution.ts +54 -0
  156. package/src/server/responses/request-prepare.ts +970 -0
  157. package/src/server/responses/request-send-budget.ts +164 -0
  158. package/src/server/responses/request-sidecar-auth.ts +149 -0
  159. package/src/server/responses/request-transport.ts +744 -0
  160. package/src/server/responses/response-effects.ts +157 -0
  161. package/src/server/responses/run-turn-execution.ts +448 -0
  162. package/src/server/responses/sidecar-execution.ts +469 -0
  163. package/src/server/responses-image-gen-repair.ts +1 -1
  164. package/src/server/workflow-refusal.ts +84 -0
  165. package/src/types/config.ts +30 -0
  166. package/src/usage/log.ts +146 -0
  167. package/src/usage/summary.ts +171 -21
@@ -0,0 +1,744 @@
1
+ import type { ResponsesRequestContext, ResponsesAdmissionState } from "./core-options";
2
+ import type { PreparedResponsesRequest } from "./request-prepare";
3
+ import type { OAuthAccessSnapshot } from "../../oauth";
4
+ import {
5
+ captureOAuthAccountSelection,
6
+ commitOAuthAccountSelection,
7
+ getAccountCredentialWithStatus,
8
+ credentialGeneration,
9
+ } from "../../oauth/store";
10
+ import type { ProviderAdapter, AdapterRequest } from "../../adapters/base";
11
+ import type { OcxParsedRequest, OcxProviderConfig } from "../../types";
12
+ import type { AnthropicAccountSelectionReason } from "../../oauth/anthropic-routing";
13
+ import {
14
+ isAnthropicAccountPoolEnabled,
15
+ getAnthropicPoolAccessSnapshot,
16
+ commitAnthropicSelectionRouting,
17
+ formatAnthropicProviderForLog,
18
+ anthropicSessionKeyFromParts,
19
+ resolveAnthropicAccountForSession,
20
+ getAnthropicPoolRetryAfterSeconds,
21
+ hasAnthropicFailoverQuorum,
22
+ } from "../../oauth/anthropic-routing";
23
+ import {
24
+ getValidAccessSnapshotForAccount,
25
+ forceRefreshOAuthAccessSnapshot,
26
+ getValidAccessTokenSnapshot,
27
+ publicOAuthAuthenticationErrorMessage,
28
+ UnsupportedOAuthProviderError,
29
+ } from "../../oauth";
30
+ import {
31
+ forgetGenericFailoverRoster,
32
+ isGenericFailoverProvider,
33
+ preferredInitialAccount,
34
+ noteGenericPoolSelection,
35
+ } from "../../oauth/generic-account-failover";
36
+ import { stampOAuthAccountLabel } from "../../providers/label";
37
+ import { resolveProviderTransport } from "../../providers/xai-transport";
38
+ import { resolveCopilotApiBaseUrl } from "../../oauth/github-copilot";
39
+ import {
40
+ providerApiKeySelectionIsCurrent,
41
+ resolveCurrentProviderApiKeyTransport,
42
+ } from "../../providers/api-key-selection";
43
+ import { resolveAdapter, resolveWireProtocolOverride } from "../adapter-resolve";
44
+ import { providerFetch } from "./fetch-helpers";
45
+ import type { ProviderFetchOptions } from "./fetch-helpers";
46
+ import { captureConfigGeneration } from "../../lib/state-store-sweeper";
47
+ import { recordAnthropicAccountQuotaFromHeaders, hasPassiveAccountQuota } from "../../providers/quota";
48
+ import { checkOutboundBodySize, describeOutboundBodyRefusal } from "./outbound-body-guard";
49
+ import { formatErrorResponse } from "../../bridge";
50
+ import { bindRouteReasoningReplayScope } from "./core-replay";
51
+ import { sessionIdHeaderFromRequest, normalizeLogConversationId } from "../request-log-conversation";
52
+ import { redactSecretString } from "../../lib/redact";
53
+ import { selectProactiveApiKeyTransport } from "../../providers/key-failover";
54
+ import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
55
+ import { providerConsumesCallerAuthorization } from "../../providers/caller-authorization";
56
+ import { releaseCodexAuthContextProbeLease, stripCodexRuntimeProviderFields } from "../../codex/auth-context";
57
+ import {
58
+ beginRequestAttempt,
59
+ sealRequestAttemptIdentity,
60
+ recordAttemptCredentialSource,
61
+ recordAdapterTierMetadata,
62
+ } from "../request-log";
63
+ import { resolvePassiveRouteSubjectId } from "../passive-route-linker";
64
+
65
+ /** Owns live credential selection and adapter bindings for one request. */
66
+ export async function prepareResponsesTransport(
67
+ requestContext: Pick<ResponsesRequestContext, "config" | "logCtx" | "options" | "req">,
68
+ admissionState: ResponsesAdmissionState,
69
+ requestState: Pick<
70
+ PreparedResponsesRequest,
71
+ | "route"
72
+ | "parsed"
73
+ | "inboundWire"
74
+ | "selectedForwardHeaders"
75
+ | "translatorBudget"
76
+ >,
77
+ ) {
78
+ const { config, logCtx, options, req } = requestContext;
79
+ const { route, parsed, inboundWire, translatorBudget } = requestState;
80
+
81
+
82
+ // OAuth providers: swap in a fresh access token (auto-refreshed) as the Bearer key, so the
83
+ // existing openai-chat / anthropic adapters authenticate with no change.
84
+ const isOAuth401ReplayProvider = (
85
+ route.providerName === "xai"
86
+ || route.providerName === "github-copilot"
87
+ || route.providerName === "kiro"
88
+ || route.providerName === "google-antigravity"
89
+ || route.providerName === "orcarouter-oauth"
90
+ ) && route.provider.authMode === "oauth";
91
+ let sentOAuthSnapshot: OAuthAccessSnapshot | undefined;
92
+ let replayOAuthCredentialSnapshot: Pick<OAuthAccessSnapshot, "accountId" | "generation"> | undefined;
93
+ let anthropicPoolAccountId: string | null = null;
94
+ let anthropicPoolFailovers = 0;
95
+ // Generic OAuth rotation (#2568) for providers with no pool of their own. Bound to the account
96
+ // the request actually used, so a concurrent rotation cannot cool an innocent replacement.
97
+ let genericFailoverAccountId: string | null = null;
98
+ let genericFailovers = 0;
99
+ let oauthSelection = route.provider.authMode === "oauth"
100
+ ? captureOAuthAccountSelection(route.providerName) : null;
101
+ let servingOAuthSnapshot: OAuthAccessSnapshot | undefined;
102
+ // These owners also serve early passthrough and sidecar sends. A dispatch-time
103
+ // rebuild must update every later builder, without entering a later block's TDZ.
104
+ let adapter: ProviderAdapter;
105
+ let activeAdapter: ProviderAdapter;
106
+ let runTurnAdapter: ProviderAdapter;
107
+ let sameTargetRequest: AdapterRequest | undefined;
108
+ let sameTargetParsed: OcxParsedRequest | undefined;
109
+ let sameTargetToken = 0;
110
+ let transportToken = 0;
111
+ let imageTierBias = 0;
112
+ const invalidateSameTargetRequest = (): void => { transportToken += 1; };
113
+ type DispatchBinding =
114
+ | { kind: "oauth"; selection: NonNullable<typeof oauthSelection>; snapshot: OAuthAccessSnapshot }
115
+ | { kind: "api-key"; provider: OcxProviderConfig };
116
+ const requestBindings = new WeakMap<AdapterRequest, DispatchBinding>();
117
+ const adapterBindings = new WeakMap<ProviderAdapter, DispatchBinding>();
118
+ const rawRunTurns = new WeakMap<ProviderAdapter, NonNullable<ProviderAdapter["runTurn"]>>();
119
+ const commitResolvedOAuthSelection = async (
120
+ candidate: OAuthAccessSnapshot,
121
+ proactive = false,
122
+ anthropicReason?: AnthropicAccountSelectionReason,
123
+ ): Promise<OAuthAccessSnapshot | null> => {
124
+ const maxSelectionAttempts = 3;
125
+ for (let attempt = 0; attempt < maxSelectionAttempts; attempt++) {
126
+ if (!oauthSelection) return null;
127
+ const proactiveEnabled = route.providerName === "anthropic"
128
+ ? isAnthropicAccountPoolEnabled(config)
129
+ : (config.providers[route.providerName]?.oauthAccountFailover?.enabled
130
+ ?? config.oauthAccountFailover?.enabled) === true;
131
+ if (proactive && candidate.accountId !== oauthSelection.accountId && !proactiveEnabled) {
132
+ oauthSelection = captureOAuthAccountSelection(route.providerName);
133
+ if (!oauthSelection) return null;
134
+ candidate = route.providerName === "anthropic"
135
+ ? await getAnthropicPoolAccessSnapshot(oauthSelection.accountId)
136
+ : await getValidAccessSnapshotForAccount(route.providerName, oauthSelection.accountId, { requireUsableAccount: true });
137
+ }
138
+ const committed = await commitOAuthAccountSelection(route.providerName, candidate.accountId, {
139
+ expectedSelection: oauthSelection,
140
+ expectedCredentialGeneration: candidate.generation,
141
+ requireUsableAccount: true,
142
+ });
143
+ if (committed) {
144
+ if (route.providerName === "anthropic" && !commitAnthropicSelectionRouting(
145
+ candidate.accountId, oauthSelection, committed,
146
+ { config, sessionKey: anthropicSessionKey, reason: anthropicReason, expectedCredentialGeneration: candidate.generation },
147
+ )) return null;
148
+ oauthSelection = committed;
149
+ servingOAuthSnapshot = candidate;
150
+ forgetGenericFailoverRoster(route.providerName);
151
+ return candidate;
152
+ }
153
+ // A newer manual choice wins over this request's old proposal, including A→B→A.
154
+ // Resolve that choice, not the rejected candidate, before trying admission again.
155
+ oauthSelection = captureOAuthAccountSelection(route.providerName);
156
+ if (!oauthSelection) return null;
157
+ candidate = route.providerName === "anthropic"
158
+ ? await getAnthropicPoolAccessSnapshot(oauthSelection.accountId)
159
+ : await getValidAccessSnapshotForAccount(route.providerName, oauthSelection.accountId, { requireUsableAccount: true });
160
+ if (route.provider.googleMode === "cloud-code-assist" && !candidate.projectId) return null;
161
+ }
162
+ return null;
163
+ };
164
+ const refreshResolvedOAuthSelection = async (sent: OAuthAccessSnapshot): Promise<OAuthAccessSnapshot> => {
165
+ const current = captureOAuthAccountSelection(route.providerName);
166
+ const unchanged = current?.accountId === oauthSelection?.accountId
167
+ && current?.revision === oauthSelection?.revision;
168
+ const candidate = unchanged ? await forceRefreshOAuthAccessSnapshot(sent) : sent;
169
+ const admitted = await commitResolvedOAuthSelection(candidate);
170
+ if (!admitted) throw new Error("OAuth selection changed during credential recovery");
171
+ genericFailoverAccountId = admitted.accountId;
172
+ stampOAuthAccountLabel(logCtx, route.providerName, route.provider, admitted.accountId);
173
+ return admitted;
174
+ };
175
+ /**
176
+ * Config generation captured where the serving credential is RESOLVED, not where the
177
+ * quota is written. A streaming turn is a long await, so a generation captured at write
178
+ * time cannot see a config or account change that happened earlier in the same turn —
179
+ * the case the fence exists for. Stays 0 for every provider without a passive quota.
180
+ */
181
+ let passiveQuotaWriterGeneration = 0;
182
+ /**
183
+ * Apply a rotated account's FULL credential snapshot to the live route (#2568d).
184
+ *
185
+ * One helper for all three rotation sites on purpose. Each site used to inline the same four
186
+ * lines, and the divergence that produced was the bug: `apiKey` was swapped while the routing
187
+ * metadata paired with it stayed behind.
188
+ *
189
+ * Returns false when the snapshot cannot be used safely, and the caller must then abandon the
190
+ * rotation rather than send a half-applied identity:
191
+ *
192
+ * - Copilot pins its bearer to an account-scoped regional origin, so transport is re-resolved
193
+ * with the new account's `apiBaseUrl` instead of inheriting the previous account's host. The
194
+ * snapshot value is RESOLVED first: `rotatedProvider` is a clone of the FAILED account's
195
+ * provider, so passing a bare `undefined` origin let the transport resolver fall through its
196
+ * own `?? validateCopilotApiBaseUrl(provider.baseUrl)` step to the previous account's host —
197
+ * pairing B's bearer with A's accepted origin. Login and refresh always persist a resolved
198
+ * origin, so this fallback protects malformed or manually seeded credentials.
199
+ * - A Cloud Code Assist provider needs an account-matched project. Antigravity's refresh path
200
+ * tolerates project discovery failing, so a stored account can legitimately have no project;
201
+ * sending that account's bearer with the FAILED account's project is worse than not rotating.
202
+ */
203
+ const applyFailoverSnapshot = async (
204
+ snapshot: OAuthAccessSnapshot,
205
+ retryParsed: OcxParsedRequest = parsed,
206
+ ): Promise<boolean> => {
207
+ if (route.provider.googleMode === "cloud-code-assist" && !snapshot.projectId) return false;
208
+ const committed = await commitResolvedOAuthSelection(snapshot);
209
+ if (!committed) return false;
210
+ snapshot = committed;
211
+ let rotatedProvider: OcxProviderConfig = { ...route.provider, apiKey: snapshot.accessToken };
212
+ if (route.providerName === "github-copilot") {
213
+ rotatedProvider = resolveProviderTransport(
214
+ route.providerName,
215
+ rotatedProvider,
216
+ parsed.options.promptCacheKey,
217
+ resolveCopilotApiBaseUrl(snapshot.apiBaseUrl),
218
+ ) as OcxProviderConfig;
219
+ }
220
+ if (snapshot.projectId) rotatedProvider = { ...rotatedProvider, project: snapshot.projectId };
221
+ route.provider = rotatedProvider;
222
+ if (route.providerName === "kiro") {
223
+ const kiroContext = { ...(snapshot.kiro ?? {}) };
224
+ // Terminal-guard continuations are rebuilt from a shallow clone. Updating only the
225
+ // outer request pairs the new bearer with the failed account's region/profile on
226
+ // the retry. Keep both owners synchronized; for ordinary paths they are identical.
227
+ parsed._kiroAuthContext = kiroContext;
228
+ if (retryParsed !== parsed) retryParsed._kiroAuthContext = { ...kiroContext };
229
+ }
230
+ // Re-stamp: a request that rotated accounts must be attributed to the account that actually
231
+ // served it. All three rotation sites funnel through here, so this is the only re-stamp
232
+ // needed -- and putting it anywhere else would let one of the three drift.
233
+ stampOAuthAccountLabel(logCtx, route.providerName, route.provider, snapshot.accountId);
234
+ if (route.providerName === "anthropic") {
235
+ anthropicPoolAccountId = snapshot.accountId;
236
+ logCtx.provider = formatAnthropicProviderForLog("anthropic", snapshot.accountId, config);
237
+ } else {
238
+ genericFailoverAccountId = snapshot.accountId;
239
+ }
240
+ sentOAuthSnapshot = snapshot;
241
+ replayOAuthCredentialSnapshot = { accountId: snapshot.accountId, generation: snapshot.generation };
242
+ return true;
243
+ };
244
+ const selectionIsCurrent = (binding: DispatchBinding | undefined): boolean => {
245
+ if (route.provider.authMode === "forward") return true;
246
+ if (!binding) return false;
247
+ if (binding.kind === "api-key") return providerApiKeySelectionIsCurrent(config, route.providerName, binding.provider);
248
+ const selected = captureOAuthAccountSelection(route.providerName);
249
+ const row = getAccountCredentialWithStatus(route.providerName, binding.snapshot.accountId);
250
+ return selected?.accountId === binding.selection.accountId && selected?.revision === binding.selection.revision
251
+ && !!row && !row.needsReauth && row.credential.expires > Date.now()
252
+ && credentialGeneration(row.credential) === binding.snapshot.generation;
253
+ };
254
+ const resolveSelectionAdapter = (provider: OcxProviderConfig, retention = config.cacheRetention): ProviderAdapter => {
255
+ const resolved = resolveAdapter(provider, retention, route.providerName);
256
+ if (route.provider.authMode === "forward") return resolved;
257
+ const binding: DispatchBinding | undefined = route.provider.authMode === "oauth"
258
+ ? oauthSelection && servingOAuthSnapshot
259
+ ? { kind: "oauth", selection: { ...oauthSelection }, snapshot: servingOAuthSnapshot }
260
+ : undefined
261
+ : { kind: "api-key", provider: { ...route.provider } };
262
+ if (binding) adapterBindings.set(resolved, binding);
263
+ const build = resolved.buildRequest.bind(resolved);
264
+ resolved.buildRequest = async (requestParsed, incoming) => {
265
+ const request = await build(requestParsed, incoming);
266
+ // Capture at adapter creation, never from mutable serving state after an await.
267
+ if (binding) requestBindings.set(request, binding);
268
+ return request;
269
+ };
270
+ if (resolved.runTurn) {
271
+ rawRunTurns.set(resolved, resolved.runTurn.bind(resolved));
272
+ resolved.runTurn = (requestParsed, incoming, emit) => runSelectedTurn(resolved, requestParsed, incoming, emit);
273
+ }
274
+ return resolved;
275
+ };
276
+ const refreshDispatchAdapter = async (requestParsed: OcxParsedRequest): Promise<ProviderAdapter> => {
277
+ if (route.provider.authMode === "oauth") {
278
+ if (!servingOAuthSnapshot || !await applyFailoverSnapshot(servingOAuthSnapshot, requestParsed)) {
279
+ throw new Error("OAuth account selection changed before dispatch");
280
+ }
281
+ } else {
282
+ const current = resolveCurrentProviderApiKeyTransport(config, route.providerName, route.provider);
283
+ if (!current) throw new Error("API key selection is unavailable before dispatch");
284
+ route.provider = current;
285
+ }
286
+ adapter = activeAdapter = runTurnAdapter = resolveSelectionAdapter(
287
+ resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire),
288
+ );
289
+ invalidateSameTargetRequest();
290
+ return adapter;
291
+ };
292
+ const refreshRunTurnAdapter = async (requestParsed: OcxParsedRequest): Promise<ProviderAdapter> => {
293
+ requestParsed._cursorIdentityScope = undefined;
294
+ requestParsed._cursorConversationId = undefined;
295
+ if (requestParsed._providerContinuation?.cursor) {
296
+ const { cursor: _oldCursor, ...rest } = requestParsed._providerContinuation;
297
+ requestParsed._providerContinuation = rest;
298
+ }
299
+ return refreshDispatchAdapter(requestParsed);
300
+ };
301
+ const runSelectedTurn = async (
302
+ selectedAdapter: ProviderAdapter,
303
+ ...[requestParsed, incoming, emit]: Parameters<NonNullable<ProviderAdapter["runTurn"]>>
304
+ ): Promise<void> => {
305
+ for (let attempt = 0; attempt < 3; attempt++) {
306
+ if (!selectionIsCurrent(adapterBindings.get(selectedAdapter))) selectedAdapter = await refreshRunTurnAdapter(requestParsed);
307
+ const binding = adapterBindings.get(selectedAdapter);
308
+ const run = rawRunTurns.get(selectedAdapter);
309
+ if (!run) throw new Error("Selected provider no longer supports this turn transport");
310
+ let sent = false;
311
+ let refused = false;
312
+ // Both main and image-loop callers already acquired the initial pacing slot.
313
+ // Subsequent physical messages retain this adapter/credential and are paced normally.
314
+ const fetch = providerFetch(route.provider, options.codexWsRuntimeIdentity, {
315
+ providerName: route.providerName, modelId: route.modelId, pacingSlotAcquired: true,
316
+ beforeDispatch: () => {
317
+ if (sent) return;
318
+ if (!selectionIsCurrent(binding)) {
319
+ refused = true;
320
+ throw new Error("Account selection changed before the first turn dispatch");
321
+ }
322
+ sent = true;
323
+ },
324
+ });
325
+ try {
326
+ await run(requestParsed, { ...incoming, providerFetch: fetch }, event => { if (!refused) emit(event); });
327
+ } catch (error) {
328
+ if (!refused) throw error;
329
+ }
330
+ if (!refused) return;
331
+ // The adapter may map the guard's exception to an error event. Neither that
332
+ // event nor a refused send may escape before retrying the newly selected account.
333
+ selectedAdapter = await refreshRunTurnAdapter(requestParsed);
334
+ }
335
+ throw new Error("Account selection changed repeatedly before turn dispatch");
336
+ };
337
+ const oauthDispatch = (wireRequest: AdapterRequest, requestParsed = parsed): ProviderFetchOptions["dispatchOverride"] => {
338
+ if (route.provider.authMode === "forward") return undefined;
339
+ return async (input, init, execute) => {
340
+ let destination = input;
341
+ let dispatchInit = init;
342
+ for (let attempt = 0; attempt < 3; attempt++) {
343
+ if (selectionIsCurrent(requestBindings.get(wireRequest))) {
344
+ const fetchImpl = (route.provider as OcxProviderConfig & { fetch?: typeof globalThis.fetch }).fetch ?? execute;
345
+ const binding = requestBindings.get(wireRequest);
346
+ const snapshot = route.providerName === "anthropic" && anthropicPoolAccountId && binding?.kind === "oauth"
347
+ ? binding.snapshot : undefined;
348
+ const writerGeneration = snapshot ? captureConfigGeneration() : 0;
349
+ const sentHeaders = snapshot ? new Headers(dispatchInit.headers) : undefined;
350
+ const ownsBearer = snapshot !== undefined
351
+ && sentHeaders?.get("authorization") === `Bearer ${snapshot.accessToken}`
352
+ && !sentHeaders?.has("x-api-key");
353
+ // Reselection can choose a provider override instead of the supplied executor.
354
+ const response = await fetchImpl(destination, { ...dispatchInit, redirect: "manual" });
355
+ // Observe each physical response before retries replace it. The binding belongs to
356
+ // this dispatch, so a manual switch cannot file A's headers against B. Header
357
+ // overrides and credential replacement make ownership unprovable: skip those writes.
358
+ if (ownsBearer && snapshot) {
359
+ try {
360
+ const current = getAccountCredentialWithStatus("anthropic", snapshot.accountId);
361
+ if (current && !current.needsReauth && credentialGeneration(current.credential) === snapshot.generation) {
362
+ recordAnthropicAccountQuotaFromHeaders(snapshot.accountId, response.headers, writerGeneration);
363
+ }
364
+ } catch { /* best-effort observation cannot fail the response */ }
365
+ }
366
+ return response;
367
+ }
368
+ const nextAdapter = await refreshDispatchAdapter(requestParsed);
369
+ const rebuilt = await nextAdapter.buildRequest(requestParsed, {
370
+ headers: requestState.selectedForwardHeaders, translatorBudget,
371
+ ...(imageTierBias > 0 ? { imageTierBias } : {}),
372
+ });
373
+ const bodySize = checkOutboundBodySize(rebuilt.body, config.maxUpstreamBodyBytes);
374
+ if (!bodySize.admitted) {
375
+ rebuilt.releaseBodyObservation?.();
376
+ return formatErrorResponse(413, "outbound_body_too_large", describeOutboundBodyRefusal(bodySize));
377
+ }
378
+ const headers = new Headers(dispatchInit.headers);
379
+ for (const name of Object.keys(wireRequest.headers)) headers.delete(name);
380
+ for (const [name, value] of Object.entries(rebuilt.headers)) headers.set(name, value);
381
+ wireRequest.releaseBodyObservation?.();
382
+ Object.assign(wireRequest, rebuilt);
383
+ const binding = requestBindings.get(rebuilt);
384
+ if (binding) requestBindings.set(wireRequest, binding);
385
+ else requestBindings.delete(wireRequest);
386
+ sameTargetRequest = wireRequest;
387
+ sameTargetParsed = requestParsed;
388
+ sameTargetToken = transportToken;
389
+ destination = rebuilt.url;
390
+ dispatchInit = { ...dispatchInit, method: rebuilt.method, headers, body: rebuilt.body };
391
+ bindRouteReasoningReplayScope({ parsed: requestParsed, providerName: route.providerName, provider: route.provider,
392
+ adapterName: nextAdapter.name, oauthCredentialSnapshot: replayOAuthCredentialSnapshot });
393
+ // The next iteration validates synchronously and calls fetch in that same turn.
394
+ }
395
+ throw new Error("OAuth account selection changed repeatedly before dispatch");
396
+ };
397
+ };
398
+ const anthropicSessionKey = route.providerName === "anthropic" && route.provider.authMode === "oauth"
399
+ ? anthropicSessionKeyFromParts({
400
+ sessionIdHeader: sessionIdHeaderFromRequest(req.headers),
401
+ threadIdHeader: req.headers.get("thread-id"),
402
+ promptCacheKey: typeof parsed.options.promptCacheKey === "string" ? parsed.options.promptCacheKey : null,
403
+ clientThreadId: typeof parsed._clientThreadId === "string" ? parsed._clientThreadId : null,
404
+ promptCacheKeyIsSharedCohort: options.promptCacheKeyIsSharedCohort === true,
405
+ })
406
+ : null;
407
+ if (route.provider.authMode === "oauth") {
408
+ try {
409
+ if (route.providerName === "anthropic" && isAnthropicAccountPoolEnabled(config)) {
410
+ const selection = resolveAnthropicAccountForSession(anthropicSessionKey, config);
411
+ if (!selection.accountId) {
412
+ if (selection.reason === "all-cooled") {
413
+ const retryAfterSec = getAnthropicPoolRetryAfterSeconds();
414
+ return formatErrorResponse(
415
+ 429,
416
+ "rate_limit_error",
417
+ "All Anthropic OAuth accounts are temporarily rate-limited",
418
+ retryAfterSec !== null ? { retryAfter: String(retryAfterSec) } : undefined,
419
+ );
420
+ }
421
+ return formatErrorResponse(401, "authentication_error", "No eligible Anthropic OAuth account available");
422
+ }
423
+ const admitted = await commitResolvedOAuthSelection(await getAnthropicPoolAccessSnapshot(selection.accountId), true, selection.reason);
424
+ if (!admitted) return formatErrorResponse(409, "conflict_error", "OAuth account selection changed; retry the request");
425
+ anthropicPoolAccountId = admitted.accountId;
426
+ route.provider = { ...route.provider, apiKey: admitted.accessToken };
427
+ logCtx.provider = formatAnthropicProviderForLog("anthropic", admitted.accountId, config);
428
+ } else {
429
+ // Prefer the account with known headroom BEFORE the first attempt. Rotation alone
430
+ // only reacts to a 429, so a turn could open on an account a previous probe already
431
+ // measured as spent. A null answer means "use the active account", so every provider
432
+ // without quota evidence keeps the resolution it has today.
433
+ const preferredAccountId = isGenericFailoverProvider(route.providerName, route.provider)
434
+ ? preferredInitialAccount(config, route.providerName)
435
+ : null;
436
+ // Resolved account-scoped, NOT through failoverAccountSnapshot: that helper marks a
437
+ // rotation site, and rotation sites must apply their credential through
438
+ // applyFailoverSnapshot's pairing rules. This is initial resolution — the code below
439
+ // already pairs the snapshot's Kiro metadata, Copilot origin and Antigravity project
440
+ // with this same bearer, exactly as it does for the active account.
441
+ let usedPreferredAccount = preferredAccountId !== null;
442
+ let resolved: OAuthAccessSnapshot;
443
+ if (preferredAccountId) {
444
+ try {
445
+ // `requireUsableAccount` makes a removed OR reauth-flagged account throw from
446
+ // inside the resolver's own store read. Without it a revoked account resolves
447
+ // successfully — its credential is still readable — and the request would
448
+ // dispatch on an account already known to need a fresh login.
449
+ resolved = await getValidAccessSnapshotForAccount(
450
+ route.providerName,
451
+ preferredAccountId,
452
+ { requireUsableAccount: true },
453
+ );
454
+ } catch {
455
+ // The roster is read behind a short TTL, so a preferred account can be removed
456
+ // or flagged for reauth in the window after it was cached. Resolving it then
457
+ // throws, and a PREFERENCE that turns a healthy request into a 401 is worse
458
+ // than no preference at all — the active account is still perfectly usable.
459
+ // Drop the stale roster so the next request re-reads it, and carry on.
460
+ forgetGenericFailoverRoster(route.providerName);
461
+ usedPreferredAccount = false;
462
+ resolved = await getValidAccessTokenSnapshot(route.providerName);
463
+ }
464
+ } else {
465
+ resolved = await getValidAccessTokenSnapshot(route.providerName);
466
+ }
467
+ // A Cloud Code Assist account needs its own project. Antigravity's refresh path
468
+ // tolerates project discovery failing, so a stored account can legitimately have
469
+ // none — and a PREFERENCE must never turn a working request into an error. Fall
470
+ // back to the ordinary active-account resolution instead, which is exactly what
471
+ // would have happened had the preference never existed.
472
+ if (usedPreferredAccount && route.provider.googleMode === "cloud-code-assist" && !resolved.projectId) {
473
+ resolved = await getValidAccessTokenSnapshot(route.providerName);
474
+ usedPreferredAccount = false;
475
+ }
476
+ const admitted = await commitResolvedOAuthSelection(resolved, true);
477
+ if (!admitted) return formatErrorResponse(409, "conflict_error", "OAuth account selection changed; retry the request");
478
+ if (admitted.accountId !== resolved.accountId) usedPreferredAccount = true;
479
+ resolved = admitted;
480
+ replayOAuthCredentialSnapshot = {
481
+ accountId: resolved.accountId,
482
+ generation: resolved.generation,
483
+ };
484
+ if (isOAuth401ReplayProvider) sentOAuthSnapshot = resolved;
485
+ route.provider = { ...route.provider, apiKey: resolved.accessToken };
486
+ // Attribution is independent of failover (#2699): stamped from the resolved snapshot
487
+ // itself, not from inside the `isGenericFailoverProvider` branch below, so a future
488
+ // narrowing of that predicate cannot silently switch attribution off.
489
+ stampOAuthAccountLabel(logCtx, route.providerName, route.provider, resolved.accountId);
490
+ // Remember which account actually served this request so a 429 cools THAT one, not
491
+ // whichever account is active by the time the response comes back (#2568).
492
+ if (isGenericFailoverProvider(route.providerName, route.provider)) {
493
+ genericFailoverAccountId = resolved.accountId;
494
+ // Advance the pool cursor only now that this account is actually admitted. The
495
+ // helper returns immediately unless the kernel is on AND the strategy is
496
+ // round-robin, so quota and fill-first pools reach it without being touched.
497
+ noteGenericPoolSelection(config, route.providerName, resolved.accountId);
498
+ }
499
+ // Anthropic is excluded from isGenericFailoverProvider -- its own pool owns affinity and
500
+ // a fail-closed local-cli credential rule -- so without this stamp its identity is
501
+ // dropped whenever the pool flag is off, and a later 429 has no account to cool. Reactive
502
+ // failover needs only the id: no affinity bind, no promotion, no quota-ranked pick. Those
503
+ // are proactive and stay behind anthropicAccountPool.enabled.
504
+ if (route.providerName === "anthropic" && hasAnthropicFailoverQuorum()) {
505
+ anthropicPoolAccountId = resolved.accountId;
506
+ }
507
+ // Captured beside the account it fences, so the two can never disagree.
508
+ if (hasPassiveAccountQuota(route.providerName)) {
509
+ passiveQuotaWriterGeneration = captureConfigGeneration();
510
+ }
511
+ if (route.providerName === "kiro") {
512
+ // `{}` is intentional: this is an account-scoped request with no stored routing metadata.
513
+ // Only genuinely accountless adapter calls leave the context undefined and use local/env fallback.
514
+ parsed._kiroAuthContext = { ...(resolved.kiro ?? {}) };
515
+ }
516
+ // Project identity belongs to the admitted account on EVERY request, including
517
+ // the request after a pool transition made that account the persisted active one.
518
+ if (route.provider.googleMode === "cloud-code-assist") {
519
+ if (!resolved.projectId) return formatErrorResponse(401, "authentication_error", publicOAuthAuthenticationErrorMessage(new Error("Cloud Code Assist account project is unavailable")));
520
+ route.provider = { ...route.provider, project: resolved.projectId };
521
+ }
522
+ }
523
+ } catch (err) {
524
+ if (err instanceof UnsupportedOAuthProviderError) {
525
+ const safeProviderName = redactSecretString(route.providerName);
526
+ return formatErrorResponse(
527
+ 400,
528
+ "invalid_request_error",
529
+ `${redactSecretString(err.message)}. Remove or reconfigure provider '${safeProviderName}' in the OpenCodex configuration.`,
530
+ );
531
+ }
532
+ return formatErrorResponse(401, "authentication_error", publicOAuthAuthenticationErrorMessage(err));
533
+ }
534
+ }
535
+ // Key-auth twin of the OAuth preference above: pick a warm key BEFORE the first attempt when
536
+ // the committed one is already cooling, instead of spending the request earning a 429 the
537
+ // runtime could already predict. The picker refuses to override a healthy committed key and
538
+ // returns null without a configured strategy, so an ordinary install evaluates one predicate.
539
+ //
540
+ // It RETURNS a rebuilt route rather than mutating one, and the assignment has to land here --
541
+ // ahead of the transport pin below, the adapterProvider copy that follows it, and the request
542
+ // the HTTP path bakes later. The image bridge and web search read route.provider directly and
543
+ // have no stale-selection re-read to save them, so ordering is the whole correctness argument.
544
+ //
545
+ // The Transport variant, not the bare picker: the picker answers with the PERSISTED row, and
546
+ // a built-in provider stored in its valid minimal form would lose the adapter id, base URL
547
+ // and static headers registry backfill supplies, throwing `Unknown adapter: undefined`.
548
+ const proactiveKeyProvider = selectProactiveApiKeyTransport(
549
+ config,
550
+ route.providerName,
551
+ route.provider,
552
+ parsed.options.promptCacheKey,
553
+ );
554
+ if (proactiveKeyProvider) route.provider = proactiveKeyProvider;
555
+ route.provider = resolveProviderTransport(
556
+ route.providerName,
557
+ route.provider,
558
+ parsed.options.promptCacheKey,
559
+ route.providerName === "github-copilot" && route.provider.authMode === "oauth"
560
+ ? resolveCopilotApiBaseUrl(sentOAuthSnapshot?.apiBaseUrl)
561
+ : undefined,
562
+ );
563
+ let adapterProvider = resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire);
564
+ const stripClaudeMainAuth = options.stripClaudeMainAuthForNoncanonicalForward === true
565
+ && !isCanonicalOpenAiForwardProvider(adapterProvider)
566
+ && ((adapterProvider.adapter === "openai-responses" && adapterProvider.authMode === "forward")
567
+ || providerConsumesCallerAuthorization(adapterProvider));
568
+ if (stripClaudeMainAuth) {
569
+ releaseCodexAuthContextProbeLease(admissionState.authCtx);
570
+ admissionState.authCtx = { kind: "main", accountId: null };
571
+ route.provider = stripCodexRuntimeProviderFields(route.provider);
572
+ adapterProvider = stripCodexRuntimeProviderFields(adapterProvider);
573
+ requestState.selectedForwardHeaders = new Headers(requestState.selectedForwardHeaders);
574
+ requestState.selectedForwardHeaders.delete("authorization");
575
+ requestState.selectedForwardHeaders.delete("chatgpt-account-id");
576
+ delete route.codexAccountMode;
577
+ delete route.codexAccountId;
578
+ delete route.codexAccountNamespace;
579
+ logCtx.provider = route.providerName;
580
+ delete logCtx.accountLogLabel;
581
+ }
582
+ adapter = resolveSelectionAdapter(adapterProvider, config.cacheRetention);
583
+ bindRouteReasoningReplayScope({
584
+ parsed,
585
+ providerName: route.providerName,
586
+ provider: adapterProvider,
587
+ adapterName: adapter.name,
588
+ oauthCredentialSnapshot: replayOAuthCredentialSnapshot,
589
+ codexAuthContext: admissionState.authCtx,
590
+ forwardHeaders: requestState.selectedForwardHeaders,
591
+ });
592
+ if (!logCtx.conversationId && parsed._cursorConversationId) {
593
+ logCtx.conversationId = normalizeLogConversationId(parsed._cursorConversationId);
594
+ }
595
+ logCtx.providerAdapter = adapter.name;
596
+ // Ordinary requests receive one durable attempt only after their final initial
597
+ // adapter is resolved. Combo children own their attempt and retries keep it.
598
+ if (!options.comboAttempt && !logCtx.activeAttempt) {
599
+ const attempt = beginRequestAttempt(
600
+ (logCtx.attempts?.length ?? 0) + 1,
601
+ logCtx.provider,
602
+ route.modelId,
603
+ adapter.name,
604
+ );
605
+ logCtx.activeAttempt = attempt;
606
+ logCtx.activeAttemptStartedAt = Date.now();
607
+ (logCtx.attempts ??= []).push(attempt);
608
+ }
609
+ sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, adapter.name, logCtx.accountLogLabel);
610
+ recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, adapterProvider, adapter.name);
611
+ runTurnAdapter = adapter;
612
+ if (adapter.runTurn) {
613
+ recordAdapterTierMetadata(logCtx, adapter.tierLogForRunTurn?.(parsed));
614
+ }
615
+ // Optional route-identity linkage for attempt correlation (CL-09 consumes it). The slot
616
+ // resolves to null unless an opt-in subsystem registered a linker, so an install without
617
+ // routing profiles does no work here and loads no additional module. The non-throwing
618
+ // guarantee lives in the slot helper.
619
+ if (logCtx.activeAttempt && !logCtx.activeAttempt.labRouteSubjectId) {
620
+ const passiveSubjectId = resolvePassiveRouteSubjectId(
621
+ config,
622
+ route.providerName,
623
+ route.modelId,
624
+ route.provider,
625
+ inboundWire,
626
+ );
627
+ if (passiveSubjectId) logCtx.activeAttempt.labRouteSubjectId = passiveSubjectId;
628
+ }
629
+ const isPassthrough = "passthrough" in adapter && !!adapter.passthrough;
630
+
631
+ const rawInput = (parsed._rawBody as { input?: unknown }).input;
632
+ if (!isPassthrough && Array.isArray(rawInput) && rawInput.some(
633
+ item => item !== null && typeof item === "object" && item.type === "computer_call_output",
634
+ )) {
635
+ return formatErrorResponse(
636
+ 400,
637
+ "invalid_request_error",
638
+ "computer_call_output requires a Responses passthrough route; send screenshots as user input_image content on translated routes.",
639
+ );
640
+ }
641
+
642
+ return {
643
+ isOAuth401ReplayProvider,
644
+ get sentOAuthSnapshot(): OAuthAccessSnapshot | undefined {
645
+ return sentOAuthSnapshot;
646
+ },
647
+ set sentOAuthSnapshot(value: OAuthAccessSnapshot | undefined) {
648
+ sentOAuthSnapshot = value;
649
+ },
650
+ get replayOAuthCredentialSnapshot(): Pick<OAuthAccessSnapshot, "accountId" | "generation"> | undefined {
651
+ return replayOAuthCredentialSnapshot;
652
+ },
653
+ set replayOAuthCredentialSnapshot(value: Pick<OAuthAccessSnapshot, "accountId" | "generation"> | undefined) {
654
+ replayOAuthCredentialSnapshot = value;
655
+ },
656
+ get anthropicPoolAccountId(): string | null {
657
+ return anthropicPoolAccountId;
658
+ },
659
+ set anthropicPoolAccountId(value: string | null) {
660
+ anthropicPoolAccountId = value;
661
+ },
662
+ get anthropicPoolFailovers(): typeof anthropicPoolFailovers {
663
+ return anthropicPoolFailovers;
664
+ },
665
+ set anthropicPoolFailovers(value: typeof anthropicPoolFailovers) {
666
+ anthropicPoolFailovers = value;
667
+ },
668
+ get genericFailoverAccountId(): string | null {
669
+ return genericFailoverAccountId;
670
+ },
671
+ set genericFailoverAccountId(value: string | null) {
672
+ genericFailoverAccountId = value;
673
+ },
674
+ get genericFailovers(): typeof genericFailovers {
675
+ return genericFailovers;
676
+ },
677
+ set genericFailovers(value: typeof genericFailovers) {
678
+ genericFailovers = value;
679
+ },
680
+ get adapter(): ProviderAdapter {
681
+ return adapter;
682
+ },
683
+ set adapter(value: ProviderAdapter) {
684
+ adapter = value;
685
+ },
686
+ get activeAdapter(): ProviderAdapter {
687
+ return activeAdapter;
688
+ },
689
+ set activeAdapter(value: ProviderAdapter) {
690
+ activeAdapter = value;
691
+ },
692
+ get runTurnAdapter(): ProviderAdapter {
693
+ return runTurnAdapter;
694
+ },
695
+ set runTurnAdapter(value: ProviderAdapter) {
696
+ runTurnAdapter = value;
697
+ },
698
+ get sameTargetRequest(): AdapterRequest | undefined {
699
+ return sameTargetRequest;
700
+ },
701
+ set sameTargetRequest(value: AdapterRequest | undefined) {
702
+ sameTargetRequest = value;
703
+ },
704
+ get sameTargetParsed(): OcxParsedRequest | undefined {
705
+ return sameTargetParsed;
706
+ },
707
+ set sameTargetParsed(value: OcxParsedRequest | undefined) {
708
+ sameTargetParsed = value;
709
+ },
710
+ get sameTargetToken(): typeof sameTargetToken {
711
+ return sameTargetToken;
712
+ },
713
+ set sameTargetToken(value: typeof sameTargetToken) {
714
+ sameTargetToken = value;
715
+ },
716
+ get transportToken(): typeof transportToken {
717
+ return transportToken;
718
+ },
719
+ set transportToken(value: typeof transportToken) {
720
+ transportToken = value;
721
+ },
722
+ get imageTierBias(): typeof imageTierBias {
723
+ return imageTierBias;
724
+ },
725
+ set imageTierBias(value: typeof imageTierBias) {
726
+ imageTierBias = value;
727
+ },
728
+ invalidateSameTargetRequest,
729
+ requestBindings,
730
+ adapterBindings,
731
+ commitResolvedOAuthSelection,
732
+ refreshResolvedOAuthSelection,
733
+ passiveQuotaWriterGeneration,
734
+ applyFailoverSnapshot,
735
+ selectionIsCurrent,
736
+ resolveSelectionAdapter,
737
+ refreshRunTurnAdapter,
738
+ oauthDispatch,
739
+ anthropicSessionKey,
740
+ isPassthrough,
741
+ };
742
+ }
743
+
744
+ export type ResponsesTransport = Exclude<Awaited<ReturnType<typeof prepareResponsesTransport>>, Response>;