@bitkyc08/opencodex 2.79.0 → 2.81.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.
- package/README.md +3 -3
- package/gui/dist/assets/App-7yhoys8a.js +54 -0
- package/gui/dist/assets/Tray-EwAOsP0S.js +1 -0
- package/gui/dist/assets/index-RsoaAxXY.js +94 -0
- package/gui/dist/assets/quota-summary-D2lN8tDm.js +1 -0
- package/gui/dist/index.html +1 -1
- package/gui/dist/provider-icons/claude-green.svg +1 -0
- package/package.json +3 -2
- package/src/adapters/anthropic-model-contract.ts +8 -2
- package/src/adapters/anthropic.ts +7 -2
- package/src/adapters/codebuddy/mcp-server.ts +2 -25
- package/src/adapters/codebuddy/tool-bridge.ts +1 -7
- package/src/adapters/coding-agent/mcp-server.ts +33 -0
- package/src/adapters/coding-agent/protocol.ts +122 -17
- package/src/adapters/coding-agent/tool-catalog.ts +75 -0
- package/src/adapters/coding-agent/turn.ts +101 -44
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +2 -0
- package/src/adapters/cursor/live-transport.ts +5 -3
- package/src/adapters/cursor/native-exec-fs.ts +12 -9
- package/src/adapters/cursor/native-exec.ts +13 -5
- package/src/adapters/cursor/protobuf-request.ts +13 -7
- package/src/adapters/cursor/request-builder.ts +23 -10
- package/src/adapters/cursor/tool-definitions.ts +9 -6
- package/src/adapters/cursor/tool-guidance.ts +29 -8
- package/src/adapters/cursor/tool-naming.ts +40 -7
- package/src/adapters/cursor/tool-wording.ts +46 -0
- package/src/adapters/cursor.ts +3 -0
- package/src/adapters/devin/live-models.ts +2 -0
- package/src/adapters/devin.ts +25 -11
- package/src/adapters/kiro-retry.ts +9 -4
- package/src/adapters/openai-chat/messages.ts +7 -4
- package/src/adapters/physical-send.ts +8 -5
- package/src/adapters/qoder/adapter.ts +41 -6
- package/src/adapters/qoder/scaffold-guard.ts +2 -3
- package/src/adapters/zed.ts +16 -1
- package/src/claude/inbound-cache-stabilize.ts +4 -9
- package/src/claude/inbound.ts +10 -0
- package/src/claude/intercept/model-bindings.ts +1 -0
- package/src/claude/intercept/picker-listener.ts +71 -4
- package/src/cli/account-anthropic-threshold.ts +14 -5
- package/src/cli/account-api.ts +3 -1
- package/src/cli/account-extended.ts +13 -9
- package/src/cli/account-policy-dto.ts +1 -1
- package/src/cli/account-policy.ts +13 -4
- package/src/cli/account.ts +3 -3
- package/src/cli/capabilities-accounts.ts +2 -2
- package/src/cli/capabilities-agents-routing.ts +24 -0
- package/src/cli/capabilities-integrations.ts +16 -0
- package/src/cli/codex-shim-autorestore.ts +3 -0
- package/src/cli/dispatch.ts +10 -1
- package/src/cli/help.ts +4 -1
- package/src/cli/index.ts +11 -5
- package/src/cli/integration-plan-dto.ts +1 -0
- package/src/cli/integration-preview.ts +8 -1
- package/src/cli/integrations.ts +32 -3
- package/src/cli/message-args.ts +34 -0
- package/src/cli/message-command.ts +43 -0
- package/src/cli/message-runtime.ts +6 -0
- package/src/cli/observe.ts +3 -1
- package/src/cli/provider.ts +29 -6
- package/src/cli/registry.ts +24 -2
- package/src/clients/config-export/commandcode.ts +131 -0
- package/src/clients/config-export/contracts.ts +12 -0
- package/src/clients/config-export.ts +64 -1
- package/src/codex/auth-api/account-list.ts +11 -3
- package/src/codex/auth-api/main-account-probe.ts +25 -8
- package/src/codex/auth-api/pool-mode-gate.ts +33 -9
- package/src/codex/auth-api/pool-quota-probe.ts +15 -1
- package/src/codex/auth-api/routes.ts +1 -0
- package/src/codex/autostart-health.ts +3 -3
- package/src/codex/catalog/auto-review.ts +13 -6
- package/src/codex/catalog/build-entries.ts +15 -7
- package/src/codex/catalog/control-plane.ts +45 -0
- package/src/codex/catalog/effort.ts +11 -1
- package/src/codex/catalog/gather-capture.ts +5 -3
- package/src/codex/catalog/parsing.ts +2 -1
- package/src/codex/catalog/provider-models.ts +15 -9
- package/src/codex/control-plane-models.ts +6 -0
- package/src/codex/journal.ts +36 -5
- package/src/codex/shim-overlay.ts +9 -1
- package/src/combos/jev-decision-contract.ts +25 -2
- package/src/combos/jev.ts +6 -3
- package/src/combos/types.ts +1 -1
- package/src/config/diagnostics.ts +27 -16
- package/src/config/load-degrade.ts +34 -0
- package/src/config/schema/anthropic-account-pool.ts +143 -0
- package/src/config/schema/config-schema.ts +7 -1
- package/src/config/schema/leaf-validators.ts +4 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +347 -199
- package/src/generated/model-metadata.ts +5 -4
- package/src/images/loop.ts +2 -1
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/mutation-plan.ts +7 -0
- package/src/integrations/registry.ts +8 -0
- package/src/integrations/state.ts +9 -2
- package/src/integrations/writer.ts +8 -2
- package/src/lib/lab-live-route-production.ts +9 -1
- package/src/lib/request-execution-budget.ts +236 -58
- package/src/lib/spend-pool-alias-validation.ts +79 -0
- package/src/lib/spend-pool-continuity.ts +34 -0
- package/src/lib/spend-reservation-ledger.ts +412 -50
- package/src/lib/state-store-registrations.ts +20 -3
- package/src/lib/upstream-retry.ts +17 -5
- package/src/lib/workflow-budget.ts +53 -11
- package/src/messaging/budget.ts +45 -0
- package/src/messaging/discovery.ts +57 -0
- package/src/messaging/envelope.ts +49 -0
- package/src/messaging/input.ts +37 -0
- package/src/messaging/rpc.ts +196 -0
- package/src/messaging/send.ts +58 -0
- package/src/messaging/socket.ts +72 -0
- package/src/messaging/types.ts +29 -0
- package/src/oauth/anthropic-account-refusal.ts +51 -17
- package/src/oauth/anthropic-account-threshold.ts +8 -2
- package/src/oauth/anthropic-continuity.ts +15 -1
- package/src/oauth/anthropic-model-quota.ts +120 -48
- package/src/oauth/anthropic-model-routes.ts +11 -3
- package/src/oauth/anthropic-oauth-definitions.ts +98 -0
- package/src/oauth/anthropic-pool-config.ts +30 -0
- package/src/oauth/anthropic-rate-limit-policy.ts +64 -9
- package/src/oauth/anthropic-routing.ts +1202 -866
- package/src/oauth/anthropic-send-ownership.ts +41 -0
- package/src/oauth/anthropic.ts +4 -1
- package/src/oauth/generic-account-failover.ts +3 -1
- package/src/oauth/health.ts +8 -6
- package/src/oauth/index.ts +90 -173
- package/src/oauth/kiro.ts +22 -9
- package/src/oauth/login-cli.ts +2 -0
- package/src/oauth/model-discovery-auth.ts +43 -0
- package/src/oauth/pool-kernel.ts +11 -5
- package/src/oauth/pool-settings-capability.ts +5 -3
- package/src/oauth/store-anthropic-instance.ts +87 -0
- package/src/oauth/store.ts +67 -9
- package/src/oauth/token-guardian.ts +2 -0
- package/src/oauth/types.ts +2 -0
- package/src/protocols/plan-snapshot.ts +5 -0
- package/src/protocols/settings.ts +20 -5
- package/src/providers/anthropic-instance-id.ts +47 -0
- package/src/providers/anthropic-instance.ts +48 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +11 -1
- package/src/providers/derive.ts +2 -0
- package/src/providers/kiro-models.ts +3 -0
- package/src/providers/label.ts +1 -1
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/quota/account-cache.ts +37 -11
- package/src/providers/quota/anthropic-account-quota.ts +93 -0
- package/src/providers/quota/anthropic-cooldown-recovery.ts +222 -111
- package/src/providers/quota/report-cache.ts +24 -1
- package/src/providers/quota/vendor-probes-oauth.ts +46 -14
- package/src/providers/quota.ts +22 -32
- package/src/providers/registry/entries-core.ts +35 -30
- package/src/providers/registry/model-ids.ts +1 -0
- package/src/providers/registry/model-seeds.ts +7 -4
- package/src/providers/registry/types.ts +3 -0
- package/src/providers/registry.ts +3 -3
- package/src/responses/spill-store.ts +5 -1
- package/src/responses/state/metrics.ts +87 -0
- package/src/responses/state/snapshot-codec.ts +6 -0
- package/src/responses/state/spill-queue.ts +1 -0
- package/src/responses/state/unforced-store-false.ts +55 -0
- package/src/responses/state.ts +50 -66
- package/src/router.ts +28 -4
- package/src/routing/compatibility/assemble.ts +1 -1
- package/src/routing/quota.ts +7 -3
- package/src/server/auth-cors.ts +5 -0
- package/src/server/chat-completions.ts +6 -1
- package/src/server/chat-native.ts +45 -25
- package/src/server/claude-messages.ts +16 -2
- package/src/server/gui-static.ts +38 -4
- package/src/server/index/spend-ledger-lifecycle.ts +10 -4
- package/src/server/index.ts +16 -16
- package/src/server/inference/context.ts +1 -1
- package/src/server/lifecycle.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +30 -6
- package/src/server/management/anthropic-account-threshold.ts +12 -9
- package/src/server/management/anthropic-pool-settings.ts +72 -0
- package/src/server/management/anthropic-reset-grant-routes.ts +98 -39
- package/src/server/management/config-routes.ts +27 -7
- package/src/server/management/decision-routes.ts +2 -1
- package/src/server/management/integration-routes.ts +25 -2
- package/src/server/management/oauth-account-routes.ts +79 -98
- package/src/server/management/provider-instance-ownership.ts +44 -0
- package/src/server/management/provider-routes.ts +21 -28
- package/src/server/management/route-registry.ts +3 -1
- package/src/server/management/routing-profile-routes.ts +4 -4
- package/src/server/management/system-restart.ts +44 -14
- package/src/server/management/system-routes.ts +24 -2
- package/src/server/management/vision-sidecar-options.ts +9 -6
- package/src/server/management/web-search-sidecar-options.ts +51 -2
- package/src/server/management-auth.ts +6 -0
- package/src/server/messages-native-eligibility.ts +3 -2
- package/src/server/messages-native-oauth.ts +86 -41
- package/src/server/messages-native-scope.ts +4 -1
- package/src/server/messages-native-selector.ts +39 -0
- package/src/server/messages-native.ts +84 -25
- package/src/server/request-log.ts +5 -0
- package/src/server/responses/adapter-continuation.ts +29 -17
- package/src/server/responses/adapter-dispatch.ts +47 -17
- package/src/server/responses/antigravity-validation-refusal.ts +20 -6
- package/src/server/responses/combo-requested-effort.ts +14 -0
- package/src/server/responses/compact.ts +25 -8
- package/src/server/responses/compaction-recovery.ts +1 -1
- package/src/server/responses/core-combo-native.ts +4 -1
- package/src/server/responses/core-combo.ts +21 -18
- package/src/server/responses/core-normalize.ts +1 -0
- package/src/server/responses/core-options.ts +3 -1
- package/src/server/responses/core-replay.ts +5 -0
- package/src/server/responses/passthrough-delivery.ts +7 -3
- package/src/server/responses/passthrough-dispatch.ts +21 -8
- package/src/server/responses/request-prepare.ts +37 -5
- package/src/server/responses/request-send-budget.ts +74 -37
- package/src/server/responses/request-spend.ts +325 -33
- package/src/server/responses/request-transport.ts +132 -52
- package/src/server/responses/run-turn-execution.ts +11 -3
- package/src/server/responses/sidecar-execution.ts +96 -14
- package/src/server/responses-undeclared-tool-guard.ts +5 -1
- package/src/server/workflow-refusal.ts +30 -0
- package/src/service/desktop-startup.ts +100 -3
- package/src/service/ownership-mutation-lease.d.mts +1 -1
- package/src/service/ownership-mutation-lease.mjs +5 -7
- package/src/sidecar/anthropic-binding.ts +79 -0
- package/src/sidecar/auth.ts +76 -2
- package/src/types/anthropic-account-pool.ts +33 -0
- package/src/types/config.ts +12 -28
- package/src/types/provider.ts +5 -0
- package/src/types/tools.ts +7 -0
- package/src/types.ts +4 -0
- package/src/usage/cost.ts +3 -2
- package/src/usage/expected-prices.ts +66 -14
- package/src/vision/anthropic-describe.ts +16 -3
- package/src/vision/index.ts +3 -0
- package/src/vision/plan.ts +19 -6
- package/src/web-search/alpha-search.ts +7 -1
- package/src/web-search/anthropic-executor.ts +16 -4
- package/src/web-search/header-deadline.ts +67 -0
- package/src/web-search/index.ts +7 -3
- package/src/web-search/loop.ts +32 -10
- package/src/web-search/passthrough-bridge.ts +4 -1
- package/src/web-search/sidecar-providers.ts +4 -3
- package/gui/dist/assets/App-BOUa-ek3.js +0 -54
- package/gui/dist/assets/Tray-DUtzB4zF.js +0 -1
- package/gui/dist/assets/index-DwHNDF4s.js +0 -94
- package/gui/dist/assets/quota-summary-BuL4WIBo.js +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { classifyAnthropic429 } from "../../oauth/anthropic-rate-limit-policy";
|
|
2
|
-
import {
|
|
2
|
+
import { rotateAnthropicAccountOnResponseForInstance } from "../../oauth/anthropic-account-refusal";
|
|
3
3
|
import { authorizeResendForRecovery } from "../../lib/request-resend-gate";
|
|
4
4
|
import { ambiguousResendAllowanceFor, selfContainedResponsesBody } from "./reset-replay";
|
|
5
5
|
import { transientSendCapFor } from "./request-send-budget";
|
|
@@ -65,7 +65,7 @@ import { bindRouteReasoningReplayScope } from "./core-replay";
|
|
|
65
65
|
import {
|
|
66
66
|
AnthropicAccountCooldownError,
|
|
67
67
|
ANTHROPIC_POOL_MAX_FAILOVERS_PER_REQUEST,
|
|
68
|
-
|
|
68
|
+
anthropicRoutingFor,
|
|
69
69
|
formatAnthropicProviderForLog,
|
|
70
70
|
} from "../../oauth/anthropic-routing";
|
|
71
71
|
import {
|
|
@@ -106,6 +106,7 @@ import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
|
|
|
106
106
|
import { mapCodexAuthContextErrorToResponse } from "./codex-auth-error";
|
|
107
107
|
import { chargeWorkflowSends } from "../../lib/workflow-budget";
|
|
108
108
|
import { isAntigravityValidationRefusal } from "./antigravity-validation-refusal";
|
|
109
|
+
import { unboundPoolSpendRefusalResponse } from "../workflow-refusal";
|
|
109
110
|
|
|
110
111
|
/** One responsibility of the Responses request pipeline; state owners are explicit. */
|
|
111
112
|
export async function prepareAdapterExchange(
|
|
@@ -139,6 +140,8 @@ export async function prepareAdapterExchange(
|
|
|
139
140
|
| "replayOAuthCredentialSnapshot"
|
|
140
141
|
| "invalidateSameTargetRequest"
|
|
141
142
|
| "resolveSelectionAdapter"
|
|
143
|
+
| "anthropicInstance"
|
|
144
|
+
| "currentAnthropicRouteDecision"
|
|
142
145
|
| "anthropicRouteDecision"
|
|
143
146
|
| "anthropicPoolAccountId"
|
|
144
147
|
| "anthropicPoolFailovers"
|
|
@@ -157,7 +160,8 @@ export async function prepareAdapterExchange(
|
|
|
157
160
|
| "noteAdapterPhysicalSend"
|
|
158
161
|
| "noteAdapterRecoveryWithheld"
|
|
159
162
|
| "remainingTransientSendBudget"
|
|
160
|
-
| "
|
|
163
|
+
| "adapterSendBudget"
|
|
164
|
+
| "transientSendReporter"
|
|
161
165
|
| "recoverySendAllowance"
|
|
162
166
|
| "recoveryClassFor"
|
|
163
167
|
| "sendBudgetExhausted"
|
|
@@ -175,6 +179,8 @@ export async function prepareAdapterExchange(
|
|
|
175
179
|
refreshResolvedOAuthSelection,
|
|
176
180
|
invalidateSameTargetRequest,
|
|
177
181
|
resolveSelectionAdapter,
|
|
182
|
+
anthropicInstance,
|
|
183
|
+
currentAnthropicRouteDecision,
|
|
178
184
|
anthropicSessionKey,
|
|
179
185
|
commitResolvedOAuthSelection,
|
|
180
186
|
applyFailoverSnapshot,
|
|
@@ -194,7 +200,7 @@ export async function prepareAdapterExchange(
|
|
|
194
200
|
noteAdapterPhysicalSend,
|
|
195
201
|
noteAdapterRecoveryWithheld,
|
|
196
202
|
remainingTransientSendBudget,
|
|
197
|
-
|
|
203
|
+
transientSendReporter,
|
|
198
204
|
recoverySendAllowance,
|
|
199
205
|
recoveryClassFor,
|
|
200
206
|
sendBudgetExhausted,
|
|
@@ -333,6 +339,8 @@ export async function prepareAdapterExchange(
|
|
|
333
339
|
try {
|
|
334
340
|
if (transportState.activeAdapter.fetchResponse) {
|
|
335
341
|
transportState.noteRoutedAttemptSend(inputTokenEstimate);
|
|
342
|
+
const producer = adapterDispatchBudget?.beginSpendProducer?.();
|
|
343
|
+
try {
|
|
336
344
|
upstreamResponse = await withProviderRequestSlot(route.providerName, route.provider, route.modelId, upstream.signal, pacingSlot =>
|
|
337
345
|
transportState.activeAdapter.fetchResponse!(builtInitialRequest, {
|
|
338
346
|
kiroPreferAccountFailover: route.providerName === "kiro" && isGenericOAuthFailoverEnabled(config, "kiro"),
|
|
@@ -352,6 +360,7 @@ export async function prepareAdapterExchange(
|
|
|
352
360
|
? createCodexAuthDispatchGuard(admissionState.authCtx, options.codexAuthPolicy ?? config, route.modelId, options.admission, options.visionDescribeTerminal === true) : undefined,
|
|
353
361
|
}),
|
|
354
362
|
}));
|
|
363
|
+
} finally { producer?.close(); }
|
|
355
364
|
} else {
|
|
356
365
|
// #1851 scope guard: transient-5xx retry on this generic adapter path is opt-in for
|
|
357
366
|
// direct Google AI Studio only (Vertex/Antigravity use fetchResponse above). Other
|
|
@@ -361,13 +370,14 @@ export async function prepareAdapterExchange(
|
|
|
361
370
|
// legacy direct-Google exception is preserved exactly; every other adapter still keeps
|
|
362
371
|
// reset-only semantics so combo failover hops on the first 5xx.
|
|
363
372
|
const transientPolicy = transientRetryPolicyFor(route.provider);
|
|
373
|
+
const resetPolicy = resetReplayPolicyFor(route.provider);
|
|
364
374
|
const compactPrepaid = options.compactionRecoveryAttempted ? sendBudgetState.pendingHopPermit : undefined;
|
|
365
375
|
if (compactPrepaid) sendBudgetState.pendingHopPermit = undefined;
|
|
366
376
|
let compactPrepaidUsed = false;
|
|
367
377
|
// Combo and emergency compaction admission already book this target's first send.
|
|
368
378
|
// Its configured initial ceiling is target-local; the shared remainder below still
|
|
369
379
|
// accounts for earlier targets without deducting their sends from this target twice.
|
|
370
|
-
const initialSendCap = transientPolicy ||
|
|
380
|
+
const initialSendCap = transientPolicy || resetPolicy
|
|
371
381
|
? transientSendCapFor(transientPolicy?.attempts,
|
|
372
382
|
(options.comboAttempt || compactPrepaid) ? 0 : sendBudgetState.sendsUsed)
|
|
373
383
|
: 1;
|
|
@@ -376,6 +386,11 @@ export async function prepareAdapterExchange(
|
|
|
376
386
|
: fetchWithResetRetry;
|
|
377
387
|
upstreamResponse = await fetchWithRetryPolicy(
|
|
378
388
|
recovery => {
|
|
389
|
+
// Unconfigured generic sends may omit the counting reporter. Capture policy
|
|
390
|
+
// independently so a later recovery still belongs to this request's start.
|
|
391
|
+
sendBudgetState.adapterSendBudget?.startRequest?.({
|
|
392
|
+
poolId: logCtx.spendPoolId ?? route.providerName, identityId: logCtx.accountLogLabel,
|
|
393
|
+
});
|
|
379
394
|
if (compactPrepaid && !compactPrepaidUsed) {
|
|
380
395
|
if (!compactPrepaid.use()) throw new SendBudgetExhaustedError(safeHostLabel(builtInitialRequest.url));
|
|
381
396
|
compactPrepaidUsed = true;
|
|
@@ -398,12 +413,18 @@ export async function prepareAdapterExchange(
|
|
|
398
413
|
abortSignal: upstream.signal,
|
|
399
414
|
label: safeHostLabel(builtInitialRequest.url),
|
|
400
415
|
claimAmbiguousResend: claimPreHeaderResend,
|
|
401
|
-
...(
|
|
416
|
+
...(sendBudgetState.adapterSendBudget?.spendEnforced || options.comboDispatchPermit || transientPolicy || resetPolicy || compactPrepaid
|
|
402
417
|
? {
|
|
403
418
|
// A pending compaction permit already booked this leg's first physical send.
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
419
|
+
...(transientPolicy || resetPolicy || compactPrepaid
|
|
420
|
+
? {
|
|
421
|
+
attempts: Math.min(initialSendCap,
|
|
422
|
+
remainingTransientSendBudget(initialSendCap) + (compactPrepaid ? 1 : 0)),
|
|
423
|
+
}
|
|
424
|
+
: {}),
|
|
425
|
+
// Keep the exact combo or compaction receipt on the helper callback. The reporter
|
|
426
|
+
// also settles the request and workflow counters for reset-only retries.
|
|
427
|
+
onSendsConsumed: transientSendReporter(compactPrepaid ?? options.comboDispatchPermit),
|
|
407
428
|
}
|
|
408
429
|
: {}),
|
|
409
430
|
},
|
|
@@ -437,7 +458,8 @@ export async function prepareAdapterExchange(
|
|
|
437
458
|
// blaming the provider makes the caller send the whole turn again -- the amplification this
|
|
438
459
|
// budget exists to stop. The passthrough path has answered 429 here since #4546.
|
|
439
460
|
if (err instanceof SendBudgetExhaustedError) {
|
|
440
|
-
return
|
|
461
|
+
return unboundPoolSpendRefusalResponse(logCtx)
|
|
462
|
+
?? formatErrorResponse(429, SEND_BUDGET_EXHAUSTED_CODE, err.message);
|
|
441
463
|
}
|
|
442
464
|
const msg = describeUpstreamConnectFailure(err, connectMs);
|
|
443
465
|
return formatErrorResponse(502, "upstream_error", msg);
|
|
@@ -541,6 +563,8 @@ export async function prepareAdapterExchange(
|
|
|
541
563
|
try {
|
|
542
564
|
if (transportState.activeAdapter.fetchResponse) {
|
|
543
565
|
transportState.noteRoutedAttemptSend(retryEstimate, recovery);
|
|
566
|
+
const producer = adapterDispatchBudget?.beginSpendProducer?.();
|
|
567
|
+
try {
|
|
544
568
|
return await withProviderRequestSlot(route.providerName, route.provider, route.modelId, upstream.signal, pacingSlot => {
|
|
545
569
|
// The dispatch boundary is HERE, not before the pacing wait: that wait can reject for
|
|
546
570
|
// an abort, a saturated queue, an expired slot or a removed provider, and none of
|
|
@@ -570,6 +594,7 @@ export async function prepareAdapterExchange(
|
|
|
570
594
|
}),
|
|
571
595
|
});
|
|
572
596
|
});
|
|
597
|
+
} finally { producer?.close(); }
|
|
573
598
|
}
|
|
574
599
|
// #2643 review: this leg used to call fetchWithHeaderTimeout directly, so an
|
|
575
600
|
// opted-in provider's transient-5xx policy applied to the initial send and to
|
|
@@ -581,7 +606,7 @@ export async function prepareAdapterExchange(
|
|
|
581
606
|
const refetchWithPolicy = (route.provider.adapter === "google" || refetchTransientPolicy)
|
|
582
607
|
? fetchWithTransientRetry
|
|
583
608
|
: fetchWithResetRetry;
|
|
584
|
-
const helperCountsSends = refetchTransientPolicy !== null || resetReplayPolicyFor(route.provider) !== null;
|
|
609
|
+
const helperCountsSends = sendBudgetState.adapterSendBudget?.spendEnforced === true || refetchTransientPolicy !== null || resetReplayPolicyFor(route.provider) !== null;
|
|
585
610
|
const prepaid = sendBudgetState.pendingHopPermit;
|
|
586
611
|
const configuredTotal = refetchTransientPolicy?.attempts;
|
|
587
612
|
const refetchCap = transientSendCapFor(configuredTotal,
|
|
@@ -601,6 +626,9 @@ export async function prepareAdapterExchange(
|
|
|
601
626
|
try {
|
|
602
627
|
return await refetchWithPolicy(
|
|
603
628
|
recoveryKind => {
|
|
629
|
+
sendBudgetState.adapterSendBudget?.startRequest?.({
|
|
630
|
+
poolId: logCtx.spendPoolId ?? route.providerName, identityId: logCtx.accountLogLabel,
|
|
631
|
+
});
|
|
604
632
|
if (refetchAllowance?.permit && !refetchAllowance.permit.use()) {
|
|
605
633
|
throw new SendBudgetExhaustedError(safeHostLabel(retryRequest.url));
|
|
606
634
|
}
|
|
@@ -629,7 +657,7 @@ export async function prepareAdapterExchange(
|
|
|
629
657
|
...(refetchAllowance
|
|
630
658
|
? {
|
|
631
659
|
attempts: refetchAllowance.attempts,
|
|
632
|
-
onSendsConsumed:
|
|
660
|
+
onSendsConsumed: transientSendReporter(refetchAllowance.permit),
|
|
633
661
|
}
|
|
634
662
|
: {}),
|
|
635
663
|
},
|
|
@@ -673,7 +701,8 @@ export async function prepareAdapterExchange(
|
|
|
673
701
|
// Same rule on the recovery leg: the ladder refused to send again, so the answer names
|
|
674
702
|
// this proxy rather than the provider it never reached.
|
|
675
703
|
if (err instanceof SendBudgetExhaustedError) {
|
|
676
|
-
return { failed:
|
|
704
|
+
return { failed: unboundPoolSpendRefusalResponse(logCtx)
|
|
705
|
+
?? formatErrorResponse(429, SEND_BUDGET_EXHAUSTED_CODE, err.message) };
|
|
677
706
|
}
|
|
678
707
|
const msg = describeUpstreamConnectFailure(err, connectMs);
|
|
679
708
|
return { failed: formatErrorResponse(502, "upstream_error", msg) };
|
|
@@ -1048,23 +1077,24 @@ export async function prepareAdapterExchange(
|
|
|
1048
1077
|
// before output, within the shared request and account rotation limits.
|
|
1049
1078
|
while (
|
|
1050
1079
|
(upstreamResponse.status === 429 || upstreamResponse.status === 403)
|
|
1080
|
+
&& anthropicInstance
|
|
1051
1081
|
&& transportState.anthropicPoolAccountId
|
|
1052
1082
|
) {
|
|
1053
|
-
const nextAccountId = await
|
|
1083
|
+
const nextAccountId = await rotateAnthropicAccountOnResponseForInstance(anthropicInstance, upstreamResponse, {
|
|
1054
1084
|
config, accountId: transportState.anthropicPoolAccountId, sessionKey: anthropicSessionKey,
|
|
1055
|
-
model: route.modelId, requestKey: transportState, decision: transportState.anthropicRouteDecision, signal: upstream.signal,
|
|
1085
|
+
model: route.modelId, requestKey: transportState, decision: transportState.anthropicRouteDecision, currentDecision: currentAnthropicRouteDecision, signal: upstream.signal,
|
|
1056
1086
|
canRetry: !sendBudgetExhausted() && transportState.anthropicPoolFailovers < ANTHROPIC_POOL_MAX_FAILOVERS_PER_REQUEST,
|
|
1057
1087
|
});
|
|
1058
1088
|
if (!nextAccountId) break;
|
|
1059
1089
|
try {
|
|
1060
|
-
const admitted = await commitResolvedOAuthSelection(await getAnthropicPoolAccessSnapshot(nextAccountId));
|
|
1090
|
+
const admitted = await commitResolvedOAuthSelection(await anthropicRoutingFor(anthropicInstance).getAnthropicPoolAccessSnapshot(nextAccountId));
|
|
1061
1091
|
if (!admitted) throw new Error("OAuth selection changed during recovery");
|
|
1062
1092
|
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ }
|
|
1063
1093
|
transportState.anthropicPoolAccountId = admitted.accountId;
|
|
1064
1094
|
transportState.anthropicPoolFailovers += 1;
|
|
1065
1095
|
route.provider = { ...route.provider, apiKey: admitted.accessToken };
|
|
1066
1096
|
invalidateSameTargetRequest();
|
|
1067
|
-
logCtx.provider = formatAnthropicProviderForLog(
|
|
1097
|
+
logCtx.provider = formatAnthropicProviderForLog(anthropicInstance, admitted.accountId, config);
|
|
1068
1098
|
transportState.activeAdapter = resolveSelectionAdapter(
|
|
1069
1099
|
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire, route.staticPolicy),
|
|
1070
1100
|
config.cacheRetention,
|
|
@@ -1,19 +1,33 @@
|
|
|
1
|
-
import { ANTIGRAVITY_VALIDATION_REQUIRED_PREFIX } from "../../adapters/google-errors";
|
|
1
|
+
import { ANTIGRAVITY_VALIDATION_REQUIRED_PREFIX, safeAntigravityHttpErrorMessage } from "../../adapters/google-errors";
|
|
2
2
|
import { readBoundedResponseBody } from "../../lib/bounded-body";
|
|
3
3
|
|
|
4
|
+
/** Match only the normalized Antigravity validation-refusal marker. */
|
|
5
|
+
export function hasAntigravityValidationRefusalMarker(text: string): boolean {
|
|
6
|
+
return text.startsWith(`${ANTIGRAVITY_VALIDATION_REQUIRED_PREFIX}: `);
|
|
7
|
+
}
|
|
8
|
+
|
|
4
9
|
/** Inspect only the Google adapter's normalized, already bounded 403 response. */
|
|
5
10
|
export async function isAntigravityValidationRefusal(response: Response, signal?: AbortSignal): Promise<boolean> {
|
|
11
|
+
return inspectValidationRefusal(response, signal, false);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Sidecar dispatch retains the raw Google envelope; normalize only a complete bounded body. */
|
|
15
|
+
export async function isAntigravityRawValidationRefusal(response: Response, signal?: AbortSignal): Promise<boolean> {
|
|
16
|
+
return inspectValidationRefusal(response, signal, true);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function inspectValidationRefusal(response: Response, signal: AbortSignal | undefined, raw: boolean): Promise<boolean> {
|
|
6
20
|
if (response.status !== 403 || signal?.aborted) return false;
|
|
7
21
|
try {
|
|
8
22
|
const body = await readBoundedResponseBody(response.clone(), {
|
|
9
|
-
maxBytes: 1024,
|
|
10
|
-
totalTimeoutMs: 1000,
|
|
11
|
-
firstByteTimeoutMs: 1000,
|
|
12
|
-
inactivityTimeoutMs: 1000,
|
|
23
|
+
maxBytes: raw ? 4096 : 1024,
|
|
24
|
+
totalTimeoutMs: raw ? 2000 : 1000,
|
|
25
|
+
firstByteTimeoutMs: raw ? 2000 : 1000,
|
|
26
|
+
inactivityTimeoutMs: raw ? 2000 : 1000,
|
|
13
27
|
signal,
|
|
14
28
|
});
|
|
15
29
|
return !signal?.aborted && body.displaySafe && !body.truncated && !body.timedOut && !body.oversized
|
|
16
|
-
&& body.text
|
|
30
|
+
&& hasAntigravityValidationRefusalMarker(raw ? safeAntigravityHttpErrorMessage(403, body.text) : body.text);
|
|
17
31
|
} catch {
|
|
18
32
|
return false;
|
|
19
33
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Restore the caller label while retaining JEV's forced effort and later child transitions. */
|
|
2
|
+
export function comboRequestedEffortLabel(
|
|
3
|
+
originalRequestedEffort: string | undefined,
|
|
4
|
+
childRequestedEffort: string | undefined,
|
|
5
|
+
forcedEffort?: string | null,
|
|
6
|
+
): string | undefined {
|
|
7
|
+
if (originalRequestedEffort === undefined) return childRequestedEffort;
|
|
8
|
+
const transitionIndex = childRequestedEffort?.indexOf("->") ?? -1;
|
|
9
|
+
const laterTransitions = transitionIndex >= 0 ? childRequestedEffort!.slice(transitionIndex) : "";
|
|
10
|
+
const forcedTransition = forcedEffort != null && forcedEffort !== originalRequestedEffort
|
|
11
|
+
? `->${forcedEffort}`
|
|
12
|
+
: "";
|
|
13
|
+
return `${originalRequestedEffort}${forcedTransition}${laterTransitions}`;
|
|
14
|
+
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { createPhysicalSendReporter } from "../../lib/request-execution-budget";
|
|
2
|
+
import { createInferenceSendBudget } from "../inference/context";
|
|
3
|
+
import { unboundPoolSpendRefusalResponse } from "../workflow-refusal";
|
|
1
4
|
import { capturePoolQuotaWriter } from "../../codex/account-store";
|
|
2
5
|
import { previewXaiOauthWireModel } from "./core-normalize";
|
|
3
6
|
import {
|
|
@@ -722,6 +725,7 @@ export async function handleResponsesCompact(
|
|
|
722
725
|
logCtx.provider = route.codexAccountNamespace
|
|
723
726
|
? `${route.providerName}-${route.codexAccountNamespace}`
|
|
724
727
|
: route.providerName;
|
|
728
|
+
logCtx.spendPoolId = route.providerName;
|
|
725
729
|
logCtx.providerAdapter = route.provider.adapter;
|
|
726
730
|
// #4940, and the same refusal the ordinary Responses path makes in request-prepare.ts. Compact has
|
|
727
731
|
// to repeat it rather than inherit it: the native branch below dispatches straight to
|
|
@@ -804,7 +808,7 @@ export async function handleResponsesCompact(
|
|
|
804
808
|
// failure that hands off, continues here. The routed turn used to call handleResponses with
|
|
805
809
|
// no budget at all, so `handleResponsesInner` minted a fresh four after the native attempt
|
|
806
810
|
// had already spent some of the first one.
|
|
807
|
-
const sendBudget: RequestExecutionBudget = options.sendBudget ??
|
|
811
|
+
const sendBudget: RequestExecutionBudget = options.sendBudget ?? createInferenceSendBudget(req, logCtx);
|
|
808
812
|
// A manual override onto another backend must not mint ciphertext the conversation model cannot replay.
|
|
809
813
|
const manualOverrideCrossesProvider = options.compactionRoutingOverride
|
|
810
814
|
? !compactionRoutingKeepsProviderIdentity(config, options.compactionRoutingOverride, route)
|
|
@@ -1022,12 +1026,21 @@ export async function handleResponsesCompact(
|
|
|
1022
1026
|
// `kind === "pool"` and a main-pool credential left it false -- so 401 then 429 really did
|
|
1023
1027
|
// reach five. The single sends spend the base allowance first and then the one shared
|
|
1024
1028
|
// final-recovery reserve, which is the same rule the Responses path follows.
|
|
1025
|
-
const sendSingleCompactAttempt = (
|
|
1029
|
+
const sendSingleCompactAttempt = async (
|
|
1026
1030
|
doFetch: () => Promise<Response>,
|
|
1027
1031
|
): Promise<Response> => {
|
|
1028
1032
|
if (sendBudget.remainingBaseSends(TRANSIENT_RETRY_MAX_ATTEMPTS) > 0) {
|
|
1029
|
-
sendBudget.
|
|
1030
|
-
|
|
1033
|
+
const report = createPhysicalSendReporter(sendBudget, () => ({ poolId: logCtx.spendPoolId ?? route.providerName,
|
|
1034
|
+
identityId: logCtx.accountLogLabel }));
|
|
1035
|
+
let started = false;
|
|
1036
|
+
try {
|
|
1037
|
+
if (report.beforeSend?.() === false) throw new SendBudgetExhaustedError(safeHostLabel(compactUrl));
|
|
1038
|
+
started = true;
|
|
1039
|
+
return await doFetch();
|
|
1040
|
+
} finally {
|
|
1041
|
+
try { if (started) report(1); }
|
|
1042
|
+
finally { report.close?.(); }
|
|
1043
|
+
}
|
|
1031
1044
|
}
|
|
1032
1045
|
const decision = sendBudget.reserveDispatch({
|
|
1033
1046
|
sendClass: "auth-recovery",
|
|
@@ -1076,7 +1089,8 @@ export async function handleResponsesCompact(
|
|
|
1076
1089
|
// Draws the shared remainder instead of a fresh three. Compact is a native endpoint
|
|
1077
1090
|
// of the same logical turn, so its sends belong to the same cap.
|
|
1078
1091
|
attempts: sendBudget.remainingBaseSends(TRANSIENT_RETRY_MAX_ATTEMPTS),
|
|
1079
|
-
onSendsConsumed: (
|
|
1092
|
+
onSendsConsumed: createPhysicalSendReporter(sendBudget, () => ({ poolId: logCtx.spendPoolId ?? route.providerName,
|
|
1093
|
+
identityId: logCtx.accountLogLabel })),
|
|
1080
1094
|
});
|
|
1081
1095
|
};
|
|
1082
1096
|
|
|
@@ -1084,9 +1098,12 @@ export async function handleResponsesCompact(
|
|
|
1084
1098
|
// actually happens, so every recorder call names the context that produced it.
|
|
1085
1099
|
let outcomeCtx = authCtx;
|
|
1086
1100
|
const localDispatchRefusal = (error: unknown): Response | undefined => {
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1089
|
-
|
|
1101
|
+
const cause = unwrapUpstreamRetryEvidenceError(error);
|
|
1102
|
+
const response = cause instanceof SendBudgetExhaustedError
|
|
1103
|
+
? unboundPoolSpendRefusalResponse(logCtx) ?? formatErrorResponse(429, "request_send_budget_exhausted", cause.message)
|
|
1104
|
+
: mapCodexAuthContextErrorToResponse(cause, {
|
|
1105
|
+
now: Date.now(), accountSelector: route.codexAccountNamespace,
|
|
1106
|
+
});
|
|
1090
1107
|
if (response) {
|
|
1091
1108
|
releaseUpstreamHostAdmission(compactHostAdmissionLease);
|
|
1092
1109
|
compactHostAdmissionLease = null;
|
|
@@ -47,7 +47,7 @@ function settlePhysicalSends(log: RequestLogContext, budget: RequestExecutionBud
|
|
|
47
47
|
// An external report may settle a prepaid booking without changing used. Its explicit
|
|
48
48
|
// receipt outranks the numeric delta, or the same source send would be charged twice.
|
|
49
49
|
const unreported = Math.max(0, sent - Math.max(reported, Math.max(0, budget.used - beforeUsed)));
|
|
50
|
-
if (unreported > 0) budget.used += unreported;
|
|
50
|
+
if (unreported > 0 && !budget.spendEnforced) budget.used += unreported;
|
|
51
51
|
return sent;
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -18,7 +18,7 @@ import type { OcxConfig } from "../../types";
|
|
|
18
18
|
import type { RouteResult } from "../../router";
|
|
19
19
|
import type { DataPlaneAdmission } from "../auth-cors";
|
|
20
20
|
import type { AdmissionLease } from "../../lib/admission";
|
|
21
|
-
import type { RequestExecutionBudget } from "../../lib/request-execution-budget";
|
|
21
|
+
import type { RequestExecutionBudget, SingleUseDispatchPermit } from "../../lib/request-execution-budget";
|
|
22
22
|
import type { TransientSendBudget } from "../../lib/upstream-retry";
|
|
23
23
|
import type { ResponsesTerminalStatus } from "../../bridge";
|
|
24
24
|
import type { NativeChatFinishLog } from "../chat-native";
|
|
@@ -62,6 +62,7 @@ export interface NativeComboChildRun {
|
|
|
62
62
|
childLog: RequestLogContext;
|
|
63
63
|
attemptHandle: InferenceAttempt;
|
|
64
64
|
sendBudget: RequestExecutionBudget;
|
|
65
|
+
comboDispatchPermit?: SingleUseDispatchPermit;
|
|
65
66
|
turnAdmissionLease?: AdmissionLease;
|
|
66
67
|
onFirstOutput: () => void;
|
|
67
68
|
/** Reports to the combo's child callbacks; it never writes the parent's final row itself. */
|
|
@@ -272,6 +273,7 @@ export interface ComboChildCallbacks {
|
|
|
272
273
|
export async function dispatchNativeComboChild(input: {
|
|
273
274
|
source: ComboProtocolSource;
|
|
274
275
|
plan: NativeComboChildPlan;
|
|
276
|
+
comboDispatchPermit?: SingleUseDispatchPermit;
|
|
275
277
|
logCtx: RequestLogContext;
|
|
276
278
|
childLog: RequestLogContext;
|
|
277
279
|
attempt: PersistedUsageAttempt;
|
|
@@ -317,6 +319,7 @@ export async function dispatchNativeComboChild(input: {
|
|
|
317
319
|
childLog,
|
|
318
320
|
attemptHandle,
|
|
319
321
|
sendBudget: plan.sendBudget,
|
|
322
|
+
comboDispatchPermit: input.comboDispatchPermit,
|
|
320
323
|
...(input.turnAdmissionLease ? { turnAdmissionLease: input.turnAdmissionLease } : {}),
|
|
321
324
|
onFirstOutput: () => {
|
|
322
325
|
outputSeen = true;
|
|
@@ -52,11 +52,10 @@ import { routeConcreteModel, comboRouteDecisionTrace } from "../../router";
|
|
|
52
52
|
import { memoryModelRouteReason } from "./memory-models";
|
|
53
53
|
import { poolAccountProviderLabel } from "../../providers/label";
|
|
54
54
|
import { getAccountSet } from "../../oauth/store";
|
|
55
|
+
import { configuredAnthropicInstance } from "../../providers/anthropic-instance";
|
|
55
56
|
import {
|
|
57
|
+
anthropicRoutingFor,
|
|
56
58
|
formatAnthropicProviderForLog,
|
|
57
|
-
getAnthropicAccountHealthSnapshot,
|
|
58
|
-
getAnthropicPoolRetryAfterSeconds,
|
|
59
|
-
getEligibleAnthropicAccounts,
|
|
60
59
|
} from "../../oauth/anthropic-routing";
|
|
61
60
|
import { codexAccountLogLabel } from "../../codex/account-label";
|
|
62
61
|
import { codexQuotaScopeForModel, getCodexQuotaHealthSnapshot } from "../../codex/routing";
|
|
@@ -106,6 +105,7 @@ import { mandatoryResponsesReasoningReplayUnavailable } from "./core-replay";
|
|
|
106
105
|
import { settleOperatorReplacement } from "../../lib/upstream-retry";
|
|
107
106
|
import { createComboProtocolLanes, dispatchNativeComboChild } from "./core-combo-native";
|
|
108
107
|
import { createJevModelInvoker } from "./jev-model-invoke";
|
|
108
|
+
import { comboRequestedEffortLabel } from "./combo-requested-effort";
|
|
109
109
|
import { clientWireOf } from "../inference/client-wire";
|
|
110
110
|
|
|
111
111
|
/**
|
|
@@ -117,10 +117,11 @@ export const COMBO_TARGET_BASE_SENDS = CODEX_TEXT_GUARDED_BUDGET_POLICY.baseSend
|
|
|
117
117
|
|
|
118
118
|
function cooledPoolAccountLabel(config: OcxConfig, providerName: string, modelId: string, label: string | undefined): string | undefined {
|
|
119
119
|
if (!label) return undefined;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
const instance = configuredAnthropicInstance(config, providerName);
|
|
121
|
+
if (instance && config.providers[providerName]?.authMode === "oauth") {
|
|
122
|
+
const matches = getAccountSet(instance)?.accounts.filter(account =>
|
|
123
|
+
formatAnthropicProviderForLog(instance, account.id) === label) ?? [];
|
|
124
|
+
return matches.length === 1 && anthropicRoutingFor(instance).getAnthropicAccountHealthSnapshot(matches[0]!.id) ? label : undefined;
|
|
124
125
|
}
|
|
125
126
|
const provider = config.providers[providerName];
|
|
126
127
|
if (!provider || !isCanonicalOpenAiForwardProvider(provider)) return undefined;
|
|
@@ -151,12 +152,13 @@ export function isAnthropicPoolLocalRefusal(
|
|
|
151
152
|
failedAccount: string | undefined,
|
|
152
153
|
now = Date.now(),
|
|
153
154
|
): boolean {
|
|
154
|
-
|
|
155
|
+
const instance = configuredAnthropicInstance(config, providerName);
|
|
156
|
+
return instance !== undefined
|
|
155
157
|
&& config.providers[providerName]?.authMode === "oauth"
|
|
156
158
|
&& status === 429
|
|
157
159
|
&& failedAccount === undefined
|
|
158
|
-
&& getEligibleAnthropicAccounts(now).length === 0
|
|
159
|
-
&& getAnthropicPoolRetryAfterSeconds(now) !== null;
|
|
160
|
+
&& anthropicRoutingFor(instance).getEligibleAnthropicAccounts(now).length === 0
|
|
161
|
+
&& anthropicRoutingFor(instance).getAnthropicPoolRetryAfterSeconds(now) !== null;
|
|
160
162
|
}
|
|
161
163
|
|
|
162
164
|
/**
|
|
@@ -656,13 +658,9 @@ export async function executeComboResponses(
|
|
|
656
658
|
&& isDeclaredReasoningEffort(originalRequestedEffortValue)
|
|
657
659
|
? originalRequestedEffortValue
|
|
658
660
|
: undefined;
|
|
659
|
-
const restoreOriginalRequestedEffort = (childLog: RequestLogContext): void => {
|
|
661
|
+
const restoreOriginalRequestedEffort = (childLog: RequestLogContext, forcedEffort?: string | null): void => {
|
|
660
662
|
if (originalRequestedEffort === undefined) return;
|
|
661
|
-
|
|
662
|
-
const transitionIndex = normalizedRequestedEffort?.indexOf("->") ?? -1;
|
|
663
|
-
childLog.requestedEffort = transitionIndex >= 0
|
|
664
|
-
? `${originalRequestedEffort}${normalizedRequestedEffort!.slice(transitionIndex)}`
|
|
665
|
-
: originalRequestedEffort;
|
|
663
|
+
childLog.requestedEffort = comboRequestedEffortLabel(originalRequestedEffort, childLog.requestedEffort, forcedEffort);
|
|
666
664
|
recordAttemptRequestedEffort(childLog);
|
|
667
665
|
};
|
|
668
666
|
|
|
@@ -716,6 +714,9 @@ export async function executeComboResponses(
|
|
|
716
714
|
while (pick) {
|
|
717
715
|
if (options.abortSignal?.aborted) return clientCancelledResponse();
|
|
718
716
|
const firstComboTarget = comboTargetsDispatched === 0;
|
|
717
|
+
const targetRoute = routeConcreteModel(config, `${pick.target.provider}/${pick.target.model}`);
|
|
718
|
+
// The inherited spend tracker observes this parent log, before the child has its own label.
|
|
719
|
+
logCtx.spendPoolId = targetRoute.providerName;
|
|
719
720
|
// The first target seeds the ledger's target identity and charges nothing; every later one
|
|
720
721
|
// is a real transition, refused once the declared hops, the alternate-target ledger or the
|
|
721
722
|
// request total are spent. `countedExternally` is required: the child charges its own
|
|
@@ -747,7 +748,6 @@ export async function executeComboResponses(
|
|
|
747
748
|
...(logCtx.conversationId ? { conversationId: logCtx.conversationId } : {}),
|
|
748
749
|
...(logCtx.surface ? { surface: logCtx.surface } : {}),
|
|
749
750
|
};
|
|
750
|
-
const targetRoute = routeConcreteModel(config, `${pick.target.provider}/${pick.target.model}`);
|
|
751
751
|
const targetReasoningEfforts = supportedLadderFor({
|
|
752
752
|
provider: targetRoute.provider,
|
|
753
753
|
modelId: targetRoute.modelId,
|
|
@@ -864,6 +864,7 @@ export async function executeComboResponses(
|
|
|
864
864
|
response = nativeChild ? await dispatchNativeComboChild({
|
|
865
865
|
source: options.protocolSource!,
|
|
866
866
|
plan: nativeChild,
|
|
867
|
+
comboDispatchPermit: hopDecision?.allowed ? hopDecision.permit : undefined,
|
|
867
868
|
logCtx,
|
|
868
869
|
childLog,
|
|
869
870
|
attempt,
|
|
@@ -889,6 +890,7 @@ export async function executeComboResponses(
|
|
|
889
890
|
// parent arrived with.
|
|
890
891
|
sendBudget: targetSendBudget,
|
|
891
892
|
comboAttempt: true,
|
|
893
|
+
comboDispatchPermit: hopDecision?.allowed ? hopDecision.permit : undefined,
|
|
892
894
|
comboReplaySnapshot,
|
|
893
895
|
deferCodexResetDerivedCooldown,
|
|
894
896
|
// Attempt-relative TTFT is recorded HERE (not via childLog.firstOutputMs — a later
|
|
@@ -907,7 +909,8 @@ export async function executeComboResponses(
|
|
|
907
909
|
onNativePassthroughCancel: callbackGate.onCancel,
|
|
908
910
|
onResponseComplete: callbackGate.onResponseComplete,
|
|
909
911
|
});
|
|
910
|
-
|
|
912
|
+
// Native Chat rebuilds its own body and does not apply the initial JEV effort.
|
|
913
|
+
restoreOriginalRequestedEffort(childLog, nativeChild ? undefined : initialJevDecision?.effort);
|
|
911
914
|
} catch (error) {
|
|
912
915
|
callbackGate.discard();
|
|
913
916
|
if (options.abortSignal?.aborted) {
|
|
@@ -210,6 +210,7 @@ export async function applyFinalRouteRequestNormalization(args: {
|
|
|
210
210
|
if (preserveAnthropicResponseModel) parsed._responseModelId = responseModelId;
|
|
211
211
|
logCtx.model = virtualModel?.selectedModelId ?? route.modelId;
|
|
212
212
|
logCtx.provider = route.providerName;
|
|
213
|
+
logCtx.spendPoolId = route.providerName;
|
|
213
214
|
logCtx.providerAdapter = route.provider.adapter;
|
|
214
215
|
logCtx.routeDecision = route.routeDecision;
|
|
215
216
|
logCtx.policyEligibility = route.policyEligibility;
|
|
@@ -71,7 +71,7 @@ export interface HandleResponsesOptions {
|
|
|
71
71
|
compactionRecoveryKind?: "compaction-v1" | "compaction-v2";
|
|
72
72
|
onCompactionRecoveryRoute?: (route: RouteResult) => void;
|
|
73
73
|
onCompactionRecoveryAdapterEvent?: (event: AdapterEvent) => void;
|
|
74
|
-
/** Physical-send reports already delivered to the shared
|
|
74
|
+
/** Physical-send reports already delivered to the shared ledger, including exact booking settlement. */
|
|
75
75
|
onCompactionRecoverySendsReported?: (count: number) => void;
|
|
76
76
|
/** Private holder for the Kiro serving-account lease. */
|
|
77
77
|
accountLoad?: { lease: AccountLease | null; cancelled: boolean };
|
|
@@ -158,6 +158,8 @@ export interface HandleResponsesOptions {
|
|
|
158
158
|
callerDirectAuth?: CallerDirectAuth | null;
|
|
159
159
|
/** Internal recursion guard; callers outside this module must not set it. */
|
|
160
160
|
comboAttempt?: boolean;
|
|
161
|
+
/** Exact externally booked combo hop, used for its child's spend preflight and send reports. */
|
|
162
|
+
comboDispatchPermit?: SingleUseDispatchPermit;
|
|
161
163
|
/** Internal handoff: this combo was selected by shadow-call interception. */
|
|
162
164
|
shadowCallIntercepted?: boolean;
|
|
163
165
|
/** Internal handoff: the memory phase this turn belongs to, so combo children keep its routing. */
|
|
@@ -37,6 +37,11 @@ export function adapterNeedsForcedContinuation(name: string): boolean {
|
|
|
37
37
|
return name === "kiro" || name === "cursor";
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/** Adapters opting into restricted store:false replay for pending client function calls. */
|
|
41
|
+
export function adapterNeedsToolCallContinuation(name: string): boolean {
|
|
42
|
+
return name === "qoder";
|
|
43
|
+
}
|
|
44
|
+
|
|
40
45
|
|
|
41
46
|
export type ContinuationOwnerRead =
|
|
42
47
|
| { kind: "missing" }
|
|
@@ -364,6 +364,7 @@ export async function deliverPassthroughResponse(
|
|
|
364
364
|
| "subagentFallbackAccountId"
|
|
365
365
|
| "clientRequestedStream"
|
|
366
366
|
| "translatorBudget"
|
|
367
|
+
| "inboundWire"
|
|
367
368
|
>,
|
|
368
369
|
transportState: Pick<ResponsesTransport, "requestBindings">,
|
|
369
370
|
sidecarState: Pick<ResponsesSidecarAuth, "openAiSidecar">,
|
|
@@ -428,7 +429,8 @@ export async function deliverPassthroughResponse(
|
|
|
428
429
|
normalizeFunctionCompletionJson,
|
|
429
430
|
} = nativeExchange;
|
|
430
431
|
const { commitReasoningReplayServingRoute, recordTerminalOutcomes } = responseEffects;
|
|
431
|
-
const { parsed, route, subagentQuotaFailureModel, clientRequestedStream, translatorBudget } = requestState;
|
|
432
|
+
const { parsed, route, subagentQuotaFailureModel, clientRequestedStream, translatorBudget, inboundWire } = requestState;
|
|
433
|
+
const enforceDeclaredToolNames = inboundWire !== "chat" && inboundWire !== "anthropic";
|
|
432
434
|
const { openAiSidecar } = sidecarState;
|
|
433
435
|
const { requestBindings } = transportState;
|
|
434
436
|
|
|
@@ -637,6 +639,7 @@ export async function deliverPassthroughResponse(
|
|
|
637
639
|
route.provider.webSearchBridge?.backend,
|
|
638
640
|
config,
|
|
639
641
|
openAiSidecar,
|
|
642
|
+
route.providerName,
|
|
640
643
|
);
|
|
641
644
|
const webSearchBridgePlan = planPassthroughWebSearchBridge(parsed, route.provider, {
|
|
642
645
|
providerName: route.providerName,
|
|
@@ -836,6 +839,7 @@ export async function deliverPassthroughResponse(
|
|
|
836
839
|
providerExecutedCallTypes,
|
|
837
840
|
declaredBareWireToolNames,
|
|
838
841
|
recoverableBareCustomWireToolNames,
|
|
842
|
+
enforceDeclaredToolNames,
|
|
839
843
|
)
|
|
840
844
|
: undefined,
|
|
841
845
|
grokUpstreamEchoEnabled
|
|
@@ -1293,7 +1297,7 @@ export async function deliverPassthroughResponse(
|
|
|
1293
1297
|
if (grokUpstreamEchoEnabled) {
|
|
1294
1298
|
clientJson = stripGrokUpstreamEnvelopeEchoFromResponsesJson(clientJson);
|
|
1295
1299
|
}
|
|
1296
|
-
// #1700: same
|
|
1300
|
+
// #1700: same inbound-wire refusal policy as the SSE relay above. Both the plain JSON answer and
|
|
1297
1301
|
// the reframed-SSE branch below are built from this body, so one check covers them. This
|
|
1298
1302
|
// runs BEFORE the continuation cache write below: a refused turn must not become state a
|
|
1299
1303
|
// later `previous_response_id` replay can expand from.
|
|
@@ -1312,7 +1316,7 @@ export async function deliverPassthroughResponse(
|
|
|
1312
1316
|
return undefined;
|
|
1313
1317
|
}
|
|
1314
1318
|
})();
|
|
1315
|
-
if (undeclared !== undefined) {
|
|
1319
|
+
if (enforceDeclaredToolNames && undeclared !== undefined) {
|
|
1316
1320
|
return formatErrorResponse(502, "upstream_error", undeclaredToolCallMessage(undeclared));
|
|
1317
1321
|
}
|
|
1318
1322
|
clientJson = normalizeDefaultNamespaceInJson(
|