@yansigit/opencodex 2.32.0 → 2.33.1
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.
- package/README.md +2 -2
- package/gui/dist/assets/index-CIDo4y4k.js +102 -0
- package/gui/dist/assets/index-DrSQdTRd.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +8 -5
- package/src/adapters/anthropic.ts +20 -6
- package/src/adapters/azure.ts +20 -4
- package/src/adapters/base.ts +3 -1
- package/src/adapters/command-code.ts +131 -17
- package/src/adapters/cursor/envelope-echo.ts +162 -0
- package/src/adapters/cursor/live-transport.ts +4 -2
- package/src/adapters/cursor/native-exec-fs.ts +13 -12
- package/src/adapters/cursor/native-exec-network.ts +3 -5
- package/src/adapters/cursor/native-exec-policy.ts +47 -0
- package/src/adapters/cursor/native-exec-shell.ts +13 -25
- package/src/adapters/cursor/native-exec.ts +18 -10
- package/src/adapters/cursor/protobuf-events.ts +186 -9
- package/src/adapters/cursor/protobuf-request.ts +53 -18
- package/src/adapters/cursor/request-builder.ts +11 -3
- package/src/adapters/cursor/tool-definitions.ts +49 -2
- package/src/adapters/cursor/tool-result-normalize.ts +21 -8
- package/src/adapters/cursor/types.ts +11 -3
- package/src/adapters/cursor.ts +123 -0
- package/src/adapters/google-aistudio-parser.ts +49 -0
- package/src/adapters/google-antigravity-replay.ts +2 -2
- package/src/adapters/google-antigravity-wire.ts +7 -0
- package/src/adapters/google-errors.ts +6 -2
- package/src/adapters/google-http.ts +30 -7
- package/src/adapters/google-truncation.ts +5 -0
- package/src/adapters/google-wire-compiler.ts +38 -6
- package/src/adapters/google.ts +242 -28
- package/src/adapters/kiro-tools.ts +20 -9
- package/src/adapters/openai-chat.ts +9 -0
- package/src/adapters/openai-responses.ts +2 -1
- package/src/bridge.ts +112 -9
- package/src/chat/inbound.ts +15 -0
- package/src/claude/context-windows.ts +16 -9
- package/src/cli/doctor.ts +2 -2
- package/src/cli/index.ts +10 -3
- package/src/cli/provider.ts +6 -0
- package/src/cli/status.ts +23 -0
- package/src/codex/auth-api.ts +4 -2
- package/src/codex/autostart-health.ts +16 -0
- package/src/codex/catalog/aggregation.ts +12 -12
- package/src/codex/catalog/effort.ts +18 -3
- package/src/codex/catalog/metadata.ts +27 -1
- package/src/codex/catalog/model-metadata.ts +39 -12
- package/src/codex/catalog/parsing.ts +38 -27
- package/src/codex/catalog/provider-fetch.ts +232 -133
- package/src/codex/catalog/sync.ts +1 -1
- package/src/codex/convergence.ts +5 -0
- package/src/codex/shim.ts +56 -3
- package/src/config/provider-validation.ts +37 -0
- package/src/config.ts +78 -2
- package/src/generated/compatibility-version.json +187 -119
- package/src/generated/model-metadata.ts +3 -0
- package/src/images/loop.ts +37 -6
- package/src/lib/azure-identity.ts +154 -0
- package/src/lib/debug.ts +42 -0
- package/src/lib/errors.ts +14 -0
- package/src/lib/provider-outbound.ts +45 -33
- package/src/lib/provider-tls-profile.ts +309 -0
- package/src/lib/proxy-env.ts +49 -0
- package/src/lib/redact.ts +10 -1
- package/src/oauth/aistudio-native-daemon.ts +62 -0
- package/src/oauth/aistudio-session-sync.ts +95 -0
- package/src/oauth/antigravity-routing.ts +282 -236
- package/src/oauth/callback-server.ts +22 -2
- package/src/oauth/command-code.ts +5 -16
- package/src/oauth/google-aistudio-auth.ts +98 -0
- package/src/oauth/google-antigravity.ts +42 -5
- package/src/oauth/index.ts +15 -3
- package/src/oauth/key-providers.ts +8 -0
- package/src/oauth/kimi.ts +9 -1
- package/src/oauth/login-cli.ts +66 -1
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +6 -0
- package/src/providers/antigravity-quota.ts +3 -1
- package/src/providers/api-keys.ts +2 -1
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/derive.ts +5 -1
- package/src/providers/key-failover.ts +5 -1
- package/src/providers/openai-tiers.ts +5 -0
- package/src/providers/provider-id-rewrite.ts +1 -0
- package/src/providers/quota.ts +148 -50
- package/src/providers/registry.ts +27 -4
- package/src/providers/request-pacing.ts +33 -6
- package/src/providers/xai-transport.ts +21 -0
- package/src/responses/google-provider-options.ts +36 -0
- package/src/responses/namespace-tool-compat.ts +84 -4
- package/src/responses/parser.ts +11 -0
- package/src/responses/provider-opaque-metadata.ts +3 -3
- package/src/responses/schema.ts +37 -0
- package/src/responses/state.ts +94 -4
- package/src/router.ts +11 -2
- package/src/routing/account-pool/cooldown.ts +8 -0
- package/src/routing/account-pool/index.ts +1 -0
- package/src/server/aistudio-ws-hub.ts +295 -0
- package/src/server/auth-cors.ts +29 -0
- package/src/server/chat-completions.ts +2 -0
- package/src/server/images.ts +19 -35
- package/src/server/index.ts +94 -0
- package/src/server/management/agent-settings-routes.ts +205 -15
- package/src/server/management/combo-routes.ts +6 -0
- package/src/server/management/config-routes.ts +31 -5
- package/src/server/management/logs-usage-routes.ts +11 -5
- package/src/server/management/model-rows.ts +4 -0
- package/src/server/management/oauth-account-routes.ts +38 -7
- package/src/server/management/provider-routes.ts +113 -15
- package/src/server/management/routing-profile-routes.ts +3 -0
- package/src/server/port-reclaim.ts +19 -1
- package/src/server/request-log-conversation.ts +12 -0
- package/src/server/request-log.ts +23 -1
- package/src/server/responses/agent-task-recovery.ts +1 -1
- package/src/server/responses/compact.ts +30 -1
- package/src/server/responses/core.ts +363 -156
- package/src/server/responses/empty-completion-guard.ts +35 -6
- package/src/server/responses/fetch-helpers.ts +18 -5
- package/src/server/responses/policy-fallback.ts +1 -1
- package/src/server/responses/v2-native-parent-override.ts +59 -0
- package/src/server/responses/ws-upstream.ts +75 -2
- package/src/server/responses-undeclared-tool-guard.ts +90 -8
- package/src/server/ws-bridge.ts +2 -1
- package/src/service.ts +1 -1
- package/src/smoke/fingerprint-cache.ts +133 -0
- package/src/smoke/live-scenarios.ts +33 -0
- package/src/smoke/runner.ts +119 -0
- package/src/types/config.ts +16 -1
- package/src/types/provider.ts +18 -1
- package/src/types/request.ts +30 -0
- package/src/types/tools.ts +51 -0
- package/src/types.ts +6 -0
- package/src/usage/command-code-manifest.ts +116 -0
- package/src/usage/cost.ts +2 -2
- package/src/usage/expected-prices.ts +83 -0
- package/src/usage/log.ts +2 -2
- package/src/usage/summary.ts +34 -12
- package/src/web-search/gemini-executor.ts +6 -4
- package/src/web-search/index.ts +16 -8
- package/src/web-search/loop.ts +42 -6
- package/gui/dist/assets/index-BG43zwVe.js +0 -102
- package/gui/dist/assets/index-CiSI-jrP.css +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Server } from "bun";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { bridgeToResponsesSSE, buildResponseJSON, formatErrorResponse, type ResponsesTerminalStatus } from "../../bridge";
|
|
3
|
+
import { adapterEventDiagnosticDetails, bridgeToResponsesSSE, buildResponseJSON, formatErrorResponse, type BridgeDiagnosticContext, type BridgeDiagnosticSequence, type ResponsesTerminalStatus } from "../../bridge";
|
|
4
4
|
import { formatPassthroughUpstreamError } from "./passthrough-error";
|
|
5
5
|
import {
|
|
6
6
|
createResponsesFieldBackfillBlockRewrite,
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
resolveEnvValue,
|
|
15
15
|
} from "../../config";
|
|
16
16
|
import { parseRequest } from "../../responses/parser";
|
|
17
|
+
import { googleProviderOptionsRouteError } from "../../responses/google-provider-options";
|
|
18
|
+
import type { ProviderAdapter } from "../../adapters/base";
|
|
17
19
|
import {
|
|
18
20
|
bindReasoningReplayScope,
|
|
19
21
|
commitReasoningReplayServingIdentity,
|
|
@@ -70,9 +72,12 @@ import {
|
|
|
70
72
|
pickComboTarget,
|
|
71
73
|
targetKey,
|
|
72
74
|
} from "../../combos";
|
|
73
|
-
import { isInjectionDebugEnabled } from "../../lib/debug-settings";
|
|
75
|
+
import { isDebugEnabled, isInjectionDebugEnabled } from "../../lib/debug-settings";
|
|
76
|
+
import { debugStreamDiagnostic } from "../../lib/debug";
|
|
74
77
|
import { injectionDebugLog } from "../../lib/injection-debug-log";
|
|
78
|
+
import { OcxRequestValidationError } from "../../lib/errors";
|
|
75
79
|
import { resolveClientRetryAfter } from "../../lib/retry-after";
|
|
80
|
+
import { antigravityOAuthDestinationConfigError, isAntigravityOAuthProvider } from "../../lib/provider-tls-profile";
|
|
76
81
|
import { enrichOpenCodeZenRateLimitMessage } from "../../providers/opencode-zen-rate-limit";
|
|
77
82
|
import { modelInList, namespacedToolName } from "../../types";
|
|
78
83
|
import type {
|
|
@@ -90,6 +95,7 @@ import {
|
|
|
90
95
|
forceRefreshOAuthAccessSnapshot,
|
|
91
96
|
getOAuthCredentialApiBaseUrl,
|
|
92
97
|
getValidAccessTokenForAccount,
|
|
98
|
+
getValidAccessTokenSnapshotForAccount,
|
|
93
99
|
getValidAccessTokenSnapshot,
|
|
94
100
|
publicOAuthAuthenticationErrorMessage,
|
|
95
101
|
type OAuthAccessSnapshot,
|
|
@@ -108,12 +114,12 @@ import {
|
|
|
108
114
|
rotateAnthropicAccountOn429,
|
|
109
115
|
} from "../../oauth/anthropic-routing";
|
|
110
116
|
import {
|
|
111
|
-
antigravity429StickWaitMs,
|
|
112
117
|
antigravitySessionKeyFromParts,
|
|
113
|
-
bindAntigravityProject,
|
|
114
118
|
bindAntigravitySessionAffinity,
|
|
119
|
+
recordAntigravityCooldown,
|
|
120
|
+
recordAntigravitySyntheticFailure,
|
|
115
121
|
resolveAntigravityAccountForSession,
|
|
116
|
-
|
|
122
|
+
retryableAntigravity429DelayMs,
|
|
117
123
|
} from "../../oauth/antigravity-routing";
|
|
118
124
|
import {
|
|
119
125
|
CURSOR_POOL_MAX_FAILOVERS_PER_REQUEST,
|
|
@@ -170,6 +176,7 @@ import {
|
|
|
170
176
|
fetchWithResetRetry,
|
|
171
177
|
fetchWithTransientRetry,
|
|
172
178
|
prepareSameTarget429Wait,
|
|
179
|
+
sleepWithAbort,
|
|
173
180
|
} from "../../lib/upstream-retry";
|
|
174
181
|
import { ForwardAdmissionCredentialError, validateForwardAdmissionCredential } from "../auth-cors";
|
|
175
182
|
import type { DataPlaneAdmission } from "../auth-cors";
|
|
@@ -211,7 +218,7 @@ import {
|
|
|
211
218
|
rotateProviderTransportOn429,
|
|
212
219
|
} from "../../providers/key-failover";
|
|
213
220
|
import { shouldAttemptImageTierRetry } from "../image-retry";
|
|
214
|
-
import { resolveProviderTransport } from "../../providers/xai-transport";
|
|
221
|
+
import { isXaiResponsesDestination, resolveProviderTransport } from "../../providers/xai-transport";
|
|
215
222
|
import type { WsData } from "../ws-bridge";
|
|
216
223
|
import { codexAccountSelectionForTurn, registerTurn, trackStreamLifetime, unregisterTurn } from "../lifecycle";
|
|
217
224
|
import { redactSecretString, sanitizeLogMetadataString } from "../../lib/redact";
|
|
@@ -242,6 +249,7 @@ import {
|
|
|
242
249
|
} from "../request-log";
|
|
243
250
|
import {
|
|
244
251
|
conversationIdFromResponsesRequest,
|
|
252
|
+
inboundClientThreadIdFromRequest,
|
|
245
253
|
normalizeLogConversationId,
|
|
246
254
|
reasoningReplayConversationIdFromResponsesRequest,
|
|
247
255
|
sessionIdHeaderFromRequest,
|
|
@@ -290,6 +298,7 @@ import { createResponsesModelPayloadRewrite, rewriteResponsesModelJson } from ".
|
|
|
290
298
|
import type { EffectiveSubagentRoster, SpawnAgentSurface } from "../../codex/catalog";
|
|
291
299
|
|
|
292
300
|
import { buildToolBridgeMaps, collabSurface, injectDeveloperMessage, multiAgentGuidanceText } from "./collaboration";
|
|
301
|
+
import { decideV2NativeParentOverride } from "./v2-native-parent-override";
|
|
293
302
|
import { mapCodexAuthContextErrorToResponse } from "./codex-auth-error";
|
|
294
303
|
import { hasUnreadableEncryptedAgentTask, looksLikeBackendCiphertext, sanitizeEncryptedContentInPlace } from "./encrypted-payload";
|
|
295
304
|
import { fetchWithHeaderTimeout, providerFetch, safeHostLabel, safeOriginLabel } from "./fetch-helpers";
|
|
@@ -328,12 +337,14 @@ import {
|
|
|
328
337
|
import {
|
|
329
338
|
collectDeclaredNamelessClientCallTypes,
|
|
330
339
|
collectDeclaredWireToolNames,
|
|
340
|
+
collectProviderExecutedCallTypes,
|
|
331
341
|
createUndeclaredToolCallGuardBlockRewrite,
|
|
332
342
|
currentTurnWireToolCatalogBody,
|
|
333
343
|
hasExplicitWireToolCatalog,
|
|
334
344
|
undeclaredToolCallMessage,
|
|
335
345
|
undeclaredToolCallName,
|
|
336
346
|
undeclaredToolCallNameInResponse,
|
|
347
|
+
type ProviderExecutedCallType,
|
|
337
348
|
} from "../responses-undeclared-tool-guard";
|
|
338
349
|
import { createGithubCopilotResponsesBlockRewrite } from "../github-copilot-responses-repair";
|
|
339
350
|
import { responsesJsonToSseStream } from "../responses-json-events";
|
|
@@ -349,6 +360,34 @@ import { preflightComboStreamResponse } from "./combo-stream-preflight";
|
|
|
349
360
|
// already-committed event boundary and can replay custom adapter work.
|
|
350
361
|
const runTurnAdapterSseResponses = new WeakSet<Response>();
|
|
351
362
|
|
|
363
|
+
function diagnoseAdapterEvents(
|
|
364
|
+
events: AsyncIterable<AdapterEvent>,
|
|
365
|
+
adapterName: string,
|
|
366
|
+
requestId: string | undefined,
|
|
367
|
+
logCtx: RequestLogContext,
|
|
368
|
+
state: BridgeDiagnosticSequence,
|
|
369
|
+
): AsyncIterable<AdapterEvent> {
|
|
370
|
+
if (!requestId) return events;
|
|
371
|
+
return (async function* () {
|
|
372
|
+
for await (const event of events) {
|
|
373
|
+
const attempt = logCtx.activeAttempt;
|
|
374
|
+
debugStreamDiagnostic(
|
|
375
|
+
{
|
|
376
|
+
requestId,
|
|
377
|
+
adapterName,
|
|
378
|
+
...(attempt?.ordinal !== undefined ? { attempt: attempt.ordinal } : {}),
|
|
379
|
+
...(attempt?.recoveryKinds.at(-1) !== undefined ? { recovery: attempt.recoveryKinds.at(-1) } : {}),
|
|
380
|
+
},
|
|
381
|
+
"adapter",
|
|
382
|
+
++state.value,
|
|
383
|
+
event.type,
|
|
384
|
+
adapterEventDiagnosticDetails(event),
|
|
385
|
+
);
|
|
386
|
+
yield event;
|
|
387
|
+
}
|
|
388
|
+
})();
|
|
389
|
+
}
|
|
390
|
+
|
|
352
391
|
/**
|
|
353
392
|
* Adapters whose continuation state must survive Codex's store:false requests.
|
|
354
393
|
*/
|
|
@@ -1765,7 +1804,7 @@ export async function handleComboResponses(
|
|
|
1765
1804
|
// Expand previous_response_id before image policy and child dispatch so a
|
|
1766
1805
|
// continuation that only references prior images still fails closed when
|
|
1767
1806
|
// imageInput is disabled (and so targets see the full replayed input).
|
|
1768
|
-
const inboundClientThreadId = req.headers
|
|
1807
|
+
const inboundClientThreadId = inboundClientThreadIdFromRequest(req.headers);
|
|
1769
1808
|
const body = expandPreviousResponseInput(rawBody, inboundClientThreadId);
|
|
1770
1809
|
const scopeMismatch = previousResponseScopeMismatch(body);
|
|
1771
1810
|
if (scopeMismatch) {
|
|
@@ -2216,7 +2255,7 @@ async function handleResponsesInner(
|
|
|
2216
2255
|
let unreadableEncryptedAgentTask = hasUnreadableEncryptedAgentTask(
|
|
2217
2256
|
(body as { input?: unknown } | undefined)?.input,
|
|
2218
2257
|
);
|
|
2219
|
-
const inboundClientThreadId = req.headers
|
|
2258
|
+
const inboundClientThreadId = inboundClientThreadIdFromRequest(req.headers);
|
|
2220
2259
|
const originalBody = body;
|
|
2221
2260
|
if (options.comboReplaySnapshot) {
|
|
2222
2261
|
copyPreviousResponseReplayProvenance(options.comboReplaySnapshot.sourceBody, body);
|
|
@@ -2256,6 +2295,7 @@ async function handleResponsesInner(
|
|
|
2256
2295
|
let toolBridgeMaps: ReturnType<typeof buildToolBridgeMaps>;
|
|
2257
2296
|
try {
|
|
2258
2297
|
parsed = parseRequest(body);
|
|
2298
|
+
parsed._promptCacheKeyIsSharedCohort = options.promptCacheKeyIsSharedCohort === true;
|
|
2259
2299
|
toolBridgeMaps = buildToolBridgeMaps(parsed, translatorBudget);
|
|
2260
2300
|
if (previousResponseInputExpanded) parsed._previousResponseInputExpanded = true;
|
|
2261
2301
|
const providerContinuationCandidate = options.comboReplaySnapshot
|
|
@@ -2381,6 +2421,27 @@ async function handleResponsesInner(
|
|
|
2381
2421
|
return formatErrorResponse(404, "invalid_request_error", err instanceof Error ? err.message : String(err));
|
|
2382
2422
|
}
|
|
2383
2423
|
|
|
2424
|
+
const parentOverride = decideV2NativeParentOverride({
|
|
2425
|
+
kind: "responses",
|
|
2426
|
+
config,
|
|
2427
|
+
headers: req.headers,
|
|
2428
|
+
parsed,
|
|
2429
|
+
sourceRoute: route,
|
|
2430
|
+
comboAttempt: options.comboAttempt,
|
|
2431
|
+
targetEvidence: evidenceFromBody(parsed._rawBody),
|
|
2432
|
+
});
|
|
2433
|
+
if (parentOverride.kind === "reject") {
|
|
2434
|
+
if (parentOverride.trace) logCtx.routeDecision = parentOverride.trace as typeof logCtx.routeDecision;
|
|
2435
|
+
return formatErrorResponse(404, "invalid_request_error", parentOverride.message);
|
|
2436
|
+
}
|
|
2437
|
+
if (parentOverride.kind === "override") {
|
|
2438
|
+
route = parentOverride.route;
|
|
2439
|
+
parsed.modelId = route.modelId;
|
|
2440
|
+
if (parsed._rawBody && typeof parsed._rawBody === "object") {
|
|
2441
|
+
(parsed._rawBody as { model?: string }).model = route.modelId;
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2384
2445
|
const hasUnexpandedPreviousResponse = !!parsed.previousResponseId
|
|
2385
2446
|
&& parsed._previousResponseInputExpanded !== true;
|
|
2386
2447
|
// Exact account selectors are isolated from Pool-wide quota work. A canonical replay miss must
|
|
@@ -2504,6 +2565,7 @@ async function handleResponsesInner(
|
|
|
2504
2565
|
"_providerContinuationOwner",
|
|
2505
2566
|
"_cursorConversationId",
|
|
2506
2567
|
"_clientThreadId",
|
|
2568
|
+
"_promptCacheKeyIsSharedCohort",
|
|
2507
2569
|
"_reasoningReplayScope",
|
|
2508
2570
|
"_cursorIsolateConversation",
|
|
2509
2571
|
];
|
|
@@ -2668,14 +2730,28 @@ async function handleResponsesInner(
|
|
|
2668
2730
|
|
|
2669
2731
|
// OAuth providers: swap in a fresh access token (auto-refreshed) as the Bearer key, so the
|
|
2670
2732
|
// existing openai-chat / anthropic adapters authenticate with no change.
|
|
2671
|
-
const
|
|
2672
|
-
|
|
2733
|
+
const isAntigravityOAuth = isAntigravityOAuthProvider(route.providerName, route.provider);
|
|
2734
|
+
if (route.providerName === "google-antigravity") {
|
|
2735
|
+
const antigravityConfigError = antigravityOAuthDestinationConfigError(route.providerName, route.provider);
|
|
2736
|
+
if (antigravityConfigError) {
|
|
2737
|
+
return formatErrorResponse(400, "invalid_request_error", antigravityConfigError);
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
const isOAuth401ReplayProvider = isAntigravityOAuth
|
|
2741
|
+
|| ((route.providerName === "xai" || route.providerName === "github-copilot" || route.providerName === "kiro")
|
|
2742
|
+
&& route.provider.authMode === "oauth");
|
|
2673
2743
|
let sentOAuthSnapshot: OAuthAccessSnapshot | undefined;
|
|
2674
2744
|
let replayOAuthCredentialSnapshot: Pick<OAuthAccessSnapshot, "accountId" | "generation"> | undefined;
|
|
2675
2745
|
let anthropicPoolAccountId: string | null = null;
|
|
2676
2746
|
let anthropicPoolFailovers = 0;
|
|
2677
|
-
let antigravityAccountId: string |
|
|
2678
|
-
let
|
|
2747
|
+
let antigravityAccountId: string | null = null;
|
|
2748
|
+
let antigravitySessionKey: string | null = null;
|
|
2749
|
+
let antigravity429RetryAttempted = false;
|
|
2750
|
+
const observeAntigravityProviderError = isAntigravityOAuth
|
|
2751
|
+
? (error: { code?: string; status?: number; message?: string }) => {
|
|
2752
|
+
if (antigravityAccountId) recordAntigravitySyntheticFailure(antigravityAccountId, error);
|
|
2753
|
+
}
|
|
2754
|
+
: undefined;
|
|
2679
2755
|
let cursorPoolAccountId: string | null = null;
|
|
2680
2756
|
let cursorPoolFailovers = 0;
|
|
2681
2757
|
const oauthSessionKeyParts = {
|
|
@@ -2688,18 +2764,16 @@ async function handleResponsesInner(
|
|
|
2688
2764
|
const anthropicSessionKey = route.providerName === "anthropic" && route.provider.authMode === "oauth"
|
|
2689
2765
|
? anthropicSessionKeyFromParts(oauthSessionKeyParts)
|
|
2690
2766
|
: null;
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
? antigravitySessionKeyFromParts(oauthSessionKeyParts)
|
|
2695
|
-
: null;
|
|
2767
|
+
if (isAntigravityOAuth) {
|
|
2768
|
+
antigravitySessionKey = antigravitySessionKeyFromParts(oauthSessionKeyParts);
|
|
2769
|
+
}
|
|
2696
2770
|
const cursorSessionKey = route.providerName === "cursor"
|
|
2697
2771
|
&& route.provider.authMode === "oauth"
|
|
2698
2772
|
? cursorSessionKeyFromParts({
|
|
2699
2773
|
clientThreadId: typeof parsed._clientThreadId === "string" ? parsed._clientThreadId : null,
|
|
2700
2774
|
})
|
|
2701
2775
|
: null;
|
|
2702
|
-
if (route.provider.authMode === "oauth") {
|
|
2776
|
+
if (route.provider.authMode === "oauth" || isAntigravityOAuth) {
|
|
2703
2777
|
try {
|
|
2704
2778
|
if (route.providerName === "anthropic" && isAnthropicAccountPoolEnabled(config)) {
|
|
2705
2779
|
const selection = resolveAnthropicAccountForSession(anthropicSessionKey, config);
|
|
@@ -2721,50 +2795,6 @@ async function handleResponsesInner(
|
|
|
2721
2795
|
promoteAnthropicActiveAccount(selection.accountId);
|
|
2722
2796
|
route.provider = { ...route.provider, apiKey: accessToken };
|
|
2723
2797
|
logCtx.provider = formatAnthropicProviderForLog("anthropic", selection.accountId, config);
|
|
2724
|
-
} else if (
|
|
2725
|
-
route.providerName === "google-antigravity"
|
|
2726
|
-
&& route.provider.googleMode === "cloud-code-assist"
|
|
2727
|
-
) {
|
|
2728
|
-
const selection = resolveAntigravityAccountForSession(antigravitySessionKey);
|
|
2729
|
-
if (!selection.accountId) {
|
|
2730
|
-
if (selection.reason === "all-cooled") {
|
|
2731
|
-
return formatErrorResponse(
|
|
2732
|
-
429,
|
|
2733
|
-
"rate_limit_error",
|
|
2734
|
-
"All Google Antigravity OAuth accounts are temporarily unavailable",
|
|
2735
|
-
);
|
|
2736
|
-
}
|
|
2737
|
-
return formatErrorResponse(
|
|
2738
|
-
401,
|
|
2739
|
-
"authentication_error",
|
|
2740
|
-
"No eligible Google Antigravity OAuth account available",
|
|
2741
|
-
);
|
|
2742
|
-
}
|
|
2743
|
-
let resolved = await getValidAccessTokenSnapshot(route.providerName);
|
|
2744
|
-
if (selection.accountId !== resolved.accountId) {
|
|
2745
|
-
const accessToken = await getValidAccessTokenForAccount("google-antigravity", selection.accountId);
|
|
2746
|
-
const nextCredential = getAccountCredential("google-antigravity", selection.accountId);
|
|
2747
|
-
resolved = {
|
|
2748
|
-
...resolved,
|
|
2749
|
-
accountId: selection.accountId,
|
|
2750
|
-
accessToken,
|
|
2751
|
-
// Always replace; omitting a missing id would keep the previous account's projectId.
|
|
2752
|
-
projectId: nextCredential?.projectId,
|
|
2753
|
-
};
|
|
2754
|
-
}
|
|
2755
|
-
replayOAuthCredentialSnapshot = {
|
|
2756
|
-
accountId: resolved.accountId,
|
|
2757
|
-
generation: resolved.generation,
|
|
2758
|
-
};
|
|
2759
|
-
if (selection.reason === "failover") replayOAuthCredentialSnapshot = undefined;
|
|
2760
|
-
route.provider = { ...route.provider, apiKey: resolved.accessToken };
|
|
2761
|
-
antigravityAccountId = selection.accountId;
|
|
2762
|
-
bindAntigravitySessionAffinity(antigravitySessionKey, selection.accountId);
|
|
2763
|
-
const bound = bindAntigravityProject(route.provider, resolved.projectId);
|
|
2764
|
-
if (!bound.ok) {
|
|
2765
|
-
return formatErrorResponse(bound.status, bound.type, bound.message);
|
|
2766
|
-
}
|
|
2767
|
-
route.provider = bound.provider;
|
|
2768
2798
|
} else if (
|
|
2769
2799
|
route.providerName === "cursor"
|
|
2770
2800
|
&& route.provider.authMode === "oauth"
|
|
@@ -2796,7 +2826,28 @@ async function handleResponsesInner(
|
|
|
2796
2826
|
route.provider = { ...route.provider, apiKey: resolved.accessToken };
|
|
2797
2827
|
logCtx.provider = formatCursorProviderForLog("cursor", selection.accountId);
|
|
2798
2828
|
} else {
|
|
2799
|
-
|
|
2829
|
+
let resolved: OAuthAccessSnapshot;
|
|
2830
|
+
if (isAntigravityOAuth) {
|
|
2831
|
+
const selection = resolveAntigravityAccountForSession(antigravitySessionKey);
|
|
2832
|
+
if (!selection.accountId) {
|
|
2833
|
+
return formatErrorResponse(401, "authentication_error", "Selected Antigravity OAuth account is unavailable");
|
|
2834
|
+
}
|
|
2835
|
+
if (selection.cooldownUntil !== undefined) {
|
|
2836
|
+
const retryAfter = Math.max(1, Math.ceil((selection.cooldownUntil - Date.now()) / 1000));
|
|
2837
|
+
if (selection.cooldownKind === "geoblock") {
|
|
2838
|
+
return formatErrorResponse(403, "permission_error", "Selected Antigravity OAuth account is in local cooldown after upstream location block");
|
|
2839
|
+
}
|
|
2840
|
+
return formatErrorResponse(429, "rate_limit_error", "Selected Antigravity OAuth account is in local cooldown after upstream rate limit", { retryAfter: String(retryAfter) });
|
|
2841
|
+
}
|
|
2842
|
+
if (selection.reason === "active-needs-reauth") {
|
|
2843
|
+
return formatErrorResponse(401, "authentication_error", "Selected Antigravity OAuth account needs reauthentication");
|
|
2844
|
+
}
|
|
2845
|
+
antigravityAccountId = selection.accountId;
|
|
2846
|
+
resolved = await getValidAccessTokenSnapshotForAccount(route.providerName, selection.accountId);
|
|
2847
|
+
bindAntigravitySessionAffinity(antigravitySessionKey, selection.accountId);
|
|
2848
|
+
} else {
|
|
2849
|
+
resolved = await getValidAccessTokenSnapshot(route.providerName);
|
|
2850
|
+
}
|
|
2800
2851
|
replayOAuthCredentialSnapshot = {
|
|
2801
2852
|
accountId: resolved.accountId,
|
|
2802
2853
|
generation: resolved.generation,
|
|
@@ -2808,6 +2859,14 @@ async function handleResponsesInner(
|
|
|
2808
2859
|
// Only genuinely accountless adapter calls leave the context undefined and use local/env fallback.
|
|
2809
2860
|
parsed._kiroAuthContext = { ...(resolved.kiro ?? {}) };
|
|
2810
2861
|
}
|
|
2862
|
+
// Antigravity (cloud-code-assist) must use the project paired with this exact token
|
|
2863
|
+
// snapshot. A configured project may belong to another account and is never a fallback.
|
|
2864
|
+
if (isAntigravityOAuth) {
|
|
2865
|
+
if (!resolved.projectId) {
|
|
2866
|
+
return formatErrorResponse(400, "invalid_request_error", "Antigravity project unavailable — re-run `ocx login google-antigravity`");
|
|
2867
|
+
}
|
|
2868
|
+
route.provider = { ...route.provider, project: resolved.projectId };
|
|
2869
|
+
}
|
|
2811
2870
|
}
|
|
2812
2871
|
} catch (err) {
|
|
2813
2872
|
if (err instanceof UnsupportedOAuthProviderError) {
|
|
@@ -2847,6 +2906,27 @@ async function handleResponsesInner(
|
|
|
2847
2906
|
delete logCtx.accountLogLabel;
|
|
2848
2907
|
}
|
|
2849
2908
|
const adapter = resolveAdapter(adapterProvider, config.cacheRetention);
|
|
2909
|
+
const googleOptionsError = googleProviderOptionsRouteError(parsed, {
|
|
2910
|
+
providerName: route.providerName,
|
|
2911
|
+
provider: adapterProvider,
|
|
2912
|
+
adapterName: adapter.name,
|
|
2913
|
+
});
|
|
2914
|
+
if (googleOptionsError) {
|
|
2915
|
+
return formatErrorResponse(400, "invalid_request_error", googleOptionsError);
|
|
2916
|
+
}
|
|
2917
|
+
const assertGoogleOptionsRoute = (
|
|
2918
|
+
candidate: Pick<ProviderAdapter, "name" | "validateRequest">,
|
|
2919
|
+
provider: OcxProviderConfig,
|
|
2920
|
+
requestParsed: OcxParsedRequest = parsed,
|
|
2921
|
+
): void => {
|
|
2922
|
+
const message = googleProviderOptionsRouteError(requestParsed, {
|
|
2923
|
+
providerName: route.providerName,
|
|
2924
|
+
provider,
|
|
2925
|
+
adapterName: candidate.name,
|
|
2926
|
+
});
|
|
2927
|
+
if (message) throw new OcxRequestValidationError(message);
|
|
2928
|
+
if (requestParsed.options.providerOptions?.google) candidate.validateRequest?.(requestParsed);
|
|
2929
|
+
};
|
|
2850
2930
|
bindRouteReasoningReplayScope({
|
|
2851
2931
|
parsed,
|
|
2852
2932
|
providerName: route.providerName,
|
|
@@ -2861,6 +2941,7 @@ async function handleResponsesInner(
|
|
|
2861
2941
|
}
|
|
2862
2942
|
logCtx.providerAdapter = adapter.name;
|
|
2863
2943
|
try {
|
|
2944
|
+
assertGoogleOptionsRoute(adapter, adapterProvider);
|
|
2864
2945
|
adapter.validateRequest?.(parsed);
|
|
2865
2946
|
} catch (err) {
|
|
2866
2947
|
return formatErrorResponse(
|
|
@@ -2901,6 +2982,23 @@ async function handleResponsesInner(
|
|
|
2901
2982
|
);
|
|
2902
2983
|
if (passiveSubjectId) logCtx.activeAttempt.labRouteSubjectId = passiveSubjectId;
|
|
2903
2984
|
}
|
|
2985
|
+
const diagnosticRequestId = isDebugEnabled() ? randomUUID() : undefined;
|
|
2986
|
+
const adapterDiagnosticState: BridgeDiagnosticSequence = { value: 0 };
|
|
2987
|
+
const diagnosticContext: BridgeDiagnosticContext | undefined = diagnosticRequestId
|
|
2988
|
+
? { requestId: diagnosticRequestId, adapterName: adapter.name, sequence: adapterDiagnosticState }
|
|
2989
|
+
: undefined;
|
|
2990
|
+
const noteDiagnosticAttempt = (
|
|
2991
|
+
attempt: RequestLogContext["activeAttempt"],
|
|
2992
|
+
inputEstimate: number | undefined,
|
|
2993
|
+
recovery?: AttemptRecoveryKind,
|
|
2994
|
+
adapterName?: string,
|
|
2995
|
+
): void => {
|
|
2996
|
+
noteAttemptSend(attempt, inputEstimate, recovery);
|
|
2997
|
+
if (!diagnosticContext) return;
|
|
2998
|
+
diagnosticContext.attempt = attempt?.ordinal;
|
|
2999
|
+
diagnosticContext.recovery = recovery;
|
|
3000
|
+
if (adapterName) diagnosticContext.adapterName = adapterName;
|
|
3001
|
+
};
|
|
2904
3002
|
const isPassthrough = "passthrough" in adapter && !!adapter.passthrough;
|
|
2905
3003
|
|
|
2906
3004
|
if (adapter.name === "kiro" && parsed.previousResponseId && !parsed._previousResponseInputExpanded) {
|
|
@@ -3070,8 +3168,14 @@ async function handleResponsesInner(
|
|
|
3070
3168
|
const clientDeclaredNamelessCallTypes = collectDeclaredNamelessClientCallTypes(
|
|
3071
3169
|
clientToolAuthorizationBody,
|
|
3072
3170
|
);
|
|
3171
|
+
// Hosted calls the PROVIDER runs itself. Gated on the destination actually being xAI, so a
|
|
3172
|
+
// declaration alone cannot buy the exemption on some other upstream that never serves it.
|
|
3173
|
+
const providerExecutedCallTypes = isXaiResponsesDestination(route.provider)
|
|
3174
|
+
? collectProviderExecutedCallTypes(clientToolAuthorizationBody)
|
|
3175
|
+
: new Set<ProviderExecutedCallType>();
|
|
3073
3176
|
let request: Awaited<ReturnType<typeof adapter.buildRequest>>;
|
|
3074
3177
|
try {
|
|
3178
|
+
assertGoogleOptionsRoute(adapter, adapterProvider);
|
|
3075
3179
|
request = await adapter.buildRequest(parsed, { headers: selectedForwardHeaders, translatorBudget });
|
|
3076
3180
|
} catch (error) {
|
|
3077
3181
|
releaseCodexAuthContextProbeLease(authCtx);
|
|
@@ -3181,6 +3285,9 @@ async function handleResponsesInner(
|
|
|
3181
3285
|
// rejection is sticky for the whole turn, set from every parsed payload on the inspection side.
|
|
3182
3286
|
let inspectionSawUndeclaredTool = false;
|
|
3183
3287
|
const noteInspectedPayload = (payload: unknown) => {
|
|
3288
|
+
if (isAntigravityOAuth && antigravityAccountId) {
|
|
3289
|
+
recordAntigravitySyntheticFailure(antigravityAccountId, payload);
|
|
3290
|
+
}
|
|
3184
3291
|
// Gated on the same flag as the guard itself: with no readable catalog (or a forward-auth
|
|
3185
3292
|
// provider) every name looks undeclared, and flipping this would stop recording continuation
|
|
3186
3293
|
// state for exactly the passthrough traffic the guard deliberately stands down for.
|
|
@@ -3189,6 +3296,7 @@ async function handleResponsesInner(
|
|
|
3189
3296
|
payload,
|
|
3190
3297
|
declaredWireToolNames,
|
|
3191
3298
|
declaredNamelessClientCallTypes,
|
|
3299
|
+
providerExecutedCallTypes,
|
|
3192
3300
|
) !== undefined) {
|
|
3193
3301
|
inspectionSawUndeclaredTool = true;
|
|
3194
3302
|
}
|
|
@@ -3202,6 +3310,7 @@ async function handleResponsesInner(
|
|
|
3202
3310
|
response,
|
|
3203
3311
|
declaredWireToolNames,
|
|
3204
3312
|
declaredNamelessClientCallTypes,
|
|
3313
|
+
providerExecutedCallTypes,
|
|
3205
3314
|
) !== undefined
|
|
3206
3315
|
) {
|
|
3207
3316
|
return;
|
|
@@ -3310,7 +3419,7 @@ async function handleResponsesInner(
|
|
|
3310
3419
|
// Body is a replayable string; nothing has streamed to the client yet.
|
|
3311
3420
|
upstreamResponse = await fetchWithTransientRetry(
|
|
3312
3421
|
recovery => {
|
|
3313
|
-
|
|
3422
|
+
noteDiagnosticAttempt(logCtx.activeAttempt, passthroughEstimate, recovery, route.provider.adapter);
|
|
3314
3423
|
return fetchWithHeaderTimeout(request.url, applyUpstreamRecoveryInit({
|
|
3315
3424
|
method: request.method,
|
|
3316
3425
|
headers: request.headers,
|
|
@@ -3351,10 +3460,17 @@ async function handleResponsesInner(
|
|
|
3351
3460
|
upstream.abort();
|
|
3352
3461
|
return { failed: formatErrorResponse(502, "upstream_error", "Recovery changed the provider wire unexpectedly") };
|
|
3353
3462
|
}
|
|
3463
|
+
try {
|
|
3464
|
+
assertGoogleOptionsRoute(retryAdapter, route.provider);
|
|
3465
|
+
} catch (err) {
|
|
3466
|
+
upstream.abort();
|
|
3467
|
+
return { failed: formatErrorResponse(400, "invalid_request_error", redactSecretString(err instanceof Error ? err.message : String(err))) };
|
|
3468
|
+
}
|
|
3354
3469
|
try {
|
|
3355
3470
|
request = await retryAdapter.buildRequest(parsed, {
|
|
3356
3471
|
headers: selectedForwardHeaders,
|
|
3357
3472
|
translatorBudget,
|
|
3473
|
+
...(observeAntigravityProviderError ? { onProviderError: observeAntigravityProviderError } : {}),
|
|
3358
3474
|
});
|
|
3359
3475
|
refreshRoutedNamespaceToolAliases(request);
|
|
3360
3476
|
recordAdapterReasoning(logCtx, request);
|
|
@@ -3380,7 +3496,7 @@ async function handleResponsesInner(
|
|
|
3380
3496
|
try {
|
|
3381
3497
|
return await fetchWithTransientRetry(
|
|
3382
3498
|
innerRecovery => {
|
|
3383
|
-
|
|
3499
|
+
noteDiagnosticAttempt(logCtx.activeAttempt, passthroughEstimate, innerRecovery ?? recovery, retryAdapter.name);
|
|
3384
3500
|
return fetchWithHeaderTimeout(request.url, applyUpstreamRecoveryInit({
|
|
3385
3501
|
method: request.method,
|
|
3386
3502
|
headers: request.headers,
|
|
@@ -3428,6 +3544,14 @@ async function handleResponsesInner(
|
|
|
3428
3544
|
releaseCodexAuthContextProbeLease(authCtx);
|
|
3429
3545
|
return formatErrorResponse(401, "authentication_error", publicOAuthAuthenticationErrorMessage(err));
|
|
3430
3546
|
}
|
|
3547
|
+
if (isAntigravityOAuth && refreshed.accountId !== antigravityAccountId) {
|
|
3548
|
+
upstream.abort();
|
|
3549
|
+
return formatErrorResponse(401, "authentication_error", "Antigravity OAuth account changed during refresh");
|
|
3550
|
+
}
|
|
3551
|
+
if (isAntigravityOAuth && !refreshed.projectId) {
|
|
3552
|
+
upstream.abort();
|
|
3553
|
+
return formatErrorResponse(400, "invalid_request_error", "Antigravity project unavailable — re-run `ocx login google-antigravity`");
|
|
3554
|
+
}
|
|
3431
3555
|
sentOAuthSnapshot = refreshed;
|
|
3432
3556
|
replayOAuthCredentialSnapshot = {
|
|
3433
3557
|
accountId: refreshed.accountId,
|
|
@@ -3436,6 +3560,9 @@ async function handleResponsesInner(
|
|
|
3436
3560
|
if (route.providerName === "kiro") {
|
|
3437
3561
|
parsed._kiroAuthContext = { ...(refreshed.kiro ?? {}) };
|
|
3438
3562
|
}
|
|
3563
|
+
if (isAntigravityOAuth) {
|
|
3564
|
+
route.provider = { ...route.provider, project: refreshed.projectId };
|
|
3565
|
+
}
|
|
3439
3566
|
const refreshedProvider = resolveProviderTransport(
|
|
3440
3567
|
route.providerName,
|
|
3441
3568
|
{ ...route.provider, apiKey: refreshed.accessToken },
|
|
@@ -3466,9 +3593,11 @@ async function handleResponsesInner(
|
|
|
3466
3593
|
logCtx.accountLogLabel,
|
|
3467
3594
|
);
|
|
3468
3595
|
try {
|
|
3596
|
+
assertGoogleOptionsRoute(refreshedAdapter, refreshedProvider);
|
|
3469
3597
|
request = await refreshedAdapter.buildRequest(parsed, {
|
|
3470
3598
|
headers: selectedForwardHeaders,
|
|
3471
3599
|
translatorBudget,
|
|
3600
|
+
...(observeAntigravityProviderError ? { onProviderError: observeAntigravityProviderError } : {}),
|
|
3472
3601
|
});
|
|
3473
3602
|
refreshRoutedNamespaceToolAliases(request);
|
|
3474
3603
|
recordAdapterReasoning(logCtx, request);
|
|
@@ -3483,7 +3612,7 @@ async function handleResponsesInner(
|
|
|
3483
3612
|
try {
|
|
3484
3613
|
upstreamResponse = await fetchWithTransientRetry(
|
|
3485
3614
|
recovery => {
|
|
3486
|
-
|
|
3615
|
+
noteDiagnosticAttempt(logCtx.activeAttempt, passthroughEstimate, recovery ?? "oauth-401", refreshedAdapter.name);
|
|
3487
3616
|
return fetchWithHeaderTimeout(request.url, applyUpstreamRecoveryInit({
|
|
3488
3617
|
method: request.method,
|
|
3489
3618
|
headers: request.headers,
|
|
@@ -3545,7 +3674,7 @@ async function handleResponsesInner(
|
|
|
3545
3674
|
recovery => {
|
|
3546
3675
|
// The first send of every replay is itself a rate-limit retry; inner transient-5xx
|
|
3547
3676
|
// recoveries keep their own label (recovery is provided for those).
|
|
3548
|
-
|
|
3677
|
+
noteDiagnosticAttempt(logCtx.activeAttempt, passthroughEstimate, recovery ?? "rate-limit-429", route.provider.adapter);
|
|
3549
3678
|
return fetchWithHeaderTimeout(request.url, applyUpstreamRecoveryInit({
|
|
3550
3679
|
method: request.method,
|
|
3551
3680
|
headers: request.headers,
|
|
@@ -3853,6 +3982,7 @@ async function handleResponsesInner(
|
|
|
3853
3982
|
? createUndeclaredToolCallGuardBlockRewrite(
|
|
3854
3983
|
declaredWireToolNames,
|
|
3855
3984
|
declaredNamelessClientCallTypes,
|
|
3985
|
+
providerExecutedCallTypes,
|
|
3856
3986
|
)
|
|
3857
3987
|
: undefined,
|
|
3858
3988
|
].filter((rewrite): rewrite is NonNullable<typeof rewrite> => rewrite !== undefined);
|
|
@@ -4074,6 +4204,7 @@ async function handleResponsesInner(
|
|
|
4074
4204
|
JSON.parse(clientJson),
|
|
4075
4205
|
declaredWireToolNames,
|
|
4076
4206
|
declaredNamelessClientCallTypes,
|
|
4207
|
+
providerExecutedCallTypes,
|
|
4077
4208
|
);
|
|
4078
4209
|
} catch {
|
|
4079
4210
|
return undefined;
|
|
@@ -4271,7 +4402,8 @@ async function handleResponsesInner(
|
|
|
4271
4402
|
...(vidPlan ? { videoPlan: vidPlan } : {}),
|
|
4272
4403
|
forwardHeaders: selectedForwardHeaders,
|
|
4273
4404
|
onAttemptSend: (recovery?: AttemptRecoveryKind) =>
|
|
4274
|
-
|
|
4405
|
+
noteDiagnosticAttempt(logCtx.activeAttempt, logCtx.usageLogInputTokens, recovery, adapter.name),
|
|
4406
|
+
...(diagnosticContext ? { diagnostic: diagnosticContext } : {}),
|
|
4275
4407
|
abortSignal: options.abortSignal,
|
|
4276
4408
|
maxRounds: imgPlan && vidPlan
|
|
4277
4409
|
? clampImageMaxRounds(Math.min(config.images?.maxRounds ?? 3, config.images?.videoMaxRounds ?? 2))
|
|
@@ -4286,6 +4418,7 @@ async function handleResponsesInner(
|
|
|
4286
4418
|
recordAdapterReasoning(logCtx, request);
|
|
4287
4419
|
recordAdapterTier(logCtx, request);
|
|
4288
4420
|
},
|
|
4421
|
+
validateAdapter: (requestParsed, candidate) => assertGoogleOptionsRoute(candidate, route.provider, requestParsed),
|
|
4289
4422
|
...(vidPlan?.timeoutMs ? { videoTimeoutMs: vidPlan.timeoutMs } : {}),
|
|
4290
4423
|
onUsage: usage => {
|
|
4291
4424
|
// Cursor may assign _cursorConversationId inside the image loop's first runTurn;
|
|
@@ -4375,8 +4508,10 @@ async function handleResponsesInner(
|
|
|
4375
4508
|
recordAdapterReasoning(logCtx, request);
|
|
4376
4509
|
recordAdapterTier(logCtx, request);
|
|
4377
4510
|
},
|
|
4511
|
+
validateAdapter: (requestParsed, candidate) => assertGoogleOptionsRoute(candidate, route.provider, requestParsed),
|
|
4378
4512
|
onAttemptSend: (recovery?: AttemptRecoveryKind) =>
|
|
4379
|
-
|
|
4513
|
+
noteDiagnosticAttempt(logCtx.activeAttempt, logCtx.usageLogInputTokens, recovery, adapter.name),
|
|
4514
|
+
...(diagnosticContext ? { diagnostic: diagnosticContext } : {}),
|
|
4380
4515
|
onUsage: usage => {
|
|
4381
4516
|
logCtx.usageFromBridge = true;
|
|
4382
4517
|
if (usage) {
|
|
@@ -4385,7 +4520,7 @@ async function handleResponsesInner(
|
|
|
4385
4520
|
}
|
|
4386
4521
|
},
|
|
4387
4522
|
recordSidecarOutcome: wsPlan.forwardSidecar?.recordOutcome,
|
|
4388
|
-
connectTimeoutMs: config.connectTimeoutMs ?? 200_000,
|
|
4523
|
+
connectTimeoutMs: config.connectTimeoutMs ?? Math.max(200_000, wsPlan.routedModelStallTimeoutMs),
|
|
4389
4524
|
routedModelStallTimeoutMs: wsPlan.routedModelStallTimeoutMs,
|
|
4390
4525
|
stallTimeoutSec: wsPlan.stallTimeoutSec,
|
|
4391
4526
|
streamRoutedModelOutput: wsPlan.streamRoutedModelOutput,
|
|
@@ -4465,10 +4600,14 @@ async function handleResponsesInner(
|
|
|
4465
4600
|
pacingSlotAcquired = false,
|
|
4466
4601
|
): Promise<void> => {
|
|
4467
4602
|
try {
|
|
4603
|
+
if (diagnosticContext) {
|
|
4604
|
+
diagnosticContext.recovery = recovery;
|
|
4605
|
+
diagnosticContext.attempt = logCtx.activeAttempt?.ordinal;
|
|
4606
|
+
}
|
|
4468
4607
|
if (!pacingSlotAcquired) {
|
|
4469
4608
|
await waitForProviderRequestSlot(route.providerName, route.provider, route.modelId, runTurnAbort.signal);
|
|
4470
4609
|
}
|
|
4471
|
-
|
|
4610
|
+
noteDiagnosticAttempt(logCtx.activeAttempt, logCtx.usageLogInputTokens, recovery, adapter.name);
|
|
4472
4611
|
const runTurnProviderFetch = providerFetch(
|
|
4473
4612
|
route.provider,
|
|
4474
4613
|
options.codexWsRuntimeIdentity,
|
|
@@ -4522,13 +4661,15 @@ async function handleResponsesInner(
|
|
|
4522
4661
|
onBacklogExceeded: () => runTurnAbort.abort(),
|
|
4523
4662
|
});
|
|
4524
4663
|
void runTurnAttempt(retryQueue, "empty-completion");
|
|
4525
|
-
return retryQueue.stream();
|
|
4664
|
+
return diagnoseAdapterEvents(retryQueue.stream(), adapter.name, diagnosticRequestId, logCtx, adapterDiagnosticState);
|
|
4526
4665
|
};
|
|
4527
4666
|
|
|
4528
4667
|
const { toolNsMap, declaredToolNames, toolParameterSchemas, freeformToolNames, toolSearchToolNames } = toolBridgeMaps;
|
|
4529
4668
|
if (parsed.stream) {
|
|
4530
4669
|
void runTurn();
|
|
4531
|
-
let eventSource: AsyncIterable<AdapterEvent> =
|
|
4670
|
+
let eventSource: AsyncIterable<AdapterEvent> = diagnoseAdapterEvents(
|
|
4671
|
+
queue.stream(), adapter.name, diagnosticRequestId, logCtx, adapterDiagnosticState,
|
|
4672
|
+
);
|
|
4532
4673
|
if (options.comboAttempt) {
|
|
4533
4674
|
const preflight = await preflightAdapterEvents(eventSource);
|
|
4534
4675
|
if (preflight.error || preflight.empty) {
|
|
@@ -4566,6 +4707,7 @@ async function handleResponsesInner(
|
|
|
4566
4707
|
// grok-build's strict decoder dies on the typed response.heartbeat frame; its
|
|
4567
4708
|
// eventsource layer tolerates comment keep-alives. Codex needs the opposite.
|
|
4568
4709
|
...(logCtx.surface === "grok" ? { heartbeatStyle: "comment" as const } : {}),
|
|
4710
|
+
...(diagnosticContext ? { diagnostic: diagnosticContext } : {}),
|
|
4569
4711
|
onUsage: usage => {
|
|
4570
4712
|
// Raw adapter usage, pre wire-normalization: the bridged SSE now always carries
|
|
4571
4713
|
// zero-default detail objects, so provenance must come from here (cache_detail_missing).
|
|
@@ -4674,7 +4816,12 @@ async function handleResponsesInner(
|
|
|
4674
4816
|
let initialRequest: AdapterRequest | undefined;
|
|
4675
4817
|
let inputTokenEstimate: number | undefined;
|
|
4676
4818
|
try {
|
|
4677
|
-
|
|
4819
|
+
assertGoogleOptionsRoute(activeAdapter, route.provider);
|
|
4820
|
+
initialRequest = await activeAdapter.buildRequest(parsed, {
|
|
4821
|
+
headers: selectedForwardHeaders,
|
|
4822
|
+
translatorBudget,
|
|
4823
|
+
...(observeAntigravityProviderError ? { onProviderError: observeAntigravityProviderError } : {}),
|
|
4824
|
+
});
|
|
4678
4825
|
refreshRoutedNamespaceToolAliases(initialRequest);
|
|
4679
4826
|
recordAdapterReasoning(logCtx, initialRequest);
|
|
4680
4827
|
recordAdapterTier(logCtx, initialRequest);
|
|
@@ -4711,7 +4858,7 @@ async function handleResponsesInner(
|
|
|
4711
4858
|
let upstreamResponse: Response;
|
|
4712
4859
|
try {
|
|
4713
4860
|
if (activeAdapter.fetchResponse) {
|
|
4714
|
-
|
|
4861
|
+
noteDiagnosticAttempt(logCtx.activeAttempt, inputTokenEstimate, undefined, activeAdapter.name);
|
|
4715
4862
|
await waitForProviderRequestSlot(route.providerName, route.provider, route.modelId, upstream.signal);
|
|
4716
4863
|
upstreamResponse = await activeAdapter.fetchResponse(builtInitialRequest, {
|
|
4717
4864
|
abortSignal: upstream.signal,
|
|
@@ -4720,6 +4867,7 @@ async function handleResponsesInner(
|
|
|
4720
4867
|
executor: providerFetch(route.provider, options.codexWsRuntimeIdentity, {
|
|
4721
4868
|
providerName: route.providerName,
|
|
4722
4869
|
modelId: route.modelId,
|
|
4870
|
+
pacingSlotAcquired: true,
|
|
4723
4871
|
}),
|
|
4724
4872
|
...(antigravityAccountId ? { accountId: antigravityAccountId } : {}),
|
|
4725
4873
|
});
|
|
@@ -4731,7 +4879,7 @@ async function handleResponsesInner(
|
|
|
4731
4879
|
const fetchWithRetryPolicy = route.provider.adapter === "google" ? fetchWithTransientRetry : fetchWithResetRetry;
|
|
4732
4880
|
upstreamResponse = await fetchWithRetryPolicy(
|
|
4733
4881
|
recovery => {
|
|
4734
|
-
|
|
4882
|
+
noteDiagnosticAttempt(logCtx.activeAttempt, inputTokenEstimate, recovery, activeAdapter.name);
|
|
4735
4883
|
return fetchWithHeaderTimeout(builtInitialRequest.url, applyUpstreamRecoveryInit({
|
|
4736
4884
|
method: builtInitialRequest.method,
|
|
4737
4885
|
headers: builtInitialRequest.headers,
|
|
@@ -4782,6 +4930,13 @@ async function handleResponsesInner(
|
|
|
4782
4930
|
const rebuildAndRefetch = async (
|
|
4783
4931
|
recovery: AttemptRecoveryKind,
|
|
4784
4932
|
): Promise<Response | { failed: Response }> => {
|
|
4933
|
+
try {
|
|
4934
|
+
assertGoogleOptionsRoute(activeAdapter, route.provider);
|
|
4935
|
+
} catch (err) {
|
|
4936
|
+
cleanupUpstreamAbort();
|
|
4937
|
+
upstream.abort();
|
|
4938
|
+
return { failed: formatErrorResponse(400, "invalid_request_error", redactSecretString(err instanceof Error ? err.message : String(err))) };
|
|
4939
|
+
}
|
|
4785
4940
|
let retryRequest: AdapterRequest;
|
|
4786
4941
|
if (sameTargetRequest !== undefined && sameTargetParsed === parsed && sameTargetToken === transportToken) {
|
|
4787
4942
|
// Same target (key/adapter/parsed/tier unchanged): replay the exact cached request.
|
|
@@ -4792,6 +4947,7 @@ async function handleResponsesInner(
|
|
|
4792
4947
|
headers: selectedForwardHeaders,
|
|
4793
4948
|
translatorBudget,
|
|
4794
4949
|
...(imageTierBias > 0 ? { imageTierBias } : {}),
|
|
4950
|
+
...(observeAntigravityProviderError ? { onProviderError: observeAntigravityProviderError } : {}),
|
|
4795
4951
|
});
|
|
4796
4952
|
recordAdapterReasoning(logCtx, retryRequest);
|
|
4797
4953
|
recordAdapterTier(logCtx, retryRequest);
|
|
@@ -4816,7 +4972,7 @@ async function handleResponsesInner(
|
|
|
4816
4972
|
if (retryEstimate !== undefined) logCtx.usageLogInputTokens = retryEstimate;
|
|
4817
4973
|
logCtx.providerAdapter = activeAdapter.name;
|
|
4818
4974
|
sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, activeAdapter.name, logCtx.accountLogLabel);
|
|
4819
|
-
|
|
4975
|
+
noteDiagnosticAttempt(logCtx.activeAttempt, retryEstimate, recovery, activeAdapter.name);
|
|
4820
4976
|
try {
|
|
4821
4977
|
try {
|
|
4822
4978
|
if (activeAdapter.fetchResponse) {
|
|
@@ -4828,6 +4984,7 @@ async function handleResponsesInner(
|
|
|
4828
4984
|
executor: providerFetch(route.provider, options.codexWsRuntimeIdentity, {
|
|
4829
4985
|
providerName: route.providerName,
|
|
4830
4986
|
modelId: route.modelId,
|
|
4987
|
+
pacingSlotAcquired: true,
|
|
4831
4988
|
}),
|
|
4832
4989
|
...(antigravityAccountId ? { accountId: antigravityAccountId } : {}),
|
|
4833
4990
|
});
|
|
@@ -4869,6 +5026,14 @@ async function handleResponsesInner(
|
|
|
4869
5026
|
cleanupUpstreamAbort();
|
|
4870
5027
|
return formatErrorResponse(401, "authentication_error", publicOAuthAuthenticationErrorMessage(err));
|
|
4871
5028
|
}
|
|
5029
|
+
if (isAntigravityOAuth && refreshed.accountId !== antigravityAccountId) {
|
|
5030
|
+
cleanupUpstreamAbort();
|
|
5031
|
+
return formatErrorResponse(401, "authentication_error", "Antigravity OAuth account changed during refresh");
|
|
5032
|
+
}
|
|
5033
|
+
if (isAntigravityOAuth && !refreshed.projectId) {
|
|
5034
|
+
cleanupUpstreamAbort();
|
|
5035
|
+
return formatErrorResponse(400, "invalid_request_error", "Antigravity project unavailable — re-run `ocx login google-antigravity`");
|
|
5036
|
+
}
|
|
4872
5037
|
sentOAuthSnapshot = refreshed;
|
|
4873
5038
|
replayOAuthCredentialSnapshot = {
|
|
4874
5039
|
accountId: refreshed.accountId,
|
|
@@ -4877,6 +5042,9 @@ async function handleResponsesInner(
|
|
|
4877
5042
|
if (route.providerName === "kiro") {
|
|
4878
5043
|
parsed._kiroAuthContext = { ...(refreshed.kiro ?? {}) };
|
|
4879
5044
|
}
|
|
5045
|
+
if (isAntigravityOAuth) {
|
|
5046
|
+
route.provider = { ...route.provider, project: refreshed.projectId };
|
|
5047
|
+
}
|
|
4880
5048
|
const refreshedProvider = resolveProviderTransport(
|
|
4881
5049
|
route.providerName,
|
|
4882
5050
|
{ ...route.provider, apiKey: refreshed.accessToken },
|
|
@@ -4972,6 +5140,37 @@ async function handleResponsesInner(
|
|
|
4972
5140
|
upstreamResponse = result;
|
|
4973
5141
|
}
|
|
4974
5142
|
|
|
5143
|
+
// Antigravity never rotates accounts. Record the account cooldown and allow only one
|
|
5144
|
+
// short, abort-aware replay on the same credential; longer Retry-After values surface to
|
|
5145
|
+
// the client so a conversation cannot churn through accounts or requests.
|
|
5146
|
+
if (upstreamResponse.status === 403 && isAntigravityOAuth && antigravityAccountId) {
|
|
5147
|
+
recordAntigravityCooldown(antigravityAccountId, upstreamResponse.headers.get("retry-after"), Date.now(), "geoblock");
|
|
5148
|
+
}
|
|
5149
|
+
if (upstreamResponse.status === 429 && isAntigravityOAuth && antigravityAccountId) {
|
|
5150
|
+
recordAntigravityCooldown(antigravityAccountId, upstreamResponse.headers.get("retry-after"));
|
|
5151
|
+
const retryAfter = retryableAntigravity429DelayMs(upstreamResponse.headers.get("retry-after"));
|
|
5152
|
+
if (!antigravity429RetryAttempted && retryAfter !== null) {
|
|
5153
|
+
antigravity429RetryAttempted = true;
|
|
5154
|
+
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already closed */ }
|
|
5155
|
+
try {
|
|
5156
|
+
await sleepWithAbort(retryAfter, upstream.signal);
|
|
5157
|
+
} catch {
|
|
5158
|
+
cleanupUpstreamAbort();
|
|
5159
|
+
upstream.abort();
|
|
5160
|
+
return clientCancelledResponse();
|
|
5161
|
+
}
|
|
5162
|
+
if (options.abortSignal?.aborted || upstream.signal.aborted) {
|
|
5163
|
+
cleanupUpstreamAbort();
|
|
5164
|
+
upstream.abort();
|
|
5165
|
+
return clientCancelledResponse();
|
|
5166
|
+
}
|
|
5167
|
+
const result = await rebuildAndRefetch("rate-limit-429");
|
|
5168
|
+
if ("failed" in result) return result.failed;
|
|
5169
|
+
upstreamResponse = result;
|
|
5170
|
+
continue recovery;
|
|
5171
|
+
}
|
|
5172
|
+
}
|
|
5173
|
+
|
|
4975
5174
|
// Opt-in Anthropic OAuth account pool (#294): cool the failed account and retry
|
|
4976
5175
|
// with another eligible OAuth account (bounded per request). Disabled by default.
|
|
4977
5176
|
while (
|
|
@@ -5008,75 +5207,6 @@ async function handleResponsesInner(
|
|
|
5008
5207
|
break;
|
|
5009
5208
|
}
|
|
5010
5209
|
}
|
|
5011
|
-
// Antigravity OAuth accounts use the same bounded pre-stream carousel as Anthropic, but
|
|
5012
|
-
// their process-local routing module also excludes accounts cooled by quota/rate-limit
|
|
5013
|
-
// responses. Geoblocked 403s intentionally never enter this loop.
|
|
5014
|
-
while (
|
|
5015
|
-
upstreamResponse.status === 429
|
|
5016
|
-
&& route.providerName === "google-antigravity"
|
|
5017
|
-
&& route.provider.googleMode === "cloud-code-assist"
|
|
5018
|
-
&& antigravityAccountId
|
|
5019
|
-
&& antigravityFailovers < 3
|
|
5020
|
-
) {
|
|
5021
|
-
const stickWaitMs = antigravity429StickWaitMs(antigravityAccountId);
|
|
5022
|
-
if (stickWaitMs !== null) {
|
|
5023
|
-
await Bun.sleep(stickWaitMs);
|
|
5024
|
-
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ }
|
|
5025
|
-
invalidateSameTargetRequest();
|
|
5026
|
-
activeAdapter = resolveAdapter(
|
|
5027
|
-
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire),
|
|
5028
|
-
config.cacheRetention,
|
|
5029
|
-
);
|
|
5030
|
-
bindRouteReasoningReplayScope({
|
|
5031
|
-
parsed,
|
|
5032
|
-
providerName: route.providerName,
|
|
5033
|
-
provider: route.provider,
|
|
5034
|
-
adapterName: activeAdapter.name,
|
|
5035
|
-
codexAuthContext: authCtx,
|
|
5036
|
-
forwardHeaders: selectedForwardHeaders,
|
|
5037
|
-
});
|
|
5038
|
-
const result = await rebuildAndRefetch("rate-limit-429");
|
|
5039
|
-
if ("failed" in result) return result.failed;
|
|
5040
|
-
upstreamResponse = result;
|
|
5041
|
-
continue;
|
|
5042
|
-
}
|
|
5043
|
-
const nextAccountId = rotateAntigravityAccountOn429(antigravityAccountId, antigravitySessionKey);
|
|
5044
|
-
if (!nextAccountId) break;
|
|
5045
|
-
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ }
|
|
5046
|
-
try {
|
|
5047
|
-
const accessToken = await getValidAccessTokenForAccount("google-antigravity", nextAccountId);
|
|
5048
|
-
const nextCredential = getAccountCredential("google-antigravity", nextAccountId);
|
|
5049
|
-
const bound = bindAntigravityProject(
|
|
5050
|
-
{ ...route.provider, apiKey: accessToken },
|
|
5051
|
-
nextCredential?.projectId,
|
|
5052
|
-
);
|
|
5053
|
-
if (!bound.ok) {
|
|
5054
|
-
return formatErrorResponse(bound.status, bound.type, bound.message);
|
|
5055
|
-
}
|
|
5056
|
-
antigravityAccountId = nextAccountId;
|
|
5057
|
-
antigravityFailovers += 1;
|
|
5058
|
-
route.provider = bound.provider;
|
|
5059
|
-
replayOAuthCredentialSnapshot = undefined;
|
|
5060
|
-
invalidateSameTargetRequest();
|
|
5061
|
-
activeAdapter = resolveAdapter(
|
|
5062
|
-
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire),
|
|
5063
|
-
config.cacheRetention,
|
|
5064
|
-
);
|
|
5065
|
-
bindRouteReasoningReplayScope({
|
|
5066
|
-
parsed,
|
|
5067
|
-
providerName: route.providerName,
|
|
5068
|
-
provider: route.provider,
|
|
5069
|
-
adapterName: activeAdapter.name,
|
|
5070
|
-
codexAuthContext: authCtx,
|
|
5071
|
-
forwardHeaders: selectedForwardHeaders,
|
|
5072
|
-
});
|
|
5073
|
-
const result = await rebuildAndRefetch("rate-limit-429");
|
|
5074
|
-
if ("failed" in result) return result.failed;
|
|
5075
|
-
upstreamResponse = result;
|
|
5076
|
-
} catch {
|
|
5077
|
-
break;
|
|
5078
|
-
}
|
|
5079
|
-
}
|
|
5080
5210
|
if (
|
|
5081
5211
|
upstreamResponse.status === 402
|
|
5082
5212
|
&& cursorPoolAccountId
|
|
@@ -5296,6 +5426,16 @@ async function handleResponsesInner(
|
|
|
5296
5426
|
nextParsed: OcxParsedRequest,
|
|
5297
5427
|
initialRecoveryKind?: AttemptRecoveryKind,
|
|
5298
5428
|
): AsyncGenerator<AdapterEvent> {
|
|
5429
|
+
const requestValidationEvent = (error: unknown): AdapterEvent | undefined => {
|
|
5430
|
+
if (!(error instanceof OcxRequestValidationError)) return undefined;
|
|
5431
|
+
return {
|
|
5432
|
+
type: "error",
|
|
5433
|
+
status: error.status,
|
|
5434
|
+
errorType: "invalid_request_error",
|
|
5435
|
+
code: "invalid_request_error",
|
|
5436
|
+
message: error.message,
|
|
5437
|
+
};
|
|
5438
|
+
};
|
|
5299
5439
|
let response: Response | undefined;
|
|
5300
5440
|
// One-shot recovery label for the next top-of-loop continuation send after a failover rotation.
|
|
5301
5441
|
let nextContinuationRecoveryKind: AttemptRecoveryKind | undefined = initialRecoveryKind;
|
|
@@ -5308,6 +5448,7 @@ async function handleResponsesInner(
|
|
|
5308
5448
|
*/
|
|
5309
5449
|
const fetchContinuation = async (recoveryKind?: AttemptRecoveryKind): Promise<Response> => {
|
|
5310
5450
|
let continuationRequest: AdapterRequest | undefined;
|
|
5451
|
+
assertGoogleOptionsRoute(activeAdapter, route.provider, nextParsed);
|
|
5311
5452
|
if (sameTargetRequest !== undefined && sameTargetParsed === nextParsed && sameTargetToken === transportToken) {
|
|
5312
5453
|
// Same target (key/adapter/parsed/tier unchanged): replay the exact cached request.
|
|
5313
5454
|
continuationRequest = sameTargetRequest;
|
|
@@ -5317,6 +5458,7 @@ async function handleResponsesInner(
|
|
|
5317
5458
|
headers: selectedForwardHeaders,
|
|
5318
5459
|
translatorBudget,
|
|
5319
5460
|
...(imageTierBias > 0 ? { imageTierBias } : {}),
|
|
5461
|
+
...(observeAntigravityProviderError ? { onProviderError: observeAntigravityProviderError } : {}),
|
|
5320
5462
|
});
|
|
5321
5463
|
recordAdapterReasoning(logCtx, continuationRequest);
|
|
5322
5464
|
recordAdapterTier(logCtx, continuationRequest);
|
|
@@ -5342,8 +5484,13 @@ async function handleResponsesInner(
|
|
|
5342
5484
|
// Optional recovery label for same-target / failover continuation sends.
|
|
5343
5485
|
const replayKind: AttemptRecoveryKind | undefined = recoveryKind;
|
|
5344
5486
|
try {
|
|
5487
|
+
if (diagnosticContext) {
|
|
5488
|
+
diagnosticContext.recovery = replayKind;
|
|
5489
|
+
diagnosticContext.attempt = logCtx.activeAttempt?.ordinal;
|
|
5490
|
+
diagnosticContext.adapterName = activeAdapter.name;
|
|
5491
|
+
}
|
|
5345
5492
|
if (activeAdapter.fetchResponse) {
|
|
5346
|
-
|
|
5493
|
+
noteDiagnosticAttempt(logCtx.activeAttempt, continuationEstimate, replayKind, activeAdapter.name);
|
|
5347
5494
|
await waitForProviderRequestSlot(route.providerName, route.provider, nextParsed.modelId, upstream.signal);
|
|
5348
5495
|
return await activeAdapter.fetchResponse(builtContinuationRequest, {
|
|
5349
5496
|
abortSignal: upstream.signal,
|
|
@@ -5352,6 +5499,7 @@ async function handleResponsesInner(
|
|
|
5352
5499
|
executor: providerFetch(route.provider, options.codexWsRuntimeIdentity, {
|
|
5353
5500
|
providerName: route.providerName,
|
|
5354
5501
|
modelId: nextParsed.modelId,
|
|
5502
|
+
pacingSlotAcquired: true,
|
|
5355
5503
|
}),
|
|
5356
5504
|
...(antigravityAccountId ? { accountId: antigravityAccountId } : {}),
|
|
5357
5505
|
});
|
|
@@ -5361,7 +5509,7 @@ async function handleResponsesInner(
|
|
|
5361
5509
|
const fetchContinuationWithRetryPolicy = route.provider.adapter === "google" ? fetchWithTransientRetry : fetchWithResetRetry;
|
|
5362
5510
|
return await fetchContinuationWithRetryPolicy(
|
|
5363
5511
|
recovery => {
|
|
5364
|
-
|
|
5512
|
+
noteDiagnosticAttempt(logCtx.activeAttempt, continuationEstimate, recovery ?? replayKind, activeAdapter.name);
|
|
5365
5513
|
return fetchWithHeaderTimeout(
|
|
5366
5514
|
builtContinuationRequest.url,
|
|
5367
5515
|
applyUpstreamRecoveryInit({
|
|
@@ -5390,7 +5538,10 @@ async function handleResponsesInner(
|
|
|
5390
5538
|
nextContinuationRecoveryKind = undefined;
|
|
5391
5539
|
response = await fetchContinuation(recoveryKind);
|
|
5392
5540
|
} catch (error) {
|
|
5393
|
-
|
|
5541
|
+
const validationEvent = requestValidationEvent(error);
|
|
5542
|
+
if (validationEvent) {
|
|
5543
|
+
yield validationEvent;
|
|
5544
|
+
} else if (options.abortSignal?.aborted || upstream.signal.aborted) {
|
|
5394
5545
|
yield { type: "error", message: "client closed request during terminal continuation", status: 499 };
|
|
5395
5546
|
} else {
|
|
5396
5547
|
yield { type: "error", message: `Provider continuation failed: ${redactSecretString(error instanceof Error ? error.message : String(error))}` };
|
|
@@ -5436,7 +5587,10 @@ async function handleResponsesInner(
|
|
|
5436
5587
|
try {
|
|
5437
5588
|
response = await fetchContinuation("rate-limit-429");
|
|
5438
5589
|
} catch (error) {
|
|
5439
|
-
|
|
5590
|
+
const validationEvent = requestValidationEvent(error);
|
|
5591
|
+
if (validationEvent) {
|
|
5592
|
+
yield validationEvent;
|
|
5593
|
+
} else if (options.abortSignal?.aborted || upstream.signal.aborted) {
|
|
5440
5594
|
yield { type: "error", message: "client closed request during terminal continuation", status: 499 };
|
|
5441
5595
|
} else {
|
|
5442
5596
|
yield { type: "error", message: `Provider continuation failed: ${redactSecretString(error instanceof Error ? error.message : String(error))}` };
|
|
@@ -5445,6 +5599,42 @@ async function handleResponsesInner(
|
|
|
5445
5599
|
}
|
|
5446
5600
|
}
|
|
5447
5601
|
|
|
5602
|
+
if (response.status === 403 && isAntigravityOAuth && antigravityAccountId) {
|
|
5603
|
+
recordAntigravityCooldown(antigravityAccountId, response.headers.get("retry-after"), Date.now(), "geoblock");
|
|
5604
|
+
}
|
|
5605
|
+
if (response.status === 429 && isAntigravityOAuth && antigravityAccountId) {
|
|
5606
|
+
recordAntigravityCooldown(antigravityAccountId, response.headers.get("retry-after"));
|
|
5607
|
+
const retryAfter = retryableAntigravity429DelayMs(response.headers.get("retry-after"));
|
|
5608
|
+
if (!antigravity429RetryAttempted && retryAfter !== null) {
|
|
5609
|
+
antigravity429RetryAttempted = true;
|
|
5610
|
+
try { void response.body?.cancel().catch(() => {}); } catch { /* already closed */ }
|
|
5611
|
+
try {
|
|
5612
|
+
await sleepWithAbort(retryAfter, upstream.signal);
|
|
5613
|
+
} catch {
|
|
5614
|
+
if (options.abortSignal?.aborted || upstream.signal.aborted) {
|
|
5615
|
+
yield { type: "error", message: "client closed request during terminal continuation", status: 499 };
|
|
5616
|
+
} else {
|
|
5617
|
+
yield { type: "error", message: "Provider continuation failed: retry wait interrupted" };
|
|
5618
|
+
}
|
|
5619
|
+
return;
|
|
5620
|
+
}
|
|
5621
|
+
if (options.abortSignal?.aborted || upstream.signal.aborted) {
|
|
5622
|
+
yield { type: "error", message: "client closed request during terminal continuation", status: 499 };
|
|
5623
|
+
return;
|
|
5624
|
+
}
|
|
5625
|
+
try {
|
|
5626
|
+
response = await fetchContinuation("rate-limit-429");
|
|
5627
|
+
} catch (error) {
|
|
5628
|
+
if (options.abortSignal?.aborted || upstream.signal.aborted) {
|
|
5629
|
+
yield { type: "error", message: "client closed request during terminal continuation", status: 499 };
|
|
5630
|
+
} else {
|
|
5631
|
+
yield { type: "error", message: `Provider continuation failed: ${redactSecretString(error instanceof Error ? error.message : String(error))}` };
|
|
5632
|
+
}
|
|
5633
|
+
return;
|
|
5634
|
+
}
|
|
5635
|
+
}
|
|
5636
|
+
}
|
|
5637
|
+
|
|
5448
5638
|
if (response.status === 429 && hasKeyPoolFailover(route.provider)) {
|
|
5449
5639
|
const rotated = rotateProviderTransportOn429(config, route.providerName, route.provider, {
|
|
5450
5640
|
retryAfter: response.headers.get("retry-after"),
|
|
@@ -5651,7 +5841,13 @@ async function handleResponsesInner(
|
|
|
5651
5841
|
const detachContinuationBodyGuard = cancelBodyOnAbort(response.body, upstream.signal);
|
|
5652
5842
|
try {
|
|
5653
5843
|
if (nextParsed.stream) {
|
|
5654
|
-
yield*
|
|
5844
|
+
yield* diagnoseAdapterEvents(
|
|
5845
|
+
activeAdapter.parseStream(response, translatorBudget, logCtx.activeTierMetadata),
|
|
5846
|
+
activeAdapter.name,
|
|
5847
|
+
diagnosticRequestId,
|
|
5848
|
+
logCtx,
|
|
5849
|
+
adapterDiagnosticState,
|
|
5850
|
+
);
|
|
5655
5851
|
} else if (activeAdapter.parseResponse) {
|
|
5656
5852
|
yield* await activeAdapter.parseResponse(response, translatorBudget, logCtx.activeTierMetadata);
|
|
5657
5853
|
} else {
|
|
@@ -5661,7 +5857,10 @@ async function handleResponsesInner(
|
|
|
5661
5857
|
detachContinuationBodyGuard();
|
|
5662
5858
|
}
|
|
5663
5859
|
} catch (error) {
|
|
5664
|
-
|
|
5860
|
+
const validationEvent = requestValidationEvent(error);
|
|
5861
|
+
if (validationEvent) {
|
|
5862
|
+
yield validationEvent;
|
|
5863
|
+
} else if (options.abortSignal?.aborted) {
|
|
5665
5864
|
yield { type: "error", message: "client closed request during terminal continuation", status: 499 };
|
|
5666
5865
|
} else {
|
|
5667
5866
|
yield { type: "error", message: `Provider continuation parse failed: ${redactSecretString(error instanceof Error ? error.message : String(error))}` };
|
|
@@ -5683,11 +5882,18 @@ async function handleResponsesInner(
|
|
|
5683
5882
|
};
|
|
5684
5883
|
|
|
5685
5884
|
if (parsed.stream) {
|
|
5686
|
-
const initialEventStream =
|
|
5687
|
-
upstreamResponse,
|
|
5688
|
-
|
|
5689
|
-
|
|
5885
|
+
const initialEventStream = diagnoseAdapterEvents(
|
|
5886
|
+
activeAdapter.parseStream(upstreamResponse, translatorBudget, logCtx.activeTierMetadata),
|
|
5887
|
+
activeAdapter.name,
|
|
5888
|
+
diagnosticRequestId,
|
|
5889
|
+
logCtx,
|
|
5890
|
+
adapterDiagnosticState,
|
|
5690
5891
|
);
|
|
5892
|
+
if (diagnosticContext) {
|
|
5893
|
+
diagnosticContext.adapterName = activeAdapter.name;
|
|
5894
|
+
diagnosticContext.attempt = logCtx.activeAttempt?.ordinal;
|
|
5895
|
+
diagnosticContext.recovery = logCtx.activeAttempt?.recoveryKinds.at(-1);
|
|
5896
|
+
}
|
|
5691
5897
|
const eventStream = terminalGuardEnabled
|
|
5692
5898
|
? guardTerminalEventStream({
|
|
5693
5899
|
parsed,
|
|
@@ -5723,6 +5929,7 @@ async function handleResponsesInner(
|
|
|
5723
5929
|
...(routedCompaction ? { compaction: true } : {}),
|
|
5724
5930
|
// Same grok-surface split as the runTurn branch above.
|
|
5725
5931
|
...(logCtx.surface === "grok" ? { heartbeatStyle: "comment" as const } : {}),
|
|
5932
|
+
...(diagnosticContext ? { diagnostic: diagnosticContext } : {}),
|
|
5726
5933
|
onUsage: usage => {
|
|
5727
5934
|
// Raw adapter usage, pre wire-normalization (see the runTurn branch above).
|
|
5728
5935
|
logCtx.usageFromBridge = true;
|