@bitkyc08/opencodex 2.58.0 → 2.60.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 +28 -10
- package/gui/dist/assets/index-BTuCbqQd.css +1 -0
- package/gui/dist/assets/index-DoBVdPHP.js +134 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/crusoe.svg +1 -0
- package/gui/dist/provider-icons/opper.svg +3 -0
- package/package.json +4 -1
- package/src/adapters/anthropic-image-codec.ts +16 -2
- package/src/adapters/anthropic-image-normalize.ts +49 -2
- package/src/adapters/anthropic.ts +4 -1
- package/src/adapters/base.ts +34 -1
- package/src/adapters/coding-agent/turn.ts +22 -2
- package/src/adapters/command-code.ts +50 -3
- package/src/adapters/cursor/catalog.ts +11 -0
- package/src/adapters/cursor/checkpoint-store.ts +3 -0
- package/src/adapters/cursor/discovery.ts +11 -8
- package/src/adapters/cursor/effort-map.ts +16 -2
- package/src/adapters/cursor/envelope-echo.ts +55 -2
- package/src/adapters/cursor/live-transport.ts +26 -9
- package/src/adapters/cursor/message-mapper.ts +3 -2
- package/src/adapters/cursor/protobuf-request.ts +8 -5
- package/src/adapters/cursor/request-builder.ts +21 -4
- package/src/adapters/cursor/thread-continuity.ts +105 -31
- package/src/adapters/cursor/tool-guidance.ts +5 -4
- package/src/adapters/cursor/transport.ts +19 -0
- package/src/adapters/cursor.ts +45 -2
- package/src/adapters/devin/cloud-direct/chat.ts +14 -3
- package/src/adapters/devin/cloud-direct/index.ts +7 -0
- package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +140 -0
- package/src/adapters/devin.ts +125 -26
- package/src/adapters/google-antigravity-replay.ts +1 -1
- package/src/adapters/google-antigravity-wire.ts +55 -4
- package/src/adapters/google-http.ts +57 -11
- package/src/adapters/google-tool-schema.ts +595 -31
- package/src/adapters/google-wire-compiler.ts +93 -10
- package/src/adapters/google-wire-shape.ts +461 -0
- package/src/adapters/google.ts +60 -10
- package/src/adapters/openai-chat/response-events.ts +61 -0
- package/src/adapters/openai-chat-images.ts +3 -1
- package/src/adapters/openai-chat.ts +10 -11
- package/src/adapters/openai-responses/image-gen.ts +8 -6
- package/src/adapters/openai-responses/passthrough.ts +25 -4
- package/src/adapters/openai-responses/reasoning.ts +7 -0
- package/src/adapters/openai-responses/tool-output-recovery.ts +75 -0
- package/src/adapters/openai-responses/tool-schema.ts +19 -7
- package/src/adapters/opencode-go-additional-tools.ts +12 -2
- package/src/adapters/responses-tool-schema.ts +76 -46
- package/src/adapters/run-turn-queue.ts +17 -4
- package/src/bridge/response-json.ts +1 -1
- package/src/bridge/sse.ts +28 -35
- package/src/claude/context-windows.ts +22 -0
- package/src/claude/outbound.ts +35 -4
- package/src/cli/account-api.ts +4 -3
- package/src/cli/account-extended.ts +26 -6
- package/src/cli/account-orca-import.ts +63 -0
- package/src/cli/account.ts +32 -4
- package/src/cli/capabilities.ts +40 -0
- package/src/cli/claude.ts +29 -1
- package/src/cli/codex-cli-update.ts +97 -2
- package/src/cli/dispatch.ts +57 -3
- package/src/cli/doctor.ts +218 -4
- package/src/cli/help.ts +4 -1
- package/src/cli/hub.ts +3 -2
- package/src/cli/index.ts +95 -22
- package/src/cli/models-runtime.ts +33 -4
- package/src/cli/opencode.ts +2 -2
- package/src/cli/provider.ts +13 -1
- package/src/cli/registry.ts +11 -1
- package/src/cli/runtime-api.ts +44 -0
- package/src/cli/start-args.ts +94 -0
- package/src/cli/system-command.ts +2 -0
- package/src/client/machine-api.ts +6 -5
- package/src/client/machine-listener.ts +16 -3
- package/src/client/runtime.ts +26 -2
- package/src/clients/config-export/constants.ts +2 -3
- package/src/clients/config-export.ts +5 -5
- package/src/codex/account-store.ts +146 -5
- package/src/codex/auth-api/account-list.ts +19 -11
- package/src/codex/auth-api/pool-quota-probe.ts +44 -10
- package/src/codex/auth-api/routes.ts +17 -2
- package/src/codex/auth-context.ts +16 -12
- package/src/codex/catalog/build-entries.ts +25 -4
- package/src/codex/catalog/derive-entry.ts +8 -1
- package/src/codex/catalog/effort.ts +10 -6
- package/src/codex/catalog/gather-capture.ts +22 -2
- package/src/codex/catalog/model-hints.ts +66 -33
- package/src/codex/catalog/parsing.ts +90 -5
- package/src/codex/catalog/provider-models.ts +19 -2
- package/src/codex/catalog/reserve-warn.ts +96 -0
- package/src/codex/catalog/retained-sync.ts +41 -26
- package/src/codex/catalog/routed-gather.ts +61 -3
- package/src/codex/cli-installation-identity.ts +210 -0
- package/src/codex/cli-installation-targets.ts +158 -0
- package/src/codex/context-compat.ts +5 -2
- package/src/codex/convergence.ts +5 -0
- package/src/codex/desired-state.ts +4 -1
- package/src/codex/history-job.ts +6 -6
- package/src/codex/history-provider.ts +24 -167
- package/src/codex/history-rollout-read.ts +174 -0
- package/src/codex/history-state-open.ts +105 -0
- package/src/codex/inject/config-toml.ts +44 -2
- package/src/codex/inject.ts +3 -2
- package/src/codex/internal/catalog-writer.ts +33 -1
- package/src/codex/lineage.ts +83 -32
- package/src/codex/loopback-target.ts +31 -0
- package/src/codex/main-account-hard-lock.ts +2 -1
- package/src/codex/main-account.ts +10 -3
- package/src/codex/main-device-reauth.ts +17 -9
- package/src/codex/model-cache.ts +47 -0
- package/src/codex/model-entitlements.ts +80 -2
- package/src/codex/observed-model-denials.ts +230 -0
- package/src/codex/orca-auth-source.ts +94 -0
- package/src/codex/orca-import.ts +219 -0
- package/src/codex/prompt-text-probe.ts +289 -16
- package/src/codex/quota-401-recovery.ts +12 -0
- package/src/codex/quota-types.ts +65 -0
- package/src/codex/quota.ts +24 -19
- package/src/codex/routing/cooldown-math.ts +8 -47
- package/src/codex/routing/pin-drain.ts +57 -0
- package/src/codex/routing.ts +20 -16
- package/src/codex/shim.ts +1 -1
- package/src/codex/subagent-model-fallback.ts +114 -2
- package/src/codex/windows-installation-files.ts +224 -0
- package/src/combos/failover.ts +125 -5
- package/src/config/admitted-identity.ts +222 -0
- package/src/config/diagnostics.ts +43 -1
- package/src/config/feature-flags.ts +5 -0
- package/src/config/load-degrade.ts +33 -0
- package/src/config/pending-teardown.ts +8 -0
- package/src/config/process-state.ts +36 -3
- package/src/config/provider-relative-send-path.ts +16 -0
- package/src/config/proxy-env.ts +31 -7
- package/src/config/schema/compaction-triggers.ts +11 -0
- package/src/config/schema/config-schema.ts +25 -0
- package/src/config/schema/leaf-validators.ts +76 -17
- package/src/config.ts +2 -2
- package/src/generated/compatibility-version.json +410 -258
- package/src/generated/model-metadata.ts +1 -1
- package/src/grok/reset-coupons.ts +38 -19
- package/src/images/loop.ts +6 -1
- package/src/integrations/aside-profile-context.ts +37 -3
- package/src/integrations/aside-profile-journal.ts +68 -3
- package/src/integrations/aside-profiles.ts +128 -3
- package/src/integrations/mutation-plan.ts +815 -0
- package/src/integrations/writer.ts +85 -99
- package/src/lab/live/transport.ts +4 -0
- package/src/lab/live/types.ts +5 -0
- package/src/lab/subject/behavior-fingerprint.ts +1 -1
- package/src/lib/admin-secrets.ts +9 -1
- package/src/lib/bounded-body.ts +4 -2
- package/src/lib/debug-log-buffer.ts +6 -1
- package/src/lib/debug.ts +23 -0
- package/src/lib/destination-policy.ts +48 -6
- package/src/lib/errors.ts +82 -15
- package/src/lib/http-response-semantics.ts +57 -0
- package/src/lib/lab-live-pinned-sender.ts +26 -12
- package/src/lib/local-destinations.ts +32 -5
- package/src/lib/pinned-http.ts +142 -2
- package/src/lib/plain-data.ts +103 -0
- package/src/lib/process-control.ts +13 -5
- package/src/lib/provider-outbound.ts +53 -5
- package/src/lib/proxy-env.ts +70 -3
- package/src/lib/request-execution-budget.ts +11 -3
- package/src/lib/response-body-inactivity.ts +193 -0
- package/src/lib/retry-delay.ts +69 -0
- package/src/lib/socks5-fetch.ts +741 -0
- package/src/lib/spend-ledger-owner.ts +364 -0
- package/src/lib/spend-reservation-ledger.ts +332 -35
- package/src/lib/windows-system-proxy.ts +16 -11
- package/src/lib/workflow-budget.ts +145 -8
- package/src/oauth/account-quota-rank.ts +72 -15
- package/src/oauth/callback-server.ts +4 -3
- package/src/oauth/generic-account-failover.ts +41 -27
- package/src/oauth/health.ts +12 -1
- package/src/oauth/index.ts +3 -107
- package/src/oauth/login-flow-state.ts +127 -0
- package/src/oauth/orcarouter.ts +15 -2
- package/src/oauth/store.ts +8 -0
- package/src/providers/codex-capacity.ts +9 -0
- package/src/providers/derive.ts +34 -17
- package/src/providers/devin-cli-authmode-migration.ts +14 -10
- package/src/providers/devin-provider-merge-migration.ts +33 -12
- package/src/providers/free-directory.ts +20 -2
- package/src/providers/key-failover.ts +327 -25
- package/src/providers/model-rename-migration.ts +56 -1
- package/src/providers/model-rename-startup.ts +7 -5
- package/src/providers/openai-sidecar.ts +4 -0
- package/src/providers/openai-virtual-models.ts +42 -2
- package/src/providers/opencode-go-transport.ts +14 -5
- package/src/providers/quota/antigravity.ts +22 -2
- package/src/providers/quota/report-cache.ts +3 -0
- package/src/providers/quota/vendor-probes-key.ts +1 -1
- package/src/providers/registry/entries-core.ts +39 -17
- package/src/providers/registry/entries-extended.ts +131 -1
- package/src/providers/registry/model-ids.ts +168 -0
- package/src/providers/registry/model-seeds.ts +87 -21
- package/src/providers/registry/types.ts +2 -0
- package/src/providers/resolved-model-policy-merge.ts +167 -0
- package/src/providers/resolved-model-policy.ts +406 -0
- package/src/providers/stale-vision-classification-migration.ts +137 -0
- package/src/responses/apply-patch-envelope.ts +32 -11
- package/src/responses/bridge-search-replay-cache.ts +152 -0
- package/src/responses/code-mode-helper-compat.ts +26 -16
- package/src/responses/custom-tool-compat.ts +1 -1
- package/src/responses/freeform-wrapper-scan.ts +279 -0
- package/src/responses/hosted-tool-policy.ts +85 -2
- package/src/responses/legacy-dotted-tool-name-repair.ts +134 -0
- package/src/responses/progressive-freeform-input.ts +130 -0
- package/src/responses/reasoning-envelope.ts +30 -0
- package/src/responses/schema.ts +9 -2
- package/src/responses/state.ts +5 -12
- package/src/responses/tool-name-aliases.ts +15 -1
- package/src/router.ts +91 -115
- package/src/routing/compatibility/behavior.ts +9 -0
- package/src/routing/compatibility/subject.ts +16 -1
- package/src/server/adapter-resolve.ts +9 -0
- package/src/server/auth-cors.ts +29 -0
- package/src/server/chat-completions.ts +13 -5
- package/src/server/chat-native-sse.ts +26 -9
- package/src/server/chat-native.ts +10 -4
- package/src/server/claude-messages.ts +30 -5
- package/src/server/effort-row.ts +11 -3
- package/src/server/grok-responses-control-frame.ts +160 -1
- package/src/server/gui-static.ts +36 -2
- package/src/server/inbound-body-admission.ts +187 -0
- package/src/server/index/serve-options.ts +86 -29
- package/src/server/index/spend-ledger-lifecycle.ts +66 -0
- package/src/server/index/websocket-handler.ts +6 -1
- package/src/server/index.ts +24 -25
- package/src/server/management/api-access.ts +3 -4
- package/src/server/management/aside-profile-routes.ts +266 -7
- package/src/server/management/config-routes.ts +48 -7
- package/src/server/management/context.ts +3 -0
- package/src/server/management/integration-routes.ts +287 -5
- package/src/server/management/metrics-routes.ts +20 -0
- package/src/server/management/model-rows.ts +224 -12
- package/src/server/management/provider-capability-config.ts +35 -7
- package/src/server/management/provider-routes.ts +70 -18
- package/src/server/management/route-registry.ts +14 -0
- package/src/server/management/shared.ts +10 -3
- package/src/server/management/system-restart.ts +7 -2
- package/src/server/management/system-routes.ts +2 -0
- package/src/server/management/usage-aggregate-cache.ts +4 -0
- package/src/server/management-api.ts +2 -0
- package/src/server/management-auth.ts +15 -1
- package/src/server/proxy-liveness.ts +97 -2
- package/src/server/readiness.ts +29 -10
- package/src/server/relay-eager.ts +24 -2
- package/src/server/relay.ts +136 -34
- package/src/server/request-log.ts +80 -3
- package/src/server/request-metrics.ts +236 -0
- package/src/server/responses/adapter-continuation.ts +74 -30
- package/src/server/responses/adapter-delivery.ts +39 -8
- package/src/server/responses/adapter-dispatch.ts +63 -30
- package/src/server/responses/compact.ts +89 -18
- package/src/server/responses/compaction-routing.ts +111 -0
- package/src/server/responses/core-codex-account.ts +90 -24
- package/src/server/responses/core-combo.ts +7 -7
- package/src/server/responses/core-normalize.ts +16 -15
- package/src/server/responses/core-opaque-recovery.ts +1 -0
- package/src/server/responses/core-options.ts +4 -0
- package/src/server/responses/encrypted-payload.ts +20 -2
- package/src/server/responses/fetch-helpers.ts +68 -2
- package/src/server/responses/passthrough-delivery.ts +41 -15
- package/src/server/responses/passthrough-dispatch.ts +145 -53
- package/src/server/responses/passthrough-execution.ts +11 -1
- package/src/server/responses/policy-fallback.ts +5 -13
- package/src/server/responses/request-prepare.ts +96 -17
- package/src/server/responses/request-send-budget.ts +89 -8
- package/src/server/responses/request-sidecar-auth.ts +17 -9
- package/src/server/responses/request-spend.ts +38 -9
- package/src/server/responses/request-transport.ts +15 -12
- package/src/server/responses/run-turn-execution.ts +45 -8
- package/src/server/responses/sidecar-execution.ts +19 -2
- package/src/server/responses/ws-upstream.ts +16 -28
- package/src/server/responses-custom-tool-repair.ts +29 -56
- package/src/server/responses-undeclared-tool-guard.ts +31 -1
- package/src/server/sse-frame-buffer.ts +12 -10
- package/src/server/sse-payload-rewrite.ts +37 -10
- package/src/server/system-env-shell.ts +5 -1
- package/src/server/system-env.ts +7 -1
- package/src/server/workflow-refusal.ts +56 -2
- package/src/service/cli.ts +16 -6
- package/src/service/guards.ts +10 -0
- package/src/service/health.ts +43 -0
- package/src/service/state.ts +7 -2
- package/src/tray/windows-tray.ps1 +155 -3
- package/src/types/accounts.ts +4 -0
- package/src/types/config.ts +111 -6
- package/src/types/provider.ts +37 -0
- package/src/types/request.ts +9 -1
- package/src/types/tools.ts +14 -0
- package/src/types/wire.ts +9 -1
- package/src/types.ts +1 -0
- package/src/usage/expected-prices.ts +28 -0
- package/src/usage/log.ts +87 -4
- package/src/vision/eligibility.ts +88 -9
- package/src/vision/plan.ts +34 -10
- package/src/web-search/executor.ts +41 -2
- package/src/web-search/loop.ts +6 -1
- package/src/web-search/passthrough-bridge.ts +39 -5
- package/gui/dist/assets/index-BbrHOIY0.js +0 -128
- package/gui/dist/assets/index-C5-RdDmD.css +0 -1
|
@@ -11,6 +11,7 @@ import type { PreparedResponsesRequest } from "./request-prepare";
|
|
|
11
11
|
import type { ResponsesTransport } from "./request-transport";
|
|
12
12
|
import type { ResponsesEffects } from "./response-effects";
|
|
13
13
|
import type { ResponsesSendBudget } from "./request-send-budget";
|
|
14
|
+
import { transientSendCapFor } from "./request-send-budget";
|
|
14
15
|
import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
|
|
15
16
|
import { codexSafetyBufferingFilterOptions, terminalStatusFromParsed } from "../relay";
|
|
16
17
|
import { imageGenToolCallAliases } from "../responses-image-gen-repair";
|
|
@@ -68,6 +69,7 @@ import {
|
|
|
68
69
|
sealRequestAttemptIdentity,
|
|
69
70
|
recordAttemptCredentialSource,
|
|
70
71
|
} from "../request-log";
|
|
72
|
+
import { noteAttemptRecoveryWithheld } from "../request-log";
|
|
71
73
|
import {
|
|
72
74
|
upstreamHostHealthKey,
|
|
73
75
|
normalizeUpstreamHostCircuitThreshold,
|
|
@@ -91,11 +93,15 @@ import {
|
|
|
91
93
|
usesCodexForwardPoolAuth,
|
|
92
94
|
codexWsQuotaObserver,
|
|
93
95
|
isFixedCodexAccount,
|
|
94
|
-
|
|
96
|
+
codexPoolAccountModel400Denial,
|
|
95
97
|
shouldRetryCodexPoolAccountQuota,
|
|
96
98
|
shouldRetryCodexPoolAccountTransient,
|
|
97
99
|
retryCodexPoolOnAlternateAccount,
|
|
98
100
|
} from "./core-codex-account";
|
|
101
|
+
import {
|
|
102
|
+
clearCodexModelDenialEvidence,
|
|
103
|
+
recordCodexModelDenialEvidence,
|
|
104
|
+
} from "../../codex/model-entitlements";
|
|
99
105
|
import { readCodexWsStage } from "./codex-ws-wire";
|
|
100
106
|
import { linkAbortSignal } from "./core-lifetime";
|
|
101
107
|
import type { CodexAuthContext } from "../../codex/auth-context";
|
|
@@ -105,9 +111,8 @@ import {
|
|
|
105
111
|
SendBudgetExhaustedError,
|
|
106
112
|
fetchWithTransientRetry,
|
|
107
113
|
applyUpstreamRecoveryInit,
|
|
108
|
-
TRANSIENT_RETRY_MAX_ATTEMPTS,
|
|
109
114
|
isNonReplayableResponse,
|
|
110
|
-
|
|
115
|
+
prepareSameTarget429Wait,
|
|
111
116
|
sleepWithAbort,
|
|
112
117
|
} from "../../lib/upstream-retry";
|
|
113
118
|
import { mapCodexAuthContextErrorToResponse } from "./codex-auth-error";
|
|
@@ -115,7 +120,12 @@ import { classifyTransportFailureKind, transportErrorCode } from "../../lib/upst
|
|
|
115
120
|
import { recordCodexUpstreamOutcome } from "../../codex/routing";
|
|
116
121
|
import { describeUpstreamConnectFailure } from "./upstream-error";
|
|
117
122
|
import type { OpaqueBlobRecoveryGuard } from "./core-opaque-recovery";
|
|
118
|
-
import {
|
|
123
|
+
import {
|
|
124
|
+
isOpenCodeGoDestination,
|
|
125
|
+
rateLimitRetryPolicyFor,
|
|
126
|
+
rateLimitRetryDelayMs,
|
|
127
|
+
transientRetryPolicyFor,
|
|
128
|
+
} from "../../providers/key-failover";
|
|
119
129
|
import type { AttemptRecoveryKind } from "../../usage/log";
|
|
120
130
|
import { resolveWireProtocolOverride } from "../adapter-resolve";
|
|
121
131
|
import { refreshPoolForwardAuth, refreshNativeMainForwardAuth, withClaudeNativeSession } from "./core-auth";
|
|
@@ -133,6 +143,7 @@ import { captureCodexAffinityDiagnostic } from "../../codex/affinity-debug";
|
|
|
133
143
|
import { ACCOUNT_GATED_NATIVE_OPENAI_MODELS } from "../../codex/catalog/native-models";
|
|
134
144
|
import {
|
|
135
145
|
attemptOpaqueBlobRecovery,
|
|
146
|
+
isEncryptedFunctionOutputRejection,
|
|
136
147
|
outboundResponsesBodyCarriesEncryptedFunctionOutput,
|
|
137
148
|
resetStreamedOpaqueBlobLogContext,
|
|
138
149
|
consoleGoUploadRejectionBody,
|
|
@@ -198,6 +209,7 @@ export async function preparePassthroughExchange(
|
|
|
198
209
|
| "reserveCredentialHop"
|
|
199
210
|
| "pendingHopPermit"
|
|
200
211
|
| "workflowRootId"
|
|
212
|
+
| "sendsUsed"
|
|
201
213
|
>,
|
|
202
214
|
) {
|
|
203
215
|
const { config, logCtx, options, req } = requestContext;
|
|
@@ -665,6 +677,33 @@ export async function preparePassthroughExchange(
|
|
|
665
677
|
linkAbortSignal(upstream, options.abortSignal);
|
|
666
678
|
const connectMs = config.connectTimeoutMs ?? 200_000;
|
|
667
679
|
let upstreamResponse: Response;
|
|
680
|
+
/**
|
|
681
|
+
* This leg's transient-5xx ladder cap, from the provider's own `transientRetryOn5xx`.
|
|
682
|
+
*
|
|
683
|
+
* The lane used to pass `TRANSIENT_RETRY_MAX_ATTEMPTS` at every call site, so an operator who
|
|
684
|
+
* configured the option on a key-auth `openai-responses` provider changed nothing in either
|
|
685
|
+
* direction, while the same provider on `openai-chat` was tuned normally. That asymmetry is
|
|
686
|
+
* #4893. The gate in `transientRetryPolicyFor` returns null for OAuth and forward providers,
|
|
687
|
+
* so the ChatGPT pool keeps exactly the ladder it has always had.
|
|
688
|
+
*
|
|
689
|
+
* Read per call rather than captured once, for two reasons. `route.provider` is reassigned
|
|
690
|
+
* inside the recovery loop by credential rotation and transport resolution, so a hoisted
|
|
691
|
+
* policy could outlive the provider row it came from. And the configured value is a total for
|
|
692
|
+
* the whole request, so it has to be measured against what the request has already sent at
|
|
693
|
+
* the moment each leg asks.
|
|
694
|
+
*
|
|
695
|
+
* Still bounded by the request: every site feeds this to `remainingTransientSendBudget` or
|
|
696
|
+
* `recoverySendAllowance`, which intersect it with the request-wide base allowance. So a
|
|
697
|
+
* provider can narrow this request's sends exactly and cannot widen the bound that exists to
|
|
698
|
+
* stop per-request amplification (#4546).
|
|
699
|
+
*/
|
|
700
|
+
const transientSendPolicy = () => transientRetryPolicyFor(route.provider);
|
|
701
|
+
const transientSendAttempts = (): number => transientSendCapFor(
|
|
702
|
+
transientSendPolicy()?.attempts,
|
|
703
|
+
sendBudgetState.sendsUsed,
|
|
704
|
+
);
|
|
705
|
+
const configuredTransientSendBudgetExhausted = (): boolean =>
|
|
706
|
+
transientSendPolicy() !== null && transientSendAttempts() === 0;
|
|
668
707
|
/**
|
|
669
708
|
* Refuse a built body that exceeds the operator's configured ceiling, before it is sent.
|
|
670
709
|
*
|
|
@@ -804,7 +843,15 @@ export async function preparePassthroughExchange(
|
|
|
804
843
|
// retry wrapper replaces — proves the host was reached (#914 review).
|
|
805
844
|
.then(adoptObservedResponse);
|
|
806
845
|
},
|
|
807
|
-
{ abortSignal: upstream.signal, label: safeHostLabel(request.url),
|
|
846
|
+
{ abortSignal: upstream.signal, label: safeHostLabel(request.url),
|
|
847
|
+
attempts: remainingTransientSendBudget(transientSendAttempts()), onSendsConsumed: noteTransientSends,
|
|
848
|
+
// The OpenCode Go destination stalls-then-drops inference sends (ambiguous
|
|
849
|
+
// pre-header resets surfacing as refused 429s); its subscription traffic is
|
|
850
|
+
// inference-only, so a bounded reset replay here absorbs the blip instead of
|
|
851
|
+
// failing the turn. Recovery legs keep the fail-closed refusal; only this
|
|
852
|
+
// initial send is replay-eligible. Attempts stay budget-bounded via attempts.
|
|
853
|
+
replaySafe: isOpenCodeGoDestination(route.provider),
|
|
854
|
+
},
|
|
808
855
|
);
|
|
809
856
|
} catch (err) {
|
|
810
857
|
return transportFailureResponse(err);
|
|
@@ -826,7 +873,7 @@ export async function preparePassthroughExchange(
|
|
|
826
873
|
recovery: AttemptRecoveryKind,
|
|
827
874
|
): Promise<Response | { failed: Response }> => {
|
|
828
875
|
const retryAdapter = resolveSelectionAdapter(
|
|
829
|
-
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire),
|
|
876
|
+
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire, route.staticPolicy),
|
|
830
877
|
config.cacheRetention,
|
|
831
878
|
);
|
|
832
879
|
if (!("passthrough" in retryAdapter) || !retryAdapter.passthrough) {
|
|
@@ -864,14 +911,15 @@ export async function preparePassthroughExchange(
|
|
|
864
911
|
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, retryAdapter.name);
|
|
865
912
|
const rebuiltBodyRefusal = refuseOversizedOutboundBody(request);
|
|
866
913
|
if (rebuiltBodyRefusal) return { failed: rebuiltBodyRefusal };
|
|
867
|
-
// The base allowance is spent first
|
|
868
|
-
//
|
|
869
|
-
//
|
|
870
|
-
// outside the try so the finally can hand it back if the leg never
|
|
914
|
+
// The base allowance is spent first. An unconfigured provider may then draw the one shared
|
|
915
|
+
// final-recovery reserve, which keeps a validated sanitized rebuild after a 5xx streak alive
|
|
916
|
+
// at four total sends instead of dying at three. An explicit provider total cannot widen.
|
|
917
|
+
// Reserve outside the try so the finally can hand it back if the leg never reaches its send.
|
|
871
918
|
const allowance = recoverySendAllowance(
|
|
872
|
-
|
|
919
|
+
transientSendAttempts(),
|
|
873
920
|
recoveryClassFor(recovery),
|
|
874
921
|
`${route.providerName}|${route.modelId}|${recovery}`,
|
|
922
|
+
{ allowFinalRecoveryReserve: transientSendPolicy() === null },
|
|
875
923
|
);
|
|
876
924
|
try {
|
|
877
925
|
return await fetchWithTransientRetry(
|
|
@@ -951,7 +999,7 @@ export async function preparePassthroughExchange(
|
|
|
951
999
|
route.provider = replay.provider;
|
|
952
1000
|
requestState.selectedForwardHeaders = withClaudeNativeSession(replay.headers, replay.provider, options.claudeNativeSessionId);
|
|
953
1001
|
const replayAdapter = resolveSelectionAdapter(
|
|
954
|
-
resolveWireProtocolOverride(route.providerName, route.modelId, replay.provider, inboundWire),
|
|
1002
|
+
resolveWireProtocolOverride(route.providerName, route.modelId, replay.provider, inboundWire, route.staticPolicy),
|
|
955
1003
|
config.cacheRetention,
|
|
956
1004
|
);
|
|
957
1005
|
if (!("passthrough" in replayAdapter) || !replayAdapter.passthrough) {
|
|
@@ -1033,7 +1081,7 @@ export async function preparePassthroughExchange(
|
|
|
1033
1081
|
// Refused here, before the 401 body is cancelled: once it is gone the request can only
|
|
1034
1082
|
// answer with a synthetic 502, which would report a proxy budget decision as an upstream
|
|
1035
1083
|
// fault and throw away the credential evidence the client needs.
|
|
1036
|
-
&& !sendBudgetExhausted()
|
|
1084
|
+
&& !sendBudgetExhausted(transientSendAttempts())
|
|
1037
1085
|
) {
|
|
1038
1086
|
oauth401ReplayAttempted = true;
|
|
1039
1087
|
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ }
|
|
@@ -1072,7 +1120,7 @@ export async function preparePassthroughExchange(
|
|
|
1072
1120
|
);
|
|
1073
1121
|
route.provider = refreshedProvider;
|
|
1074
1122
|
const refreshedAdapter = resolveSelectionAdapter(
|
|
1075
|
-
resolveWireProtocolOverride(route.providerName, route.modelId, refreshedProvider, inboundWire),
|
|
1123
|
+
resolveWireProtocolOverride(route.providerName, route.modelId, refreshedProvider, inboundWire, route.staticPolicy),
|
|
1076
1124
|
config.cacheRetention,
|
|
1077
1125
|
);
|
|
1078
1126
|
if (!("passthrough" in refreshedAdapter) || !refreshedAdapter.passthrough) {
|
|
@@ -1134,7 +1182,7 @@ export async function preparePassthroughExchange(
|
|
|
1134
1182
|
route.provider.authMode === "forward")
|
|
1135
1183
|
.then(adoptObservedResponse);
|
|
1136
1184
|
},
|
|
1137
|
-
{ abortSignal: upstream.signal, label: safeHostLabel(request.url), attempts: remainingTransientSendBudget(
|
|
1185
|
+
{ abortSignal: upstream.signal, label: safeHostLabel(request.url), attempts: remainingTransientSendBudget(transientSendAttempts()), onSendsConsumed: noteTransientSends },
|
|
1138
1186
|
);
|
|
1139
1187
|
} catch (err) {
|
|
1140
1188
|
return transportFailureResponse(err);
|
|
@@ -1145,13 +1193,13 @@ export async function preparePassthroughExchange(
|
|
|
1145
1193
|
|
|
1146
1194
|
// Native Responses returns before the generic adapter's OAuth rotation loop. Keep
|
|
1147
1195
|
// the same quorum, cooldown and request budget here, before any client bytes flow.
|
|
1148
|
-
|
|
1149
|
-
|
|
1196
|
+
if (
|
|
1197
|
+
upstreamResponse.status === 429
|
|
1150
1198
|
// Not a provider rate limit when this proxy synthesized it for a refused reset
|
|
1151
1199
|
// replay; rotating accounts on it would re-send an inference that may already
|
|
1152
1200
|
// have run and would cool down an account that refused nothing.
|
|
1153
1201
|
&& !isNonReplayableResponse(upstreamResponse)
|
|
1154
|
-
|
|
1202
|
+
&& transportState.genericFailoverAccountId
|
|
1155
1203
|
&& transportState.genericFailovers < GENERIC_OAUTH_MAX_FAILOVERS_PER_REQUEST
|
|
1156
1204
|
&& isGenericOAuthFailoverEnabled(config, route.providerName)
|
|
1157
1205
|
) {
|
|
@@ -1167,6 +1215,8 @@ export async function preparePassthroughExchange(
|
|
|
1167
1215
|
const nextAccountId = rotateGenericOAuthAccountOn429(
|
|
1168
1216
|
config, route.providerName, transportState.genericFailoverAccountId,
|
|
1169
1217
|
upstreamResponse.headers.get("retry-after"),
|
|
1218
|
+
Date.now(),
|
|
1219
|
+
route.modelId,
|
|
1170
1220
|
);
|
|
1171
1221
|
let snapshot: OAuthAccessSnapshot | undefined;
|
|
1172
1222
|
if (nextAccountId) {
|
|
@@ -1194,6 +1244,11 @@ export async function preparePassthroughExchange(
|
|
|
1194
1244
|
}
|
|
1195
1245
|
// No credential moved, so the reservation costs nothing.
|
|
1196
1246
|
hop.permit?.release();
|
|
1247
|
+
} else {
|
|
1248
|
+
// Rotation was available -- the roster cap above admitted it -- and the shared request
|
|
1249
|
+
// budget refused. Recorded so a one-send log is not read as "nothing was eligible",
|
|
1250
|
+
// which is the ambiguity this attribution exists to remove (#5044).
|
|
1251
|
+
noteAttemptRecoveryWithheld(logCtx.activeAttempt, "rotation-send-budget");
|
|
1197
1252
|
}
|
|
1198
1253
|
}
|
|
1199
1254
|
|
|
@@ -1203,15 +1258,15 @@ export async function preparePassthroughExchange(
|
|
|
1203
1258
|
// immediately with no same-key replay. Pre-stream only — nothing has been relayed yet, so
|
|
1204
1259
|
// the replay is lossless (same invariant as the recovery loop). Forward/OAuth providers
|
|
1205
1260
|
// keep their pool logic below (rateLimitRetryPolicyFor returns null for them).
|
|
1206
|
-
|
|
1207
|
-
|
|
1261
|
+
while (
|
|
1262
|
+
upstreamResponse.status === 429
|
|
1208
1263
|
&& !isNonReplayableResponse(upstreamResponse)
|
|
1209
|
-
|
|
1264
|
+
&& rateLimitPolicy !== null
|
|
1210
1265
|
&& rateLimitRetries < rateLimitPolicy.attempts
|
|
1211
1266
|
// Checked here rather than inside the helper: prepareSameTarget429Wait releases the 429
|
|
1212
1267
|
// body, so a refusal discovered after the wait can no longer return the real rate-limit
|
|
1213
1268
|
// answer and would surface a synthetic 502 instead.
|
|
1214
|
-
&& !sendBudgetExhausted()
|
|
1269
|
+
&& !sendBudgetExhausted(transientSendAttempts())
|
|
1215
1270
|
) {
|
|
1216
1271
|
rateLimitRetries += 1;
|
|
1217
1272
|
// Release unread body + deliberate wait via the shared same-target helper.
|
|
@@ -1259,7 +1314,7 @@ export async function preparePassthroughExchange(
|
|
|
1259
1314
|
route.provider.authMode === "forward")
|
|
1260
1315
|
.then(adoptObservedResponse);
|
|
1261
1316
|
},
|
|
1262
|
-
{ abortSignal: upstream.signal, label: safeHostLabel(request.url), attempts: remainingTransientSendBudget(
|
|
1317
|
+
{ abortSignal: upstream.signal, label: safeHostLabel(request.url), attempts: remainingTransientSendBudget(transientSendAttempts()), onSendsConsumed: noteTransientSends },
|
|
1263
1318
|
);
|
|
1264
1319
|
} catch (err) {
|
|
1265
1320
|
return transportFailureResponse(err);
|
|
@@ -1291,11 +1346,37 @@ export async function preparePassthroughExchange(
|
|
|
1291
1346
|
|
|
1292
1347
|
if (usesCodexForwardPoolAuth(admissionState.authCtx, route.provider)) {
|
|
1293
1348
|
let poolRetryOutcome: number | undefined;
|
|
1294
|
-
|
|
1349
|
+
// A success is the freshest evidence there is about this pair, and it outranks any earlier
|
|
1350
|
+
// refusal: whatever the entitlement was when upstream declined, it is not that now. Both
|
|
1351
|
+
// ids are cleared because the wire model can differ from the routed one.
|
|
1352
|
+
if (upstreamResponse.ok) {
|
|
1353
|
+
clearCodexModelDenialEvidence(
|
|
1354
|
+
admissionState.authCtx.accountId,
|
|
1355
|
+
route.modelId,
|
|
1356
|
+
admissionState.authCtx.kind === "pool" ? admissionState.authCtx.generation : undefined,
|
|
1357
|
+
);
|
|
1358
|
+
clearCodexModelDenialEvidence(
|
|
1359
|
+
admissionState.authCtx.accountId,
|
|
1360
|
+
parsed.modelId,
|
|
1361
|
+
admissionState.authCtx.kind === "pool" ? admissionState.authCtx.generation : undefined,
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
const model400Denial = await codexPoolAccountModel400Denial(
|
|
1295
1365
|
upstreamResponse,
|
|
1296
1366
|
route.modelId,
|
|
1297
1367
|
options.abortSignal,
|
|
1298
|
-
|
|
1368
|
+
parsed.modelId,
|
|
1369
|
+
);
|
|
1370
|
+
if (model400Denial !== undefined) {
|
|
1371
|
+
// Spend this refusal on more than one retry. It is the account's own authenticated
|
|
1372
|
+
// answer about this model, and the roster cache that selection otherwise reads expires
|
|
1373
|
+
// five minutes after a catalog sync fills it -- so without remembering this, the next
|
|
1374
|
+
// request selects the same account on quota alone and takes the same 400 (#4906).
|
|
1375
|
+
recordCodexModelDenialEvidence(
|
|
1376
|
+
admissionState.authCtx.accountId,
|
|
1377
|
+
model400Denial,
|
|
1378
|
+
admissionState.authCtx.kind === "pool" ? admissionState.authCtx.generation : undefined,
|
|
1379
|
+
);
|
|
1299
1380
|
poolRetryOutcome = 400;
|
|
1300
1381
|
} else if (!admissionState.authCtx.fixedAccount && await shouldRetryCodexPoolAccountQuota(
|
|
1301
1382
|
upstreamResponse,
|
|
@@ -1365,18 +1446,20 @@ export async function preparePassthroughExchange(
|
|
|
1365
1446
|
// eviction, or an older transcript). Inspect only a bounded clone of a 4xx whose exact outbound
|
|
1366
1447
|
// Responses body still carries opaque state, then rebuild once through the ordinary adapter
|
|
1367
1448
|
// sanitation path. A second rejection falls through unchanged because the guard stays armed.
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1449
|
+
if (!configuredTransientSendBudgetExhausted()) {
|
|
1450
|
+
const opaqueBlobRecovery = await attemptOpaqueBlobRecovery({
|
|
1451
|
+
response: upstreamResponse,
|
|
1452
|
+
outboundBody: request.body,
|
|
1453
|
+
adapterName: transportState.adapter.name,
|
|
1454
|
+
parsed,
|
|
1455
|
+
guard: opaqueBlobRecoveryGuard,
|
|
1456
|
+
signal: upstream.signal,
|
|
1457
|
+
}, rebuildAndRefetch);
|
|
1458
|
+
if (opaqueBlobRecovery.kind === "failed") return opaqueBlobRecovery.response;
|
|
1459
|
+
if (opaqueBlobRecovery.kind === "recovered") {
|
|
1460
|
+
upstreamResponse = opaqueBlobRecovery.response;
|
|
1461
|
+
continue passthroughRecovery;
|
|
1462
|
+
}
|
|
1380
1463
|
}
|
|
1381
1464
|
|
|
1382
1465
|
const recoveryContentType = upstreamResponse.headers.get("content-type")?.toLowerCase() ?? "";
|
|
@@ -1384,6 +1467,7 @@ export async function preparePassthroughExchange(
|
|
|
1384
1467
|
&& !!upstreamResponse.body
|
|
1385
1468
|
&& (recoveryContentType.includes("text/event-stream") || (!recoveryContentType && parsed.stream))
|
|
1386
1469
|
&& !opaqueBlobRecoveryGuard.attempted
|
|
1470
|
+
&& !configuredTransientSendBudgetExhausted()
|
|
1387
1471
|
&& outboundResponsesBodyCarriesEncryptedFunctionOutput(request.body);
|
|
1388
1472
|
if (streamedFunctionOutputCandidate) {
|
|
1389
1473
|
const preflightLog: RequestLogContext = { model: logCtx.model, provider: logCtx.provider };
|
|
@@ -1391,8 +1475,13 @@ export async function preparePassthroughExchange(
|
|
|
1391
1475
|
payload => {
|
|
1392
1476
|
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return false;
|
|
1393
1477
|
const type = (payload as { type?: unknown }).type;
|
|
1394
|
-
|
|
1395
|
-
&&
|
|
1478
|
+
const decryptRejection = (type === "error" || type === "response.failed" || type === "response.incomplete")
|
|
1479
|
+
&& isEncryptedFunctionOutputRejection(JSON.stringify(payload));
|
|
1480
|
+
// `detail` is a real WebSocket error shape but the generic preflight failure projector
|
|
1481
|
+
// intentionally understands only Responses error/message fields. Preserve only this
|
|
1482
|
+
// exact identity so the projected 502 can enter the existing single-shot recovery.
|
|
1483
|
+
if (decryptRejection) preflightLog.upstreamError = ENCRYPTED_FUNCTION_OUTPUT_REJECTION;
|
|
1484
|
+
return decryptRejection;
|
|
1396
1485
|
}, {
|
|
1397
1486
|
allowMissingContentType: !recoveryContentType && parsed.stream,
|
|
1398
1487
|
replayReadErrors: true,
|
|
@@ -1400,19 +1489,21 @@ export async function preparePassthroughExchange(
|
|
|
1400
1489
|
if (options.abortSignal?.aborted) return transportFailureResponse(options.abortSignal.reason);
|
|
1401
1490
|
upstreamResponse = preflight.response;
|
|
1402
1491
|
if (preflight.kind === "failed") {
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1492
|
+
if (!configuredTransientSendBudgetExhausted()) {
|
|
1493
|
+
const streamedOpaqueRecovery = await attemptOpaqueBlobRecovery({
|
|
1494
|
+
response: upstreamResponse,
|
|
1495
|
+
outboundBody: request.body,
|
|
1496
|
+
adapterName: transportState.adapter.name,
|
|
1497
|
+
parsed,
|
|
1498
|
+
guard: opaqueBlobRecoveryGuard,
|
|
1499
|
+
signal: upstream.signal,
|
|
1500
|
+
}, rebuildAndRefetch);
|
|
1501
|
+
if (streamedOpaqueRecovery.kind === "failed") return streamedOpaqueRecovery.response;
|
|
1502
|
+
if (streamedOpaqueRecovery.kind === "recovered") {
|
|
1503
|
+
resetStreamedOpaqueBlobLogContext(logCtx);
|
|
1504
|
+
upstreamResponse = streamedOpaqueRecovery.response;
|
|
1505
|
+
continue passthroughRecovery;
|
|
1506
|
+
}
|
|
1416
1507
|
}
|
|
1417
1508
|
logCtx.upstreamError = preflightLog.upstreamError;
|
|
1418
1509
|
logCtx.terminalHttpStatus = preflightLog.terminalHttpStatus;
|
|
@@ -1431,6 +1522,7 @@ export async function preparePassthroughExchange(
|
|
|
1431
1522
|
upstream.signal,
|
|
1432
1523
|
);
|
|
1433
1524
|
if (uploadRejectionBody !== undefined
|
|
1525
|
+
&& !configuredTransientSendBudgetExhausted()
|
|
1434
1526
|
&& isTransientConsoleGoUploadRejection({
|
|
1435
1527
|
status: upstreamResponse.status,
|
|
1436
1528
|
errorBody: uploadRejectionBody,
|
|
@@ -1469,7 +1561,7 @@ export async function preparePassthroughExchange(
|
|
|
1469
1561
|
requested: parsed.options.reasoning,
|
|
1470
1562
|
rejectionText,
|
|
1471
1563
|
});
|
|
1472
|
-
if (downgrade) {
|
|
1564
|
+
if (downgrade && !configuredTransientSendBudgetExhausted()) {
|
|
1473
1565
|
reasoningEffortDowngradeGuard.attempted = true;
|
|
1474
1566
|
parsed.options.reasoning = downgrade.effort;
|
|
1475
1567
|
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ }
|
|
@@ -10,6 +10,8 @@ import type { ResponsesEffects } from "./response-effects";
|
|
|
10
10
|
import type { ResponsesSendBudget } from "./request-send-budget";
|
|
11
11
|
import { preparePassthroughExchange } from "./passthrough-dispatch";
|
|
12
12
|
import { deliverPassthroughResponse } from "./passthrough-delivery";
|
|
13
|
+
import { guardDirectPassthroughBodyInactivity } from "../../lib/response-body-inactivity";
|
|
14
|
+
import { resolveStallTimeoutSec } from "../../stall-timeout";
|
|
13
15
|
import { releaseUpstreamHostAdmission } from "../../codex/upstream-host-health";
|
|
14
16
|
import { releaseCodexAuthContextProbeLease } from "../../codex/auth-context";
|
|
15
17
|
|
|
@@ -36,7 +38,7 @@ export async function executePassthroughResponse(
|
|
|
36
38
|
sendBudgetState,
|
|
37
39
|
);
|
|
38
40
|
if (nativeExchange instanceof Response) return nativeExchange;
|
|
39
|
-
|
|
41
|
+
const response = await deliverPassthroughResponse(
|
|
40
42
|
requestContext,
|
|
41
43
|
admissionState,
|
|
42
44
|
requestState,
|
|
@@ -45,6 +47,14 @@ export async function executePassthroughResponse(
|
|
|
45
47
|
responseEffects,
|
|
46
48
|
nativeExchange,
|
|
47
49
|
);
|
|
50
|
+
// Delivery has already classified SSE (including a missing upstream content type)
|
|
51
|
+
// and consumed bounded JSON/errors. Guard only its remaining direct body, outside
|
|
52
|
+
// the relay/lifetime wrappers so their prefetch cannot arm our inactivity clock.
|
|
53
|
+
return guardDirectPassthroughBodyInactivity(
|
|
54
|
+
response,
|
|
55
|
+
nativeExchange.upstream.signal,
|
|
56
|
+
resolveStallTimeoutSec(requestContext.config.stallTimeoutSec) * 1000,
|
|
57
|
+
);
|
|
48
58
|
} finally {
|
|
49
59
|
if (nativeHostState.lease) {
|
|
50
60
|
releaseUpstreamHostAdmission(nativeHostState.lease);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { comboFailureDecision } from "../../combos/failover";
|
|
2
2
|
import { readBoundedResponseBody } from "../../lib/bounded-body";
|
|
3
|
-
import { readJsonRequestBody, resolveInboundBodyLimitBytes } from "../request-decompress";
|
|
4
3
|
import { finishRequestAttempt, type RequestLogContext } from "../request-log";
|
|
5
4
|
import { linkRequestSessionLane } from "../request-log-conversation";
|
|
6
5
|
import type { OcxConfig } from "../../types";
|
|
@@ -129,6 +128,7 @@ export async function handleResponsesWithPolicyFallback(
|
|
|
129
128
|
const runCore = deps.runCore ?? handleResponsesCore;
|
|
130
129
|
let requestBodyReadNotified = false;
|
|
131
130
|
let storedPool401ReplayDispatched = false;
|
|
131
|
+
let rawBody: Record<string, unknown> | null = null;
|
|
132
132
|
const coreOptions: CoreOptions = {
|
|
133
133
|
...options,
|
|
134
134
|
openAiSidecarAuth: options.openAiSidecarAuth === undefined
|
|
@@ -144,23 +144,15 @@ export async function handleResponsesWithPolicyFallback(
|
|
|
144
144
|
options.onRequestBodyRead?.();
|
|
145
145
|
},
|
|
146
146
|
} : {}),
|
|
147
|
+
onRequestBodyParsed: body => {
|
|
148
|
+
options.onRequestBodyParsed?.(body);
|
|
149
|
+
if (body && typeof body === "object" && !Array.isArray(body)) rawBody = body as Record<string, unknown>;
|
|
150
|
+
},
|
|
147
151
|
onStoredPool401ReplayDispatched: () => {
|
|
148
152
|
storedPool401ReplayDispatched = true;
|
|
149
153
|
options.onStoredPool401ReplayDispatched?.();
|
|
150
154
|
},
|
|
151
155
|
};
|
|
152
|
-
let rawBody: Record<string, unknown> | null = null;
|
|
153
|
-
try {
|
|
154
|
-
const parsed = await readJsonRequestBody(
|
|
155
|
-
req.clone(),
|
|
156
|
-
undefined,
|
|
157
|
-
resolveInboundBodyLimitBytes(config.maxInboundBodyBytes),
|
|
158
|
-
);
|
|
159
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) rawBody = parsed as Record<string, unknown>;
|
|
160
|
-
} catch {
|
|
161
|
-
// Core owns the client-facing parse/decompression error.
|
|
162
|
-
}
|
|
163
|
-
|
|
164
156
|
let response: Response;
|
|
165
157
|
try {
|
|
166
158
|
response = await runCore(req, config, logCtx, coreOptions);
|