@bitkyc08/opencodex 2.80.0 → 2.82.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/bin/ocx.mjs +166 -73
- package/gui/dist/assets/App-Ds_qL7o8.js +54 -0
- package/gui/dist/assets/Tray-BqHF5Trd.js +1 -0
- package/gui/dist/assets/{index-Brk6F3fn.css → index-BAbcj51J.css} +1 -1
- package/gui/dist/assets/index-D3FBCNiP.js +94 -0
- package/gui/dist/assets/quota-summary-DTBC8n7-.js +1 -0
- package/gui/dist/index.html +2 -2
- 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 +8 -3
- 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/devin/live-models.ts +2 -0
- package/src/adapters/kiro-retry.ts +9 -4
- package/src/adapters/openai-chat/messages.ts +7 -6
- package/src/adapters/openai-responses/compaction-search-history.ts +109 -0
- package/src/adapters/openai-responses/passthrough.ts +9 -4
- package/src/adapters/openai-responses/tool-output-recovery.ts +20 -0
- 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/agents-inject.ts +14 -3
- package/src/claude/context-windows.ts +19 -17
- package/src/claude/desktop-3p.ts +88 -12
- package/src/claude/desktop-profile.ts +2 -1
- package/src/claude/gateway-cache.ts +18 -1
- package/src/claude/inbound-cache-stabilize.ts +4 -9
- package/src/claude/inbound.ts +78 -3
- package/src/claude/intercept/cli-catalog.ts +3 -0
- package/src/claude/intercept/model-bindings.ts +1 -0
- package/src/claude/intercept/picker-ca-persistence.ts +7 -6
- package/src/claude/intercept/picker-listener.ts +71 -4
- package/src/claude/intercept/picker-models.ts +14 -6
- package/src/claude/intercept/settings.ts +3 -0
- package/src/claude/long-context.ts +66 -0
- package/src/claude/model-info.ts +19 -16
- package/src/claude/outbound.ts +94 -9
- package/src/claude/subagent-model.ts +40 -11
- 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/agent.ts +9 -5
- package/src/cli/aside-profile-recovery.ts +33 -0
- package/src/cli/capabilities-accounts.ts +2 -2
- package/src/cli/capabilities-agents-routing.ts +24 -0
- package/src/cli/capabilities-base.ts +5 -5
- package/src/cli/capabilities-integrations.ts +18 -2
- package/src/cli/cli-path-diagnostics.ts +132 -0
- package/src/cli/codex-shim-autorestore.ts +3 -0
- package/src/cli/desktop-runtime-guidance.ts +65 -0
- package/src/cli/dispatch.ts +15 -1
- package/src/cli/doctor.ts +16 -3
- package/src/cli/help.ts +4 -1
- package/src/cli/index.ts +25 -14
- package/src/cli/integration-aside-sync.ts +2 -1
- package/src/cli/integration-plan-dto.ts +1 -0
- package/src/cli/integration-preview.ts +8 -1
- package/src/cli/integrations.ts +48 -6
- 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/cli/resolve.ts +25 -2
- package/src/cli/root.ts +4 -2
- package/src/cli/status.ts +64 -12
- package/src/cli/update-restart-child.ts +4 -2
- package/src/cli/update-restart-home.ts +28 -10
- package/src/cli/update-restart-service-record.ts +116 -0
- package/src/cli/update-restart-supervision.ts +96 -0
- package/src/cli/update-restart.ts +59 -13
- package/src/cli/v2.ts +27 -20
- package/src/cli/version-skew-notice.ts +77 -0
- 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/clients/lazycodex.ts +1 -1
- package/src/clients/omo-role-models.ts +32 -7
- package/src/codex/auth-api/account-list.ts +11 -3
- package/src/codex/auth-api/account-selection.ts +107 -0
- 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 +11 -49
- package/src/codex/auth-context.ts +20 -1
- package/src/codex/autostart-health.ts +12 -3
- package/src/codex/catalog/auto-review.ts +13 -6
- package/src/codex/catalog/build-entries.ts +33 -7
- package/src/codex/catalog/bundled.ts +155 -26
- package/src/codex/catalog/combo-member.ts +3 -2
- package/src/codex/catalog/control-plane.ts +45 -0
- package/src/codex/catalog/effort.ts +37 -6
- package/src/codex/catalog/gather-capture.ts +10 -3
- package/src/codex/catalog/model-hints.ts +15 -8
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/operator-tiers.ts +38 -0
- package/src/codex/catalog/parsing.ts +2 -1
- package/src/codex/catalog/provider-models.ts +58 -44
- package/src/codex/catalog/retained-sync.ts +7 -5
- package/src/codex/catalog/routed-gather.ts +15 -1
- package/src/codex/catalog-auto-refresh-sources.ts +3 -4
- package/src/codex/catalog-auto-refresh.ts +19 -0
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-home-owner.ts +3 -0
- package/src/codex/config-write-lock.ts +333 -0
- package/src/codex/control-plane-models.ts +6 -0
- package/src/codex/convergence.ts +2 -1
- package/src/codex/features.ts +257 -200
- package/src/codex/inject/config-write-section.ts +34 -0
- package/src/codex/inject/multi-agent-v2.ts +14 -6
- package/src/codex/inject/remove.ts +36 -11
- package/src/codex/inject/restore.ts +85 -20
- package/src/codex/inject-coordination.ts +15 -6
- package/src/codex/inject.ts +62 -20
- package/src/codex/journal.ts +62 -17
- package/src/codex/main-account-cache.ts +43 -0
- package/src/codex/paths.ts +2 -0
- package/src/codex/prepared-home.ts +18 -0
- package/src/codex/prompt-journal.ts +25 -12
- package/src/codex/prompt-layers.ts +49 -26
- package/src/codex/prompt-lock-claim.ts +134 -0
- package/src/codex/prompt-lock-evidence.ts +26 -0
- package/src/codex/prompt-lock-io.ts +16 -0
- package/src/codex/prompt-lock-owner.ts +130 -0
- package/src/codex/prompt-lock.ts +84 -53
- package/src/codex/routing/failure-window.ts +146 -0
- package/src/codex/routing/selection.ts +4 -1
- package/src/codex/routing.ts +13 -17
- package/src/codex/runtime.ts +106 -18
- package/src/codex/shim.ts +45 -19
- package/src/combos/jev-decision-contract.ts +25 -2
- package/src/combos/jev-service-exchange.ts +281 -0
- package/src/combos/jev.ts +34 -219
- package/src/combos/types.ts +1 -1
- package/src/config/atomic-write.ts +1 -1
- package/src/config/codex-ws-reuse-setting.ts +10 -0
- package/src/config/diagnostics.ts +27 -16
- package/src/config/live-reconcile.ts +17 -0
- package/src/config/load-degrade.ts +42 -0
- package/src/config/rebase-provenance.ts +11 -0
- package/src/config/schema/anthropic-account-pool.ts +143 -0
- package/src/config/schema/config-schema.ts +11 -1
- package/src/config/schema/leaf-validators.ts +4 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +566 -298
- package/src/generated/model-metadata.ts +7 -6
- package/src/grok/inject.ts +50 -4
- package/src/grok/sync.ts +2 -1
- package/src/images/loop.ts +30 -11
- package/src/integrations/aside-profiles.ts +10 -3
- package/src/integrations/catalog-refresh.ts +19 -3
- 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 +17 -5
- package/src/lib/bun-path-runtime.d.mts +32 -0
- package/src/lib/bun-path-runtime.mjs +95 -0
- package/src/lib/bun-runtime-preflight.ts +140 -0
- package/src/lib/desktop-cli-handoff.d.mts +20 -0
- package/src/lib/desktop-cli-handoff.mjs +92 -0
- package/src/lib/desktop-cli-record.d.mts +29 -0
- package/src/lib/desktop-cli-record.mjs +102 -0
- package/src/lib/lab-live-route-production.ts +9 -1
- package/src/lib/request-execution-budget.ts +244 -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/synced-state-location.ts +46 -6
- package/src/lib/test-home-guard.ts +87 -1
- package/src/lib/translator-budget.ts +17 -4
- package/src/lib/upstream-retry.ts +17 -5
- package/src/lib/windows-atomic-replace.ts +38 -16
- package/src/lib/windows-elevation.ts +6 -0
- package/src/lib/windows-secret-acl.ts +92 -25
- package/src/lib/winsw.ts +2 -3
- 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 +90 -18
- 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/chatgpt.ts +113 -41
- package/src/oauth/generic-account-failover.ts +3 -1
- package/src/oauth/health.ts +8 -6
- package/src/oauth/index.ts +96 -175
- 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 +68 -10
- 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/azure-model-metadata.ts +115 -0
- package/src/providers/derive.ts +23 -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-key.ts +127 -17
- package/src/providers/quota/vendor-probes-oauth.ts +46 -14
- package/src/providers/quota.ts +23 -33
- 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/reasoning-envelope.ts +29 -1
- package/src/responses/reasoning-replay-cache.ts +17 -1
- package/src/responses/spill-store.ts +20 -3
- package/src/responses/state/metrics.ts +95 -0
- package/src/responses/state/snapshot-codec.ts +6 -0
- package/src/responses/state/spill-failure.ts +12 -0
- package/src/responses/state/spill-queue.ts +13 -5
- package/src/responses/state/unforced-store-false.ts +55 -0
- package/src/responses/state.ts +68 -76
- package/src/responses/task-input.ts +17 -0
- 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/background-lifecycle.ts +27 -0
- package/src/server/chat-completions.ts +7 -1
- package/src/server/chat-native.ts +64 -28
- package/src/server/claude-messages.ts +59 -8
- package/src/server/gui-static.ts +38 -4
- package/src/server/inbound-body-admission.ts +1 -0
- package/src/server/index/claude-intercept-lifecycle.ts +3 -1
- package/src/server/index/serve-options.ts +10 -2
- package/src/server/index/spend-ledger-lifecycle.ts +12 -5
- package/src/server/index/startup-warnings.ts +19 -0
- package/src/server/index.ts +5 -2
- package/src/server/inference/context.ts +1 -1
- package/src/server/lifecycle.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +123 -38
- 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/codex-agent-role-routes.ts +4 -2
- package/src/server/management/codex-prompt-routes.ts +1 -0
- package/src/server/management/codex-role-auto-assign.ts +13 -2
- package/src/server/management/config-routes.ts +85 -14
- package/src/server/management/context.ts +2 -2
- 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/shared.ts +3 -3
- 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 +91 -45
- package/src/server/messages-native-selector.ts +39 -0
- package/src/server/messages-native.ts +133 -38
- package/src/server/messages-response-headers.ts +14 -0
- package/src/server/request-log-conversation.ts +48 -5
- package/src/server/request-log.ts +36 -2
- package/src/server/responses/adapter-continuation.ts +31 -19
- package/src/server/responses/adapter-dispatch.ts +75 -24
- package/src/server/responses/antigravity-validation-refusal.ts +20 -6
- package/src/server/responses/codex-ws-exchange.ts +17 -2
- package/src/server/responses/codex-ws-pool.ts +100 -13
- package/src/server/responses/codex-ws-wire.ts +9 -0
- package/src/server/responses/combo-requested-effort.ts +3 -1
- package/src/server/responses/compact.ts +25 -8
- package/src/server/responses/compaction-recovery.ts +1 -1
- package/src/server/responses/core-codex-account.ts +22 -1
- package/src/server/responses/core-combo-native.ts +24 -4
- package/src/server/responses/core-combo.ts +38 -25
- package/src/server/responses/core-lifetime.ts +3 -36
- package/src/server/responses/core-normalize.ts +1 -0
- package/src/server/responses/core-options.ts +8 -1
- package/src/server/responses/core-replay.ts +78 -0
- package/src/server/responses/core.ts +3 -3
- package/src/server/responses/fetch-helpers.ts +26 -3
- package/src/server/responses/passthrough-delivery.ts +30 -4
- package/src/server/responses/passthrough-dispatch.ts +39 -9
- package/src/server/responses/request-prepare.ts +41 -5
- package/src/server/responses/request-send-budget.ts +150 -50
- package/src/server/responses/request-spend.ts +325 -33
- package/src/server/responses/request-transport.ts +132 -52
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +13 -3
- package/src/server/responses/sidecar-execution.ts +113 -22
- package/src/server/responses/sidecar-send-budget.ts +58 -0
- package/src/server/responses/ws-upstream.ts +19 -6
- package/src/server/responses-undeclared-tool-guard.ts +5 -1
- package/src/server/startup-health-cache.ts +2 -1
- package/src/server/workflow-refusal.ts +33 -0
- package/src/service/cli.ts +20 -4
- package/src/service/desktop-command-guard.ts +32 -0
- package/src/service/desktop-startup.ts +66 -40
- package/src/service/desktop-supervision.d.mts +26 -0
- package/src/service/desktop-supervision.mjs +140 -0
- package/src/service/managing-cli.ts +1 -0
- package/src/service/orchestration.ts +46 -20
- package/src/service/ownership-mutation-lease.d.mts +1 -1
- package/src/service/ownership-mutation-lease.mjs +5 -7
- package/src/service/repair.ts +25 -9
- package/src/service/state.ts +4 -3
- package/src/service/systemd.ts +17 -8
- package/src/service/windows-ops.ts +61 -22
- package/src/sidecar/anthropic-binding.ts +79 -0
- package/src/sidecar/auth.ts +76 -2
- package/src/storage/policy-job.ts +5 -1
- package/src/storage/policy-worker.ts +3 -1
- package/src/storage/policy.ts +9 -0
- package/src/types/anthropic-account-pool.ts +33 -0
- package/src/types/config.ts +33 -28
- package/src/types/provider.ts +5 -0
- package/src/types/request.ts +5 -0
- package/src/types/tools.ts +7 -0
- package/src/types/wire.ts +1 -0
- package/src/types.ts +4 -0
- package/src/update/index.ts +57 -9
- package/src/update/restart-ownership.ts +10 -3
- package/src/update/runtime-ownership.d.mts +6 -0
- package/src/update/runtime-ownership.mjs +23 -7
- 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/index.ts +7 -3
- package/src/web-search/loop.ts +32 -7
- package/src/web-search/passthrough-bridge.ts +4 -1
- package/src/web-search/sidecar-providers.ts +4 -3
- package/gui/dist/assets/App-MbDVfUeC.js +0 -54
- package/gui/dist/assets/Tray-CzkxK82A.js +0 -1
- package/gui/dist/assets/index-UJt6npXk.js +0 -94
- package/gui/dist/assets/quota-summary-DU3sD6Z-.js +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { rotateAnthropicAccountOnResponseForInstance } from "../../oauth/anthropic-account-refusal";
|
|
2
2
|
import type { ResponsesRequestContext } from "./core-options";
|
|
3
3
|
import type { PreparedResponsesRequest } from "./request-prepare";
|
|
4
4
|
import type { ResponsesTransport } from "./request-transport";
|
|
@@ -35,7 +35,7 @@ import { resolveWireProtocolOverride } from "../adapter-resolve";
|
|
|
35
35
|
import { bindRouteReasoningReplayScope } from "./core-replay";
|
|
36
36
|
import {
|
|
37
37
|
ANTHROPIC_POOL_MAX_FAILOVERS_PER_REQUEST,
|
|
38
|
-
|
|
38
|
+
anthropicRoutingFor,
|
|
39
39
|
formatAnthropicProviderForLog,
|
|
40
40
|
} from "../../oauth/anthropic-routing";
|
|
41
41
|
import {
|
|
@@ -83,6 +83,8 @@ export function createAdapterContinuations(
|
|
|
83
83
|
| "oauthDispatch"
|
|
84
84
|
| "invalidateSameTargetRequest"
|
|
85
85
|
| "resolveSelectionAdapter"
|
|
86
|
+
| "anthropicInstance"
|
|
87
|
+
| "currentAnthropicRouteDecision"
|
|
86
88
|
| "anthropicRouteDecision"
|
|
87
89
|
| "anthropicPoolAccountId"
|
|
88
90
|
| "anthropicPoolFailovers"
|
|
@@ -102,7 +104,8 @@ export function createAdapterContinuations(
|
|
|
102
104
|
| "noteAdapterPhysicalSend"
|
|
103
105
|
| "noteAdapterRecoveryWithheld"
|
|
104
106
|
| "remainingTransientSendBudget"
|
|
105
|
-
| "
|
|
107
|
+
| "adapterSendBudget"
|
|
108
|
+
| "transientSendReporter"
|
|
106
109
|
| "reserveCredentialHop"
|
|
107
110
|
| "pendingHopPermit"
|
|
108
111
|
| "sendBudgetExhausted"
|
|
@@ -122,6 +125,8 @@ export function createAdapterContinuations(
|
|
|
122
125
|
oauthDispatch,
|
|
123
126
|
invalidateSameTargetRequest,
|
|
124
127
|
resolveSelectionAdapter,
|
|
128
|
+
anthropicInstance,
|
|
129
|
+
currentAnthropicRouteDecision,
|
|
125
130
|
anthropicSessionKey,
|
|
126
131
|
commitResolvedOAuthSelection,
|
|
127
132
|
applyFailoverSnapshot,
|
|
@@ -135,7 +140,7 @@ export function createAdapterContinuations(
|
|
|
135
140
|
noteAdapterPhysicalSend,
|
|
136
141
|
noteAdapterRecoveryWithheld,
|
|
137
142
|
remainingTransientSendBudget,
|
|
138
|
-
|
|
143
|
+
transientSendReporter,
|
|
139
144
|
reserveCredentialHop,
|
|
140
145
|
sendBudgetExhausted,
|
|
141
146
|
} = sendBudgetState;
|
|
@@ -211,6 +216,8 @@ export function createAdapterContinuations(
|
|
|
211
216
|
try {
|
|
212
217
|
if (transportState.activeAdapter.fetchResponse) {
|
|
213
218
|
transportState.noteRoutedAttemptSend(continuationEstimate, replayKind);
|
|
219
|
+
const producer = adapterDispatchBudget?.beginSpendProducer?.();
|
|
220
|
+
try {
|
|
214
221
|
return await withProviderRequestSlot(route.providerName, route.provider, nextParsed.modelId, upstream.signal, pacingSlot =>
|
|
215
222
|
transportState.activeAdapter.fetchResponse!(builtContinuationRequest, {
|
|
216
223
|
kiroPreferAccountFailover: route.providerName === "kiro" && isGenericOAuthFailoverEnabled(config, "kiro"),
|
|
@@ -228,6 +235,7 @@ export function createAdapterContinuations(
|
|
|
228
235
|
modelId: nextParsed.modelId,
|
|
229
236
|
}),
|
|
230
237
|
}));
|
|
238
|
+
} finally { producer?.close(); }
|
|
231
239
|
}
|
|
232
240
|
// Same #1851 scope guard as the initial send: transient-5xx retry only for direct
|
|
233
241
|
// Google AI Studio; every other adapter keeps reset-only semantics here.
|
|
@@ -237,6 +245,9 @@ export function createAdapterContinuations(
|
|
|
237
245
|
: fetchWithResetRetry;
|
|
238
246
|
return await fetchContinuationWithRetryPolicy(
|
|
239
247
|
recovery => {
|
|
248
|
+
sendBudgetState.adapterSendBudget?.startRequest?.({
|
|
249
|
+
poolId: logCtx.spendPoolId ?? route.providerName, identityId: logCtx.accountLogLabel,
|
|
250
|
+
});
|
|
240
251
|
transportState.noteRoutedAttemptSend(continuationEstimate, recovery ?? replayKind);
|
|
241
252
|
return fetchWithHeaderTimeout(
|
|
242
253
|
builtContinuationRequest.url,
|
|
@@ -261,10 +272,10 @@ export function createAdapterContinuations(
|
|
|
261
272
|
// Same request-scoped budget as the initial send and the 429/rotation refetches:
|
|
262
273
|
// a terminal-guard continuation is another leg of ONE request, so handing it a
|
|
263
274
|
// fresh `attempts` would let one request exceed the configured total-send ceiling.
|
|
264
|
-
...(continuationTransientPolicy
|
|
275
|
+
...(continuationTransientPolicy || sendBudgetState.adapterSendBudget?.spendEnforced
|
|
265
276
|
? {
|
|
266
|
-
attempts: remainingTransientSendBudget(continuationTransientPolicy
|
|
267
|
-
onSendsConsumed:
|
|
277
|
+
attempts: remainingTransientSendBudget(continuationTransientPolicy?.attempts ?? 1),
|
|
278
|
+
onSendsConsumed: transientSendReporter(),
|
|
268
279
|
}
|
|
269
280
|
: {}),
|
|
270
281
|
},
|
|
@@ -390,33 +401,34 @@ export function createAdapterContinuations(
|
|
|
390
401
|
}
|
|
391
402
|
}
|
|
392
403
|
if (
|
|
393
|
-
(response.status === 429 || response.status === 403)
|
|
404
|
+
(response.status === 429 || response.status === 403 || response.status === 401)
|
|
405
|
+
&& anthropicInstance
|
|
394
406
|
&& transportState.anthropicPoolAccountId
|
|
395
407
|
&& !isNonReplayableResponse(response)
|
|
396
408
|
) {
|
|
397
|
-
const nextAccountId = await
|
|
409
|
+
const nextAccountId = await rotateAnthropicAccountOnResponseForInstance(anthropicInstance, response, {
|
|
398
410
|
config, accountId: transportState.anthropicPoolAccountId, sessionKey: anthropicSessionKey,
|
|
399
|
-
model: route.modelId, requestKey: transportState, decision: transportState.anthropicRouteDecision, signal: upstream.signal,
|
|
411
|
+
model: route.modelId, requestKey: transportState, decision: transportState.anthropicRouteDecision, currentDecision: currentAnthropicRouteDecision, signal: upstream.signal,
|
|
400
412
|
canRetry: !sendBudgetExhausted() && transportState.anthropicPoolFailovers < ANTHROPIC_POOL_MAX_FAILOVERS_PER_REQUEST,
|
|
401
413
|
allowAccountRefusal, allow429Recovery: allowAccountRefusal,
|
|
402
414
|
});
|
|
403
415
|
if (nextAccountId) {
|
|
404
416
|
try {
|
|
405
|
-
const admitted = await commitResolvedOAuthSelection(await getAnthropicPoolAccessSnapshot(nextAccountId));
|
|
417
|
+
const admitted = await commitResolvedOAuthSelection(await anthropicRoutingFor(anthropicInstance).getAnthropicPoolAccessSnapshot(nextAccountId));
|
|
406
418
|
if (!admitted) throw new Error("OAuth selection changed during recovery");
|
|
407
419
|
try { void response.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ }
|
|
408
420
|
transportState.anthropicPoolAccountId = admitted.accountId;
|
|
409
421
|
transportState.anthropicPoolFailovers += 1;
|
|
410
422
|
route.provider = { ...route.provider, apiKey: admitted.accessToken };
|
|
411
423
|
invalidateSameTargetRequest();
|
|
412
|
-
logCtx.provider = formatAnthropicProviderForLog(
|
|
424
|
+
logCtx.provider = formatAnthropicProviderForLog(anthropicInstance, admitted.accountId, config);
|
|
413
425
|
transportState.activeAdapter = resolveSelectionAdapter(
|
|
414
426
|
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire, route.staticPolicy),
|
|
415
427
|
config.cacheRetention,
|
|
416
428
|
);
|
|
417
429
|
sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, transportState.activeAdapter.name, logCtx.accountLogLabel);
|
|
418
430
|
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, transportState.activeAdapter.name);
|
|
419
|
-
nextContinuationRecoveryKind = "anthropic-oauth-429";
|
|
431
|
+
nextContinuationRecoveryKind = response.status === 401 ? "oauth-401" : "anthropic-oauth-429";
|
|
420
432
|
continue;
|
|
421
433
|
} catch {
|
|
422
434
|
// fall through to emit continuation error below
|
|
@@ -513,9 +525,9 @@ export function createAdapterContinuations(
|
|
|
513
525
|
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, transportState.activeAdapter.name);
|
|
514
526
|
// The replay goes out on the next iteration. An adapter that owns its ladder
|
|
515
527
|
// reserves for that send itself, so hand this reservation down rather than let it
|
|
516
|
-
// take a second one for the same replay. A helper-routed replay
|
|
517
|
-
// its reporter settles
|
|
518
|
-
|
|
528
|
+
// take a second one for the same replay. A helper-routed replay carries the
|
|
529
|
+
// same permit so its reporter settles only this booking.
|
|
530
|
+
sendBudgetState.pendingHopPermit = hop.permit;
|
|
519
531
|
nextContinuationRecoveryKind = "oauth-account-429";
|
|
520
532
|
kiroRefusalPendingReplay = response;
|
|
521
533
|
continue;
|
|
@@ -604,9 +616,9 @@ export function createAdapterContinuations(
|
|
|
604
616
|
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, transportState.activeAdapter.name);
|
|
605
617
|
// The replay goes out on the next iteration. An adapter that owns its ladder
|
|
606
618
|
// reserves for that send itself, so hand this reservation down rather than let it
|
|
607
|
-
// take a second one for the same replay. A helper-routed replay
|
|
608
|
-
// its reporter settles
|
|
609
|
-
|
|
619
|
+
// take a second one for the same replay. A helper-routed replay carries the
|
|
620
|
+
// same permit so its reporter settles only this booking.
|
|
621
|
+
sendBudgetState.pendingHopPermit = hop.permit;
|
|
610
622
|
nextContinuationRecoveryKind = "oauth-account-429";
|
|
611
623
|
continue;
|
|
612
624
|
}
|
|
@@ -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"
|
|
@@ -166,6 +170,9 @@ export async function prepareAdapterExchange(
|
|
|
166
170
|
| "workflowRootId"
|
|
167
171
|
| "claimAmbiguousResend"
|
|
168
172
|
| "sendsUsed"
|
|
173
|
+
| "targetSendsUsed"
|
|
174
|
+
| "initialSendAllowance"
|
|
175
|
+
| "noteInitialDispatch"
|
|
169
176
|
>,
|
|
170
177
|
) {
|
|
171
178
|
const { options, config, logCtx, req } = requestContext;
|
|
@@ -175,6 +182,8 @@ export async function prepareAdapterExchange(
|
|
|
175
182
|
refreshResolvedOAuthSelection,
|
|
176
183
|
invalidateSameTargetRequest,
|
|
177
184
|
resolveSelectionAdapter,
|
|
185
|
+
anthropicInstance,
|
|
186
|
+
currentAnthropicRouteDecision,
|
|
178
187
|
anthropicSessionKey,
|
|
179
188
|
commitResolvedOAuthSelection,
|
|
180
189
|
applyFailoverSnapshot,
|
|
@@ -194,7 +203,7 @@ export async function prepareAdapterExchange(
|
|
|
194
203
|
noteAdapterPhysicalSend,
|
|
195
204
|
noteAdapterRecoveryWithheld,
|
|
196
205
|
remainingTransientSendBudget,
|
|
197
|
-
|
|
206
|
+
transientSendReporter,
|
|
198
207
|
recoverySendAllowance,
|
|
199
208
|
recoveryClassFor,
|
|
200
209
|
sendBudgetExhausted,
|
|
@@ -333,6 +342,8 @@ export async function prepareAdapterExchange(
|
|
|
333
342
|
try {
|
|
334
343
|
if (transportState.activeAdapter.fetchResponse) {
|
|
335
344
|
transportState.noteRoutedAttemptSend(inputTokenEstimate);
|
|
345
|
+
const producer = adapterDispatchBudget?.beginSpendProducer?.();
|
|
346
|
+
try {
|
|
336
347
|
upstreamResponse = await withProviderRequestSlot(route.providerName, route.provider, route.modelId, upstream.signal, pacingSlot =>
|
|
337
348
|
transportState.activeAdapter.fetchResponse!(builtInitialRequest, {
|
|
338
349
|
kiroPreferAccountFailover: route.providerName === "kiro" && isGenericOAuthFailoverEnabled(config, "kiro"),
|
|
@@ -352,6 +363,7 @@ export async function prepareAdapterExchange(
|
|
|
352
363
|
? createCodexAuthDispatchGuard(admissionState.authCtx, options.codexAuthPolicy ?? config, route.modelId, options.admission, options.visionDescribeTerminal === true) : undefined,
|
|
353
364
|
}),
|
|
354
365
|
}));
|
|
366
|
+
} finally { producer?.close(); }
|
|
355
367
|
} else {
|
|
356
368
|
// #1851 scope guard: transient-5xx retry on this generic adapter path is opt-in for
|
|
357
369
|
// direct Google AI Studio only (Vertex/Antigravity use fetchResponse above). Other
|
|
@@ -361,22 +373,37 @@ export async function prepareAdapterExchange(
|
|
|
361
373
|
// legacy direct-Google exception is preserved exactly; every other adapter still keeps
|
|
362
374
|
// reset-only semantics so combo failover hops on the first 5xx.
|
|
363
375
|
const transientPolicy = transientRetryPolicyFor(route.provider);
|
|
376
|
+
const resetPolicy = resetReplayPolicyFor(route.provider);
|
|
364
377
|
const compactPrepaid = options.compactionRecoveryAttempted ? sendBudgetState.pendingHopPermit : undefined;
|
|
365
378
|
if (compactPrepaid) sendBudgetState.pendingHopPermit = undefined;
|
|
366
379
|
let compactPrepaidUsed = false;
|
|
380
|
+
// A combo-owned or compaction-prepaid send is settled by the physical-dispatch receipt
|
|
381
|
+
// when spend enforcement is off. Enforced spend and direct requests keep the shared
|
|
382
|
+
// physical-send reporter, which already owns the permit lifecycle there.
|
|
383
|
+
// Stable per request: a Combo or compaction permit comes from `reserveDispatch`, which starts
|
|
384
|
+
// (and freezes) the spend policy first, so `spendEnforced` cannot change before dispatch.
|
|
385
|
+
const receiptMode = !sendBudgetState.adapterSendBudget?.spendEnforced
|
|
386
|
+
&& Boolean(options.comboInitialSend || compactPrepaid);
|
|
367
387
|
// Combo and emergency compaction admission already book this target's first send.
|
|
368
388
|
// Its configured initial ceiling is target-local; the shared remainder below still
|
|
369
389
|
// accounts for earlier targets without deducting their sends from this target twice.
|
|
370
|
-
const initialSendCap = transientPolicy ||
|
|
390
|
+
const initialSendCap = transientPolicy || resetPolicy
|
|
391
|
+
|| (options.comboAttempt && route.provider.adapter === "google")
|
|
371
392
|
? transientSendCapFor(transientPolicy?.attempts,
|
|
372
393
|
(options.comboAttempt || compactPrepaid) ? 0 : sendBudgetState.sendsUsed)
|
|
373
394
|
: 1;
|
|
374
395
|
const fetchWithRetryPolicy = (route.provider.adapter === "google" || transientPolicy)
|
|
375
396
|
? fetchWithTransientRetry
|
|
376
397
|
: fetchWithResetRetry;
|
|
377
|
-
upstreamResponse = await fetchWithRetryPolicy(
|
|
398
|
+
try { upstreamResponse = await fetchWithRetryPolicy(
|
|
378
399
|
recovery => {
|
|
379
|
-
|
|
400
|
+
// Unconfigured generic sends may omit the counting reporter. Capture policy
|
|
401
|
+
// independently so a later recovery still belongs to this request's start.
|
|
402
|
+
sendBudgetState.adapterSendBudget?.startRequest?.({
|
|
403
|
+
poolId: logCtx.spendPoolId ?? route.providerName, identityId: logCtx.accountLogLabel,
|
|
404
|
+
});
|
|
405
|
+
// Receipt mode books the physical send at executor admission instead of here.
|
|
406
|
+
if (!receiptMode && compactPrepaid && !compactPrepaidUsed) {
|
|
380
407
|
if (!compactPrepaid.use()) throw new SendBudgetExhaustedError(safeHostLabel(builtInitialRequest.url));
|
|
381
408
|
compactPrepaidUsed = true;
|
|
382
409
|
}
|
|
@@ -390,6 +417,14 @@ export async function prepareAdapterExchange(
|
|
|
390
417
|
dispatchOverride: oauthDispatch(builtInitialRequest),
|
|
391
418
|
providerName: route.providerName,
|
|
392
419
|
modelId: route.modelId,
|
|
420
|
+
onPhysicalDispatch: () => {
|
|
421
|
+
// Preserve the unconfigured direct-adapter accounting contract; Combo admission
|
|
422
|
+
// and explicit retry/compaction budgets require a receipt for their booked send.
|
|
423
|
+
if (receiptMode) {
|
|
424
|
+
sendBudgetState.noteInitialDispatch(compactPrepaid && !compactPrepaidUsed ? compactPrepaid : undefined);
|
|
425
|
+
compactPrepaidUsed = true;
|
|
426
|
+
}
|
|
427
|
+
},
|
|
393
428
|
beforeDispatch: isCanonicalOpenAiForwardProvider(route.provider)
|
|
394
429
|
? createCodexAuthDispatchGuard(admissionState.authCtx, options.codexAuthPolicy ?? config, route.modelId, options.admission, options.visionDescribeTerminal === true) : undefined,
|
|
395
430
|
}));
|
|
@@ -398,16 +433,24 @@ export async function prepareAdapterExchange(
|
|
|
398
433
|
abortSignal: upstream.signal,
|
|
399
434
|
label: safeHostLabel(builtInitialRequest.url),
|
|
400
435
|
claimAmbiguousResend: claimPreHeaderResend,
|
|
401
|
-
...(
|
|
436
|
+
...(sendBudgetState.adapterSendBudget?.spendEnforced || options.comboDispatchPermit || transientPolicy || resetPolicy || compactPrepaid || options.comboAttempt
|
|
402
437
|
? {
|
|
403
438
|
// A pending compaction permit already booked this leg's first physical send.
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
439
|
+
...(transientPolicy || resetPolicy || compactPrepaid || options.comboAttempt
|
|
440
|
+
? {
|
|
441
|
+
attempts: Math.min(initialSendCap,
|
|
442
|
+
sendBudgetState.initialSendAllowance(initialSendCap) + (compactPrepaid ? 1 : 0)),
|
|
443
|
+
}
|
|
444
|
+
: {}),
|
|
445
|
+
// Reporter path: direct requests and enforced spend. Receipt-mode sends are
|
|
446
|
+
// settled by `onPhysicalDispatch`, so a reporter here would count them twice.
|
|
447
|
+
...(receiptMode
|
|
448
|
+
? {}
|
|
449
|
+
: { onSendsConsumed: transientSendReporter(compactPrepaid ?? options.comboDispatchPermit) }),
|
|
407
450
|
}
|
|
408
451
|
: {}),
|
|
409
452
|
},
|
|
410
|
-
);
|
|
453
|
+
); } finally { compactPrepaid?.release(); }
|
|
411
454
|
}
|
|
412
455
|
} catch (err) {
|
|
413
456
|
cleanupUpstreamAbort();
|
|
@@ -437,7 +480,8 @@ export async function prepareAdapterExchange(
|
|
|
437
480
|
// blaming the provider makes the caller send the whole turn again -- the amplification this
|
|
438
481
|
// budget exists to stop. The passthrough path has answered 429 here since #4546.
|
|
439
482
|
if (err instanceof SendBudgetExhaustedError) {
|
|
440
|
-
return
|
|
483
|
+
return unboundPoolSpendRefusalResponse(logCtx)
|
|
484
|
+
?? formatErrorResponse(429, SEND_BUDGET_EXHAUSTED_CODE, err.message);
|
|
441
485
|
}
|
|
442
486
|
const msg = describeUpstreamConnectFailure(err, connectMs);
|
|
443
487
|
return formatErrorResponse(502, "upstream_error", msg);
|
|
@@ -541,6 +585,8 @@ export async function prepareAdapterExchange(
|
|
|
541
585
|
try {
|
|
542
586
|
if (transportState.activeAdapter.fetchResponse) {
|
|
543
587
|
transportState.noteRoutedAttemptSend(retryEstimate, recovery);
|
|
588
|
+
const producer = adapterDispatchBudget?.beginSpendProducer?.();
|
|
589
|
+
try {
|
|
544
590
|
return await withProviderRequestSlot(route.providerName, route.provider, route.modelId, upstream.signal, pacingSlot => {
|
|
545
591
|
// The dispatch boundary is HERE, not before the pacing wait: that wait can reject for
|
|
546
592
|
// an abort, a saturated queue, an expired slot or a removed provider, and none of
|
|
@@ -570,6 +616,7 @@ export async function prepareAdapterExchange(
|
|
|
570
616
|
}),
|
|
571
617
|
});
|
|
572
618
|
});
|
|
619
|
+
} finally { producer?.close(); }
|
|
573
620
|
}
|
|
574
621
|
// #2643 review: this leg used to call fetchWithHeaderTimeout directly, so an
|
|
575
622
|
// opted-in provider's transient-5xx policy applied to the initial send and to
|
|
@@ -581,11 +628,10 @@ export async function prepareAdapterExchange(
|
|
|
581
628
|
const refetchWithPolicy = (route.provider.adapter === "google" || refetchTransientPolicy)
|
|
582
629
|
? fetchWithTransientRetry
|
|
583
630
|
: fetchWithResetRetry;
|
|
584
|
-
const helperCountsSends = refetchTransientPolicy !== null || resetReplayPolicyFor(route.provider) !== null;
|
|
631
|
+
const helperCountsSends = sendBudgetState.adapterSendBudget?.spendEnforced === true || refetchTransientPolicy !== null || resetReplayPolicyFor(route.provider) !== null;
|
|
585
632
|
const prepaid = sendBudgetState.pendingHopPermit;
|
|
586
633
|
const configuredTotal = refetchTransientPolicy?.attempts;
|
|
587
|
-
const refetchCap = transientSendCapFor(configuredTotal,
|
|
588
|
-
sendBudgetState.sendsUsed - (prepaid ? 1 : 0));
|
|
634
|
+
const refetchCap = transientSendCapFor(configuredTotal, sendBudgetState.targetSendsUsed);
|
|
589
635
|
// An exact total includes a booked hop, even when it consumed the last base slot.
|
|
590
636
|
// Keep that funded send while forbidding the final reserve from widening the total.
|
|
591
637
|
const prepaidLastSlot = helperCountsSends && configuredTotal !== undefined && prepaid
|
|
@@ -601,6 +647,9 @@ export async function prepareAdapterExchange(
|
|
|
601
647
|
try {
|
|
602
648
|
return await refetchWithPolicy(
|
|
603
649
|
recoveryKind => {
|
|
650
|
+
sendBudgetState.adapterSendBudget?.startRequest?.({
|
|
651
|
+
poolId: logCtx.spendPoolId ?? route.providerName, identityId: logCtx.accountLogLabel,
|
|
652
|
+
});
|
|
604
653
|
if (refetchAllowance?.permit && !refetchAllowance.permit.use()) {
|
|
605
654
|
throw new SendBudgetExhaustedError(safeHostLabel(retryRequest.url));
|
|
606
655
|
}
|
|
@@ -629,7 +678,7 @@ export async function prepareAdapterExchange(
|
|
|
629
678
|
...(refetchAllowance
|
|
630
679
|
? {
|
|
631
680
|
attempts: refetchAllowance.attempts,
|
|
632
|
-
onSendsConsumed:
|
|
681
|
+
onSendsConsumed: transientSendReporter(refetchAllowance.permit),
|
|
633
682
|
}
|
|
634
683
|
: {}),
|
|
635
684
|
},
|
|
@@ -673,7 +722,8 @@ export async function prepareAdapterExchange(
|
|
|
673
722
|
// Same rule on the recovery leg: the ladder refused to send again, so the answer names
|
|
674
723
|
// this proxy rather than the provider it never reached.
|
|
675
724
|
if (err instanceof SendBudgetExhaustedError) {
|
|
676
|
-
return { failed:
|
|
725
|
+
return { failed: unboundPoolSpendRefusalResponse(logCtx)
|
|
726
|
+
?? formatErrorResponse(429, SEND_BUDGET_EXHAUSTED_CODE, err.message) };
|
|
677
727
|
}
|
|
678
728
|
const msg = describeUpstreamConnectFailure(err, connectMs);
|
|
679
729
|
return { failed: formatErrorResponse(502, "upstream_error", msg) };
|
|
@@ -1047,31 +1097,32 @@ export async function prepareAdapterExchange(
|
|
|
1047
1097
|
// Anthropic OAuth: recover a rate limit or proven account entitlement refusal
|
|
1048
1098
|
// before output, within the shared request and account rotation limits.
|
|
1049
1099
|
while (
|
|
1050
|
-
(upstreamResponse.status === 429 || upstreamResponse.status === 403)
|
|
1100
|
+
(upstreamResponse.status === 429 || upstreamResponse.status === 403 || upstreamResponse.status === 401)
|
|
1101
|
+
&& anthropicInstance
|
|
1051
1102
|
&& transportState.anthropicPoolAccountId
|
|
1052
1103
|
) {
|
|
1053
|
-
const nextAccountId = await
|
|
1104
|
+
const nextAccountId = await rotateAnthropicAccountOnResponseForInstance(anthropicInstance, upstreamResponse, {
|
|
1054
1105
|
config, accountId: transportState.anthropicPoolAccountId, sessionKey: anthropicSessionKey,
|
|
1055
|
-
model: route.modelId, requestKey: transportState, decision: transportState.anthropicRouteDecision, signal: upstream.signal,
|
|
1106
|
+
model: route.modelId, requestKey: transportState, decision: transportState.anthropicRouteDecision, currentDecision: currentAnthropicRouteDecision, signal: upstream.signal,
|
|
1056
1107
|
canRetry: !sendBudgetExhausted() && transportState.anthropicPoolFailovers < ANTHROPIC_POOL_MAX_FAILOVERS_PER_REQUEST,
|
|
1057
1108
|
});
|
|
1058
1109
|
if (!nextAccountId) break;
|
|
1059
1110
|
try {
|
|
1060
|
-
const admitted = await commitResolvedOAuthSelection(await getAnthropicPoolAccessSnapshot(nextAccountId));
|
|
1111
|
+
const admitted = await commitResolvedOAuthSelection(await anthropicRoutingFor(anthropicInstance).getAnthropicPoolAccessSnapshot(nextAccountId));
|
|
1061
1112
|
if (!admitted) throw new Error("OAuth selection changed during recovery");
|
|
1062
1113
|
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ }
|
|
1063
1114
|
transportState.anthropicPoolAccountId = admitted.accountId;
|
|
1064
1115
|
transportState.anthropicPoolFailovers += 1;
|
|
1065
1116
|
route.provider = { ...route.provider, apiKey: admitted.accessToken };
|
|
1066
1117
|
invalidateSameTargetRequest();
|
|
1067
|
-
logCtx.provider = formatAnthropicProviderForLog(
|
|
1118
|
+
logCtx.provider = formatAnthropicProviderForLog(anthropicInstance, admitted.accountId, config);
|
|
1068
1119
|
transportState.activeAdapter = resolveSelectionAdapter(
|
|
1069
1120
|
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire, route.staticPolicy),
|
|
1070
1121
|
config.cacheRetention,
|
|
1071
1122
|
);
|
|
1072
1123
|
sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, transportState.activeAdapter.name, logCtx.accountLogLabel);
|
|
1073
1124
|
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, transportState.activeAdapter.name);
|
|
1074
|
-
const result = await rebuildAndRefetch("anthropic-oauth-429");
|
|
1125
|
+
const result = await rebuildAndRefetch(upstreamResponse.status === 401 ? "oauth-401" : "anthropic-oauth-429");
|
|
1075
1126
|
if ("failed" in result) return result.failed;
|
|
1076
1127
|
upstreamResponse = result;
|
|
1077
1128
|
if (isNonReplayableResponse(upstreamResponse)) continue recovery;
|
|
@@ -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
|
}
|
|
@@ -9,7 +9,7 @@ import { CODEX_RESPONSES_HTTP_URL, type PreparedCodexWsRequest } from "./codex-w
|
|
|
9
9
|
import { CodexWsCorrelation } from "./codex-ws-correlation";
|
|
10
10
|
import type { CodexWsSession } from "./codex-ws-session";
|
|
11
11
|
import { UPGRADE_DEADLINE_MS, CODEX_WS_LIVENESS_PING_INTERVAL_MS, CODEX_WS_RESPONSE_PRELUDE_TIMEOUT_MS, MAX_CODEX_WS_FRAME_BYTES,
|
|
12
|
-
MAX_CODEX_WS_QUEUE_BYTES, markCodexWsResponse, normalizeResponsesWsRelayEvent, closedBeforeTerminalMessage,
|
|
12
|
+
MAX_CODEX_WS_QUEUE_BYTES, markCodexWsResponse, markCodexWsPreludeProjection, normalizeResponsesWsRelayEvent, closedBeforeTerminalMessage,
|
|
13
13
|
codexWsCreateFrameExceedsLimit, codexWsFailureDetail, codexWsPreResponseFailure, markCodexWsStage, codexWsOcxVersion,
|
|
14
14
|
markCodexWsSocketDeath, type CodexWsFailureStage, type CodexWsStageRecord } from "./codex-ws-wire";
|
|
15
15
|
|
|
@@ -23,6 +23,7 @@ interface ExchangeOptions {
|
|
|
23
23
|
sseFallback: typeof globalThis.fetch;
|
|
24
24
|
onQuota?: CodexWsQuotaObserver;
|
|
25
25
|
beforeDispatch?: (headers: Headers) => void;
|
|
26
|
+
onPhysicalDispatch?: () => void;
|
|
26
27
|
/** Bun version string the caller gated on; stamped onto the stage record. */
|
|
27
28
|
bunVersion?: string;
|
|
28
29
|
}
|
|
@@ -44,7 +45,8 @@ function rejectionHeaders(source: Record<string, unknown>, prelude: Headers): He
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
// Reuse the metadata owner's count/value/family budgets and window freshness
|
|
47
|
-
// rules, without publishing quota twice.
|
|
48
|
+
// rules, without publishing quota twice. Pool bookkeeping consumes the HTTP
|
|
49
|
+
// projection; plain-main publication belongs only to the WS observer.
|
|
48
50
|
const projected = new CodexWsMetadata();
|
|
49
51
|
try {
|
|
50
52
|
for (const values of [Object.fromEntries(prelude), source]) {
|
|
@@ -240,6 +242,7 @@ export function codexWsExchange(options: ExchangeOptions): Promise<Response> {
|
|
|
240
242
|
// are left out: their channel may already have sent continuation frames on this socket, so
|
|
241
243
|
// the create frame alone no longer describes the turn.
|
|
242
244
|
if (socketDied && !nativeControl) markCodexWsSocketDeath(failureResponse, stage);
|
|
245
|
+
markCodexWsPreludeProjection(failureResponse);
|
|
243
246
|
resolve(failureResponse);
|
|
244
247
|
return;
|
|
245
248
|
}
|
|
@@ -411,6 +414,17 @@ export function codexWsExchange(options: ExchangeOptions): Promise<Response> {
|
|
|
411
414
|
failStream(error);
|
|
412
415
|
return;
|
|
413
416
|
}
|
|
417
|
+
try { options.onPhysicalDispatch?.(); } catch (error) {
|
|
418
|
+
// Admission refused before the frame left. This is a pre-open rejection,
|
|
419
|
+
// not a transport failure and not an authorization to replay over SSE.
|
|
420
|
+
settledPreOpen = true;
|
|
421
|
+
sent = false;
|
|
422
|
+
terminal = true;
|
|
423
|
+
cleanup();
|
|
424
|
+
session.dispose();
|
|
425
|
+
reject(error);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
414
428
|
try {
|
|
415
429
|
ws.send(frameText);
|
|
416
430
|
sentAt = Date.now();
|
|
@@ -501,6 +515,7 @@ export function codexWsExchange(options: ExchangeOptions): Promise<Response> {
|
|
|
501
515
|
cleanup();
|
|
502
516
|
try { controller.close(); } catch { /* unused stream already closed */ }
|
|
503
517
|
session.dispose();
|
|
518
|
+
markCodexWsPreludeProjection(rejection);
|
|
504
519
|
resolve(rejection);
|
|
505
520
|
return;
|
|
506
521
|
}
|