@bitkyc08/opencodex 2.82.0 → 2.84.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/gui/dist/assets/App-CZtqiGcw.js +56 -0
- package/gui/dist/assets/Tray-C9TGtwad.js +1 -0
- package/gui/dist/assets/{index-BAbcj51J.css → index-BB6uK6tU.css} +1 -1
- package/gui/dist/assets/index-CTm4l-HQ.js +94 -0
- package/gui/dist/assets/{quota-summary-DTBC8n7-.js → quota-summary-BbEeRfPY.js} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +2 -2
- package/src/adapters/coding-agent/protocol.ts +15 -8
- package/src/adapters/cursor/catalog.ts +2 -2
- package/src/adapters/cursor/effort-map.ts +1 -1
- package/src/adapters/devin/live-models.ts +2 -2
- package/src/adapters/google-truncation.ts +22 -0
- package/src/adapters/google.ts +7 -3
- package/src/adapters/ollama-native.ts +47 -15
- package/src/adapters/openai-chat/messages.ts +8 -4
- package/src/adapters/openai-responses/empty-assistant.ts +32 -0
- package/src/adapters/openai-responses/passthrough.ts +5 -0
- package/src/adapters/openai-responses/plaintext-reasoning-notice.ts +85 -0
- package/src/adapters/openai-responses/reasoning.ts +11 -1
- package/src/adapters/openai-responses/request-strips.ts +10 -2
- package/src/adapters/qoder/adapter.ts +16 -14
- package/src/bridge/errors.ts +26 -1
- package/src/bridge.ts +1 -1
- package/src/claude/context-windows.ts +57 -4
- package/src/claude/desktop-picker.ts +1 -1
- package/src/claude/intercept/connect-proxy.ts +10 -3
- package/src/claude/intercept/picker-ca-persistence.ts +3 -1
- package/src/claude/intercept/picker-listener.ts +49 -1
- package/src/claude/intercept/settings-no-proxy.ts +146 -0
- package/src/claude/intercept/settings.ts +101 -9
- package/src/cli/account-api.ts +65 -0
- package/src/cli/account-auth.ts +44 -12
- package/src/cli/account.ts +41 -5
- package/src/cli/capabilities-access-remote.ts +8 -0
- package/src/cli/capabilities-base.ts +13 -1
- package/src/cli/catalog-command-result.ts +2 -1
- package/src/cli/claude.ts +15 -6
- package/src/cli/config-command.ts +8 -2
- package/src/cli/debug.ts +8 -0
- package/src/cli/dispatch.ts +16 -3
- package/src/cli/export-command.ts +14 -1
- package/src/cli/hub-disable.ts +99 -0
- package/src/cli/hub.ts +4 -2
- package/src/cli/index.ts +13 -4
- package/src/cli/link-child.ts +104 -0
- package/src/cli/link-enrollment.ts +96 -0
- package/src/cli/link.ts +39 -24
- package/src/cli/observe-stream.ts +6 -1
- package/src/cli/observe.ts +10 -2
- package/src/cli/provider.ts +12 -0
- package/src/cli/registry.ts +5 -4
- package/src/cli/resolve.ts +1 -1
- package/src/cli/runtime-api.ts +27 -2
- package/src/cli/status.ts +104 -20
- package/src/client/connect.ts +39 -4
- package/src/client/hub-client.ts +14 -7
- package/src/client/hub-state.ts +25 -14
- package/src/client/link-home.ts +248 -0
- package/src/client/link-management-relay.ts +61 -0
- package/src/client/link-relay.ts +6 -1
- package/src/client/link-status.ts +14 -0
- package/src/client/machine-api.ts +14 -7
- package/src/client/machine-listener.ts +29 -4
- package/src/clients/config-export/model-metadata.ts +16 -0
- package/src/clients/config-export/zcode-store.ts +46 -11
- package/src/clients/config-export/zcode.ts +3 -5
- package/src/clients/config-export.ts +47 -17
- package/src/clients/omo-role-models.ts +6 -3
- package/src/codex/agent-role-models.ts +26 -8
- package/src/codex/auth-api/account-list.ts +17 -2
- package/src/codex/auth-api/account-selection.ts +17 -7
- package/src/codex/auth-api/pool-mode-gate.ts +4 -5
- package/src/codex/auth-api/routes.ts +5 -1
- package/src/codex/auth-context.ts +8 -3
- package/src/codex/autostart-health.ts +3 -1
- package/src/codex/catalog/bundled.ts +5 -1
- package/src/codex/catalog/effort.ts +2 -2
- package/src/codex/catalog/metadata.ts +4 -2
- package/src/codex/catalog/native-models.ts +17 -3
- package/src/codex/catalog/provider-models.ts +25 -0
- package/src/codex/desktop-app/darwin.ts +1 -1
- package/src/codex/desktop-app/handoff.ts +1 -1
- package/src/codex/desktop-app/linux.ts +1 -1
- package/src/codex/desktop-app/lock.ts +1 -1
- package/src/codex/desktop-app/types.ts +1 -1
- package/src/codex/desktop-app/windows.ts +1 -1
- package/src/codex/desktop-app-restart.ts +1 -1
- package/src/codex/main-account.ts +6 -7
- package/src/codex/native-profile-startup.ts +4 -1
- package/src/codex/prompt-journal.ts +20 -2
- package/src/codex/prompt-layers/encoding.ts +49 -0
- package/src/codex/prompt-layers/import-source.ts +30 -0
- package/src/codex/prompt-layers/toml-edit.ts +269 -37
- package/src/codex/prompt-layers/toml-read.ts +52 -4
- package/src/codex/prompt-layers.ts +383 -20
- package/src/codex/prompt-lock-claim.ts +13 -3
- package/src/codex/prompt-lock-evidence.ts +3 -3
- package/src/codex/prompt-lock-owner.ts +14 -6
- package/src/codex/prompt-text-probe.ts +34 -5
- package/src/codex/quota.ts +13 -6
- package/src/codex/role-auto-assign.ts +2 -5
- package/src/codex/routing/active-account.ts +21 -0
- package/src/codex/routing.ts +1 -1
- package/src/codex/runtime.ts +196 -8
- package/src/codex/shim-overlay.ts +8 -4
- package/src/codex/shim.ts +1 -1
- package/src/combos/failover.ts +3 -0
- package/src/config/api-key-reconcile.ts +34 -0
- package/src/config/diagnostics.ts +16 -0
- package/src/config/feature-flags.ts +5 -0
- package/src/config/live-reconcile.ts +43 -29
- package/src/config/live-topology.ts +29 -0
- package/src/config/load-degrade.ts +2 -1
- package/src/config/paths.ts +3 -0
- package/src/config/salvage.ts +3 -1
- package/src/config/schema/config-schema.ts +2 -0
- package/src/generated/compatibility-version.json +373 -217
- package/src/grok/grpc-web.ts +23 -1
- package/src/grok/inject.ts +245 -221
- package/src/grok/reset-coupon-ledger.ts +293 -57
- package/src/grok/reset-coupons.ts +15 -6
- package/src/integrations/cursor-detect.ts +1 -1
- package/src/integrations/mutation-plan.ts +13 -7
- package/src/integrations/registry.ts +45 -11
- package/src/integrations/state.ts +19 -7
- package/src/integrations/target.ts +42 -5
- package/src/integrations/writer.ts +14 -6
- package/src/lib/app-owned-memory-stores.ts +4 -0
- package/src/lib/app-owned-memory.ts +11 -0
- package/src/lib/errors.ts +25 -0
- package/src/lib/json-byte-size.ts +31 -0
- package/src/lib/optional-shutdown-hooks.ts +35 -0
- package/src/lib/outbound-credential-registry.ts +210 -0
- package/src/lib/passive-read.ts +35 -0
- package/src/lib/redact.ts +2 -2
- package/src/lib/shadow-call.ts +44 -5
- package/src/link/codex-settings.ts +155 -0
- package/src/link/management-routes.ts +89 -0
- package/src/link/remote-probe.ts +38 -0
- package/src/link/ssh-argv.ts +26 -5
- package/src/link/ssh-runner.ts +58 -27
- package/src/link/status-projection.ts +22 -4
- package/src/link/supervisor.ts +144 -49
- package/src/link/tunnel-state.ts +11 -4
- package/src/oauth/anthropic-continuity.ts +4 -3
- package/src/oauth/anthropic-identity.ts +13 -2
- package/src/oauth/anthropic-model-quota.ts +13 -1
- package/src/oauth/anthropic-routing.ts +230 -23
- package/src/oauth/anthropic.ts +18 -4
- package/src/oauth/google-antigravity.ts +24 -7
- package/src/oauth/index.ts +25 -16
- package/src/oauth/kiro-account-identity.ts +106 -0
- package/src/oauth/kiro.ts +48 -5
- package/src/oauth/meta-muse-device.ts +1 -1
- package/src/oauth/plan.ts +29 -0
- package/src/oauth/store.ts +40 -3
- package/src/oauth/types.ts +3 -1
- package/src/plugins/loader.ts +11 -0
- package/src/providers/anthropic-billing-ownership.ts +74 -0
- package/src/providers/anthropic-billing-recovery.ts +31 -0
- package/src/providers/anthropic-billing-refusal.ts +55 -0
- package/src/providers/anthropic-fast.ts +1 -1
- package/src/providers/anthropic-reset-admission.ts +62 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +29 -7
- package/src/providers/api-key-resolve.ts +17 -2
- package/src/providers/devin-cli-authmode-migration.ts +1 -1
- package/src/providers/devin-provider-merge-migration.ts +1 -1
- package/src/providers/github-copilot-auto.ts +169 -0
- package/src/providers/key-failover.ts +52 -5
- package/src/providers/model-presets.ts +1 -1
- package/src/providers/model-rename-startup.ts +5 -3
- package/src/providers/muse-subscription-usage.ts +1 -1
- package/src/providers/quota/account-cache.ts +11 -0
- package/src/providers/quota/anthropic-account-quota.ts +4 -2
- package/src/providers/quota/report-cache.ts +14 -0
- package/src/providers/quota/vendor-probes-oauth.ts +3 -3
- package/src/providers/quota.ts +4 -1
- package/src/providers/registry/entries-core.ts +20 -20
- package/src/providers/registry/entries-extended.ts +4 -1
- package/src/providers/registry/model-seeds.ts +20 -3
- package/src/providers/request-pacing.ts +9 -0
- package/src/providers/stale-no-vision-seed-migration.ts +72 -0
- package/src/providers/xai-fast-model.ts +1 -1
- package/src/responses/compaction.ts +14 -0
- package/src/responses/parser.ts +7 -1
- package/src/server/auth-cors.ts +42 -0
- package/src/server/configured-credentials.ts +194 -0
- package/src/server/inbound-body-admission.ts +24 -7
- package/src/server/index/link-listener.ts +30 -17
- package/src/server/index/link-management.ts +58 -0
- package/src/server/index/optional-listeners.ts +5 -1
- package/src/server/index/serve-options.ts +21 -2
- package/src/server/index.ts +1 -1
- package/src/server/lifecycle.ts +33 -5
- package/src/server/management/agent-settings-routes.ts +28 -10
- package/src/server/management/anthropic-reset-grant-routes.ts +17 -1
- package/src/server/management/codex-agent-role-routes.ts +19 -6
- package/src/server/management/codex-prompt-routes.ts +136 -5
- package/src/server/management/codex-role-auto-assign.ts +1 -1
- package/src/server/management/grok-coupon-routes.ts +333 -57
- package/src/server/management/link-enrollment.ts +192 -0
- package/src/server/management/link-route-state.ts +185 -0
- package/src/server/management/link-routes.ts +92 -390
- package/src/server/management/model-rows.ts +3 -1
- package/src/server/management/provider-overwrite-carry.ts +10 -0
- package/src/server/management/provider-routes.ts +1 -0
- package/src/server/management/route-registry.ts +7 -5
- package/src/server/management/shared.ts +3 -0
- package/src/server/management-api.ts +3 -1
- package/src/server/messages-native.ts +68 -42
- package/src/server/relay-eager.ts +4 -0
- package/src/server/relay.ts +18 -7
- package/src/server/request-log-failure-attribution.ts +3 -0
- package/src/server/request-log-filter.ts +6 -1
- package/src/server/request-log-terminal-status.ts +243 -0
- package/src/server/request-log.ts +15 -118
- package/src/server/responses/adapter-continuation.ts +9 -1
- package/src/server/responses/adapter-dispatch.ts +293 -81
- package/src/server/responses/code-mode-collab-surface.ts +205 -0
- package/src/server/responses/codex-ws-exchange.ts +147 -6
- package/src/server/responses/codex-ws-wire.ts +203 -0
- package/src/server/responses/collaboration.ts +36 -7
- package/src/server/responses/compact.ts +129 -50
- package/src/server/responses/context-overflow.ts +13 -1
- package/src/server/responses/core-codex-account.ts +105 -1
- package/src/server/responses/core-combo-failure.ts +14 -0
- package/src/server/responses/core-combo.ts +7 -2
- package/src/server/responses/core-errors.ts +29 -1
- package/src/server/responses/core-opaque-recovery.ts +5 -5
- package/src/server/responses/core-options.ts +2 -0
- package/src/server/responses/core.ts +6 -6
- package/src/server/responses/deferred-collab-surface.ts +158 -0
- package/src/server/responses/deferred-send-reporter.ts +25 -0
- package/src/server/responses/empty-completion-guard.ts +18 -2
- package/src/server/responses/fetch-helpers.ts +12 -1
- package/src/server/responses/jev-model-invoke.ts +1 -1
- package/src/server/responses/passthrough-delivery.ts +4 -1
- package/src/server/responses/passthrough-dispatch.ts +130 -64
- package/src/server/responses/passthrough-execution.ts +3 -3
- package/src/server/responses/request-prepare.ts +6 -4
- package/src/server/responses/request-transport.ts +131 -13
- package/src/server/responses/run-turn-execution.ts +13 -5
- package/src/server/responses/sidecar-execution.ts +16 -2
- package/src/server/responses/spend-storage-error.ts +33 -0
- package/src/server/responses/ws-upstream.ts +9 -1
- package/src/server/startup-action-control.ts +22 -2
- package/src/server/startup-health-cache.ts +4 -0
- package/src/server/system-env-shell.ts +7 -2
- package/src/server/system-env.ts +11 -1
- package/src/service/cli.ts +5 -1
- package/src/service/desktop-startup.ts +147 -34
- package/src/service/desktop-supervision.d.mts +27 -1
- package/src/service/desktop-supervision.mjs +111 -5
- package/src/service/health-sentinel-launch.ts +176 -0
- package/src/service/health-sentinel-main.ts +447 -0
- package/src/service/health-sentinel-policy.ts +241 -0
- package/src/service/health-sentinel-probe.ts +54 -0
- package/src/service/health-sentinel-windows.ts +87 -0
- package/src/service/linux-linger.ts +20 -0
- package/src/types/config.ts +5 -0
- package/src/types/request.ts +5 -0
- package/src/usage/expected-prices.ts +5 -1
- package/src/usage/log.ts +19 -1
- package/gui/dist/assets/App-Ds_qL7o8.js +0 -54
- package/gui/dist/assets/Tray-BqHF5Trd.js +0 -1
- package/gui/dist/assets/index-D3FBCNiP.js +0 -94
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { spendLedgerStorageErrorResponse } from "./spend-storage-error";
|
|
1
2
|
import { createSteeringSettingsNormalizer } from "./native-steering-policy";
|
|
2
3
|
import { nativeResponseControlEligible } from "./native-response-control";
|
|
3
4
|
import { NativeInjectionReplay } from "./native-injection-replay";
|
|
@@ -8,7 +9,7 @@ import type {
|
|
|
8
9
|
PassthroughAdmissionState,
|
|
9
10
|
} from "./core-options";
|
|
10
11
|
import type { PreparedResponsesRequest } from "./request-prepare";
|
|
11
|
-
import type
|
|
12
|
+
import { CopilotAutoAdapterHandoff, type ResponsesTransport } from "./request-transport";
|
|
12
13
|
import type { ResponsesEffects } from "./response-effects";
|
|
13
14
|
import type { ResponsesSendBudget } from "./request-send-budget";
|
|
14
15
|
import { transientSendCapFor } from "./request-send-budget";
|
|
@@ -90,7 +91,7 @@ import {
|
|
|
90
91
|
safeHostLabel,
|
|
91
92
|
} from "./fetch-helpers";
|
|
92
93
|
import { classifyPoolRecoveryDispatch } from "../../routing/probe-lease";
|
|
93
|
-
import { clientCancelledResponse } from "./core-errors";
|
|
94
|
+
import { anthropicPoolCooldownResponse, clientCancelledResponse } from "./core-errors";
|
|
94
95
|
import {
|
|
95
96
|
upstreamHostCircuitOpenResponse,
|
|
96
97
|
usesCodexForwardPoolAuth,
|
|
@@ -100,6 +101,8 @@ import {
|
|
|
100
101
|
shouldRetryCodexPoolAccountQuota,
|
|
101
102
|
shouldRetryCodexPoolAccountTransient,
|
|
102
103
|
retryCodexPoolOnAlternateAccount,
|
|
104
|
+
PoolPreOutputRefusals,
|
|
105
|
+
poolPreOutputRefusalHold,
|
|
103
106
|
} from "./core-codex-account";
|
|
104
107
|
import {
|
|
105
108
|
clearCodexModelDenialEvidence,
|
|
@@ -166,6 +169,9 @@ import { isTransientConsoleGoUploadRejection } from "../../providers/opencode-ze
|
|
|
166
169
|
import { planReasoningEffortDowngrade } from "../../providers/reasoning-metadata";
|
|
167
170
|
import { unboundPoolSpendRefusalResponse } from "../workflow-refusal";
|
|
168
171
|
|
|
172
|
+
/** Transfer an uncommitted Copilot exchange to the existing translated pipeline without new admission. */
|
|
173
|
+
export interface PassthroughAdapterHandoff { kind: "adapter-handoff" }
|
|
174
|
+
|
|
169
175
|
/** Prepares and recovers one native Responses exchange before client commitment. */
|
|
170
176
|
export async function preparePassthroughExchange(
|
|
171
177
|
requestContext: Pick<ResponsesRequestContext, "config" | "logCtx" | "options" | "req">,
|
|
@@ -192,6 +198,12 @@ export async function preparePassthroughExchange(
|
|
|
192
198
|
| "passiveQuotaWriterGeneration"
|
|
193
199
|
| "oauthDispatch"
|
|
194
200
|
| "resolveSelectionAdapter"
|
|
201
|
+
| "resolveCopilotSelection"
|
|
202
|
+
| "copilotRefusalResponse"
|
|
203
|
+
| "oauth401ReplayAttempted"
|
|
204
|
+
| "activeAdapter"
|
|
205
|
+
| "runTurnAdapter"
|
|
206
|
+
| "invalidateSameTargetRequest"
|
|
195
207
|
| "isOAuth401ReplayProvider"
|
|
196
208
|
| "sentOAuthSnapshot"
|
|
197
209
|
| "refreshResolvedOAuthSelection"
|
|
@@ -248,6 +260,8 @@ export async function preparePassthroughExchange(
|
|
|
248
260
|
passiveQuotaWriterGeneration,
|
|
249
261
|
oauthDispatch,
|
|
250
262
|
resolveSelectionAdapter,
|
|
263
|
+
resolveCopilotSelection,
|
|
264
|
+
copilotRefusalResponse,
|
|
251
265
|
isOAuth401ReplayProvider,
|
|
252
266
|
refreshResolvedOAuthSelection,
|
|
253
267
|
applyFailoverSnapshot,
|
|
@@ -732,7 +746,7 @@ export async function preparePassthroughExchange(
|
|
|
732
746
|
// consumer's cancel to a signalled fetch, so we pass the signal and relay through relayWithAbort,
|
|
733
747
|
// whose cancel() aborts the upstream — preventing leaked connections (RC2, passthrough path).
|
|
734
748
|
const upstream = new AbortController();
|
|
735
|
-
linkAbortSignal(upstream, options.abortSignal);
|
|
749
|
+
const cleanupUpstreamAbort = linkAbortSignal(upstream, options.abortSignal ?? req.signal);
|
|
736
750
|
const connectMs = config.connectTimeoutMs ?? 200_000;
|
|
737
751
|
let upstreamResponse: Response;
|
|
738
752
|
/**
|
|
@@ -822,7 +836,7 @@ export async function preparePassthroughExchange(
|
|
|
822
836
|
httpOnly: true,
|
|
823
837
|
providerName: route.providerName,
|
|
824
838
|
modelId: route.modelId,
|
|
825
|
-
dispatchOverride: oauthDispatch(request),
|
|
839
|
+
dispatchOverride: oauthDispatch(request, parsed, "passthrough"),
|
|
826
840
|
beforeDispatch: headers => {
|
|
827
841
|
if (signal.aborted) throw signal.reason;
|
|
828
842
|
if (!transportState.selectionIsCurrent(transportState.requestBindings.get(request))) {
|
|
@@ -896,9 +910,10 @@ export async function preparePassthroughExchange(
|
|
|
896
910
|
describeOutboundBodyRefusal(result),
|
|
897
911
|
);
|
|
898
912
|
};
|
|
899
|
-
const transportFailureResponse = (err: unknown): Response => {
|
|
913
|
+
const transportFailureResponse = (err: unknown): Response | PassthroughAdapterHandoff => {
|
|
914
|
+
cleanupUpstreamAbort();
|
|
900
915
|
upstream.abort();
|
|
901
|
-
if (options.abortSignal?.aborted) {
|
|
916
|
+
if (options.abortSignal?.aborted || req.signal.aborted) {
|
|
902
917
|
releaseUpstreamHostAdmission(nativeHostState.lease);
|
|
903
918
|
nativeHostState.lease = null;
|
|
904
919
|
releaseCodexAuthContextProbeLease(admissionState.authCtx);
|
|
@@ -915,6 +930,17 @@ export async function preparePassthroughExchange(
|
|
|
915
930
|
?? formatErrorResponse(429, "request_send_budget_exhausted", err.message);
|
|
916
931
|
}
|
|
917
932
|
const refusal = unwrapUpstreamRetryEvidenceError(err);
|
|
933
|
+
if (refusal instanceof CopilotAutoAdapterHandoff) {
|
|
934
|
+
bindRefreshedAdapter(refusal.adapter);
|
|
935
|
+
return handoffToChat(refusal.adapter);
|
|
936
|
+
}
|
|
937
|
+
const copilotRefusal = copilotRefusalResponse(refusal);
|
|
938
|
+
if (copilotRefusal) {
|
|
939
|
+
releaseUpstreamHostAdmission(nativeHostState.lease);
|
|
940
|
+
nativeHostState.lease = null;
|
|
941
|
+
releaseCodexAuthContextProbeLease(admissionState.authCtx);
|
|
942
|
+
return copilotRefusal;
|
|
943
|
+
}
|
|
918
944
|
// Pacing may outlive the selected account's admission. No fetch occurred, so do
|
|
919
945
|
// not turn an operator pause into a 502 or charge it to host/account health.
|
|
920
946
|
if (refusal instanceof OAuthAccountPausedError || refusal instanceof OAuthLoginRequiredError || refusal instanceof AnthropicAccountCooldownError) {
|
|
@@ -925,12 +951,11 @@ export async function preparePassthroughExchange(
|
|
|
925
951
|
return formatErrorResponse(401, "authentication_error", publicOAuthAuthenticationErrorMessage(refusal));
|
|
926
952
|
}
|
|
927
953
|
if (refusal instanceof AnthropicAccountCooldownError) {
|
|
928
|
-
return
|
|
929
|
-
refusal.retryAfterSeconds === null ? undefined : { retryAfter: String(refusal.retryAfterSeconds) });
|
|
954
|
+
return anthropicPoolCooldownResponse(refusal, { comboAttempt: options.comboAttempt, logCtx });
|
|
930
955
|
}
|
|
931
956
|
return formatErrorResponse(403, "permission_error", publicOAuthAuthenticationErrorMessage(refusal));
|
|
932
957
|
}
|
|
933
|
-
const localRefusal = mapCodexAuthContextErrorToResponse(refusal, {
|
|
958
|
+
const localRefusal = spendLedgerStorageErrorResponse(err, logCtx) ?? mapCodexAuthContextErrorToResponse(refusal, {
|
|
934
959
|
now: Date.now(), accountSelector: route.codexAccountNamespace,
|
|
935
960
|
});
|
|
936
961
|
if (localRefusal) {
|
|
@@ -981,6 +1006,9 @@ export async function preparePassthroughExchange(
|
|
|
981
1006
|
// Stable per request: the Combo booking (`reserveDispatch`) already started and froze the spend
|
|
982
1007
|
// policy, so `spendEnforced` cannot change before dispatch even though no reporter starts it here.
|
|
983
1008
|
const receiptMode = !sendBudgetState.adapterSendBudget?.spendEnforced && Boolean(options.comboInitialSend);
|
|
1009
|
+
// #6969: Pool sends hold empty lifecycle frames so a pre-output refusal reaches the ladder below.
|
|
1010
|
+
const preOutputRefusals = new PoolPreOutputRefusals();
|
|
1011
|
+
const preOutputRefusalHold = () => poolPreOutputRefusalHold(admissionState.authCtx, route.provider, connectMs, options.comboAttempt);
|
|
984
1012
|
try {
|
|
985
1013
|
// Transient-5xx pre-stream retry (devlog/_plan/260716_claudecode_hardening/010):
|
|
986
1014
|
// the ChatGPT backend emits transient 502/520s that an immediate retry absorbs.
|
|
@@ -1004,18 +1032,19 @@ export async function preparePassthroughExchange(
|
|
|
1004
1032
|
nativeControl: nativeResponseControlEligible(route.provider, options.nativeControl) && options.inboundTransport === "websocket" && !options.comboAttempt
|
|
1005
1033
|
&& responseEffects.plaintextV2AgentMessageToolNames.size === 0
|
|
1006
1034
|
? options.nativeControl : undefined,
|
|
1007
|
-
dispatchOverride: oauthDispatch(request),
|
|
1035
|
+
dispatchOverride: oauthDispatch(request, parsed, "passthrough"),
|
|
1008
1036
|
providerName: route.providerName,
|
|
1009
1037
|
modelId: route.modelId,
|
|
1010
1038
|
onPhysicalDispatch: receiptMode ? sendBudgetState.noteInitialDispatch : undefined,
|
|
1011
1039
|
onCodexWsQuota: codexWsQuotaObserver(admissionState.authCtx, route.provider, route.modelId),
|
|
1012
1040
|
beforeDispatch: isCanonicalOpenAiForwardProvider(route.provider)
|
|
1013
1041
|
? createCodexAuthDispatchGuard(admissionState.authCtx, options.codexAuthPolicy ?? config, route.modelId, options.admission, options.visionDescribeTerminal === true) : undefined,
|
|
1042
|
+
preOutputRefusalHold: preOutputRefusalHold(),
|
|
1014
1043
|
}),
|
|
1015
1044
|
route.provider.authMode === "forward")
|
|
1016
1045
|
// Every real attempt response — including an intermediate 5xx the
|
|
1017
1046
|
// retry wrapper replaces — proves the host was reached (#914 review).
|
|
1018
|
-
.then(adoptObservedResponse);
|
|
1047
|
+
.then(adoptObservedResponse).then(preOutputRefusals.observe);
|
|
1019
1048
|
},
|
|
1020
1049
|
{ abortSignal: upstream.signal, label: safeHostLabel(request.url),
|
|
1021
1050
|
attempts: sendBudgetState.initialSendAllowance(transientSendAttempts()),
|
|
@@ -1026,6 +1055,8 @@ export async function preparePassthroughExchange(
|
|
|
1026
1055
|
},
|
|
1027
1056
|
);
|
|
1028
1057
|
} catch (err) {
|
|
1058
|
+
preOutputRefusals.recordOnLocalExit(err, options.abortSignal?.aborted === true || req.signal.aborted,
|
|
1059
|
+
config, admissionState.authCtx, route.provider, route.modelId);
|
|
1029
1060
|
return transportFailureResponse(err);
|
|
1030
1061
|
} finally {
|
|
1031
1062
|
request.releaseBodyObservation?.();
|
|
@@ -1034,7 +1065,33 @@ export async function preparePassthroughExchange(
|
|
|
1034
1065
|
const opaqueBlobRecoveryGuard: OpaqueBlobRecoveryGuard = { attempted: false };
|
|
1035
1066
|
// At most one reasoning-effort downgrade per request.
|
|
1036
1067
|
const reasoningEffortDowngradeGuard: { attempted: boolean } = { attempted: false };
|
|
1037
|
-
|
|
1068
|
+
/** Rebind the refreshed wire before transferring this request to the Chat pipeline. */
|
|
1069
|
+
function bindRefreshedAdapter(refreshedAdapter: typeof transportState.adapter) {
|
|
1070
|
+
bindRouteReasoningReplayScope({
|
|
1071
|
+
parsed,
|
|
1072
|
+
providerName: route.providerName,
|
|
1073
|
+
provider: route.provider,
|
|
1074
|
+
adapterName: refreshedAdapter.name,
|
|
1075
|
+
oauthCredentialSnapshot: transportState.replayOAuthCredentialSnapshot,
|
|
1076
|
+
});
|
|
1077
|
+
logCtx.providerAdapter = refreshedAdapter.name;
|
|
1078
|
+
sealRequestAttemptIdentity(
|
|
1079
|
+
logCtx.activeAttempt,
|
|
1080
|
+
logCtx.provider,
|
|
1081
|
+
refreshedAdapter.name,
|
|
1082
|
+
logCtx.accountLogLabel,
|
|
1083
|
+
);
|
|
1084
|
+
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, refreshedAdapter.name);
|
|
1085
|
+
}
|
|
1086
|
+
/** Transfer adapter ownership without retaining the native abort link or request cache. */
|
|
1087
|
+
function handoffToChat(refreshedAdapter: typeof transportState.adapter) {
|
|
1088
|
+
cleanupUpstreamAbort();
|
|
1089
|
+
upstream.abort();
|
|
1090
|
+
transportState.adapter = transportState.activeAdapter = transportState.runTurnAdapter = refreshedAdapter;
|
|
1091
|
+
transportState.invalidateSameTargetRequest();
|
|
1092
|
+
return { kind: "adapter-handoff" as const };
|
|
1093
|
+
}
|
|
1094
|
+
let oauth401ReplayAttempted = transportState.oauth401ReplayAttempted;
|
|
1038
1095
|
let codex401ReplayKind: "main" | "stored" | null = null;
|
|
1039
1096
|
// Console Go answers a transient 400 "Invalid upload request." for bodies it accepts
|
|
1040
1097
|
// moments later; at most one byte-identical replay is allowed per request.
|
|
@@ -1043,7 +1100,7 @@ export async function preparePassthroughExchange(
|
|
|
1043
1100
|
let rateLimitRetries = 0;
|
|
1044
1101
|
const rebuildAndRefetch = async (
|
|
1045
1102
|
recovery: AttemptRecoveryKind,
|
|
1046
|
-
): Promise<Response | { failed: Response }> => {
|
|
1103
|
+
): Promise<Response | { failed: Response | PassthroughAdapterHandoff }> => {
|
|
1047
1104
|
const retryAdapter = resolveSelectionAdapter(
|
|
1048
1105
|
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire, route.staticPolicy),
|
|
1049
1106
|
config.cacheRetention,
|
|
@@ -1113,7 +1170,7 @@ export async function preparePassthroughExchange(
|
|
|
1113
1170
|
nativeControl: nativeResponseControlEligible(route.provider, options.nativeControl) && options.inboundTransport === "websocket" && !options.comboAttempt
|
|
1114
1171
|
&& responseEffects.plaintextV2AgentMessageToolNames.size === 0
|
|
1115
1172
|
? options.nativeControl : undefined,
|
|
1116
|
-
dispatchOverride: oauthDispatch(request),
|
|
1173
|
+
dispatchOverride: oauthDispatch(request, parsed, "passthrough"),
|
|
1117
1174
|
providerName: route.providerName,
|
|
1118
1175
|
modelId: route.modelId,
|
|
1119
1176
|
onCodexWsQuota: codexWsQuotaObserver(admissionState.authCtx, route.provider, route.modelId),
|
|
@@ -1220,7 +1277,7 @@ export async function preparePassthroughExchange(
|
|
|
1220
1277
|
nativeControl: nativeResponseControlEligible(route.provider, options.nativeControl) && options.inboundTransport === "websocket" && !options.comboAttempt
|
|
1221
1278
|
&& responseEffects.plaintextV2AgentMessageToolNames.size === 0
|
|
1222
1279
|
? options.nativeControl : undefined,
|
|
1223
|
-
dispatchOverride: oauthDispatch(request),
|
|
1280
|
+
dispatchOverride: oauthDispatch(request, parsed, "passthrough"),
|
|
1224
1281
|
providerName: route.providerName,
|
|
1225
1282
|
modelId: route.modelId,
|
|
1226
1283
|
onCodexWsQuota: codexWsQuotaObserver(admissionState.authCtx, route.provider, route.modelId),
|
|
@@ -1282,7 +1339,7 @@ export async function preparePassthroughExchange(
|
|
|
1282
1339
|
// fault and throw away the credential evidence the client needs.
|
|
1283
1340
|
&& !sendBudgetExhausted(transientSendAttempts())
|
|
1284
1341
|
) {
|
|
1285
|
-
oauth401ReplayAttempted = true;
|
|
1342
|
+
oauth401ReplayAttempted = transportState.oauth401ReplayAttempted = true;
|
|
1286
1343
|
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ }
|
|
1287
1344
|
let refreshed: OAuthAccessSnapshot;
|
|
1288
1345
|
try {
|
|
@@ -1305,7 +1362,7 @@ export async function preparePassthroughExchange(
|
|
|
1305
1362
|
if (route.providerName === "kiro") {
|
|
1306
1363
|
parsed._kiroAuthContext = { ...(refreshed.kiro ?? {}) };
|
|
1307
1364
|
}
|
|
1308
|
-
|
|
1365
|
+
let refreshedProvider = resolveProviderTransport(
|
|
1309
1366
|
route.providerName,
|
|
1310
1367
|
{
|
|
1311
1368
|
...route.provider,
|
|
@@ -1318,29 +1375,21 @@ export async function preparePassthroughExchange(
|
|
|
1318
1375
|
: undefined,
|
|
1319
1376
|
);
|
|
1320
1377
|
route.provider = refreshedProvider;
|
|
1378
|
+
try { await resolveCopilotSelection(parsed); }
|
|
1379
|
+
catch (err) { return transportFailureResponse(err); }
|
|
1380
|
+
refreshedProvider = route.provider;
|
|
1321
1381
|
const refreshedAdapter = resolveSelectionAdapter(
|
|
1322
1382
|
resolveWireProtocolOverride(route.providerName, route.modelId, refreshedProvider, inboundWire, route.staticPolicy),
|
|
1323
1383
|
config.cacheRetention,
|
|
1324
1384
|
);
|
|
1385
|
+
bindRefreshedAdapter(refreshedAdapter);
|
|
1325
1386
|
if (!("passthrough" in refreshedAdapter) || !refreshedAdapter.passthrough) {
|
|
1387
|
+
if (route.providerName === "github-copilot" && refreshedAdapter.name === "openai-chat") {
|
|
1388
|
+
return handoffToChat(refreshedAdapter);
|
|
1389
|
+
}
|
|
1326
1390
|
upstream.abort();
|
|
1327
1391
|
return formatErrorResponse(502, "upstream_error", "OAuth refresh changed the provider wire unexpectedly");
|
|
1328
1392
|
}
|
|
1329
|
-
bindRouteReasoningReplayScope({
|
|
1330
|
-
parsed,
|
|
1331
|
-
providerName: route.providerName,
|
|
1332
|
-
provider: refreshedProvider,
|
|
1333
|
-
adapterName: refreshedAdapter.name,
|
|
1334
|
-
oauthCredentialSnapshot: transportState.replayOAuthCredentialSnapshot,
|
|
1335
|
-
});
|
|
1336
|
-
logCtx.providerAdapter = refreshedAdapter.name;
|
|
1337
|
-
sealRequestAttemptIdentity(
|
|
1338
|
-
logCtx.activeAttempt,
|
|
1339
|
-
logCtx.provider,
|
|
1340
|
-
refreshedAdapter.name,
|
|
1341
|
-
logCtx.accountLogLabel,
|
|
1342
|
-
);
|
|
1343
|
-
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, refreshedAdapter.name);
|
|
1344
1393
|
try {
|
|
1345
1394
|
request = await refreshedAdapter.buildRequest(parsed, {
|
|
1346
1395
|
headers: requestState.selectedForwardHeaders,
|
|
@@ -1373,7 +1422,7 @@ export async function preparePassthroughExchange(
|
|
|
1373
1422
|
nativeControl: nativeResponseControlEligible(route.provider, options.nativeControl) && options.inboundTransport === "websocket" && !options.comboAttempt
|
|
1374
1423
|
&& responseEffects.plaintextV2AgentMessageToolNames.size === 0
|
|
1375
1424
|
? options.nativeControl : undefined,
|
|
1376
|
-
dispatchOverride: oauthDispatch(request),
|
|
1425
|
+
dispatchOverride: oauthDispatch(request, parsed, "passthrough"),
|
|
1377
1426
|
providerName: route.providerName,
|
|
1378
1427
|
modelId: route.modelId,
|
|
1379
1428
|
onCodexWsQuota: codexWsQuotaObserver(admissionState.authCtx, route.provider, route.modelId),
|
|
@@ -1414,38 +1463,50 @@ export async function preparePassthroughExchange(
|
|
|
1414
1463
|
true,
|
|
1415
1464
|
);
|
|
1416
1465
|
if (hop.allowed) {
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
if (nextAccountId) {
|
|
1425
|
-
try { snapshot = await failoverAccountSnapshot(route.providerName, nextAccountId); }
|
|
1426
|
-
catch { /* Keep the original 429 body readable when the next credential is unavailable. */ }
|
|
1427
|
-
}
|
|
1428
|
-
if (snapshot && await applyFailoverSnapshot(snapshot)) {
|
|
1429
|
-
transportState.genericFailovers += 1;
|
|
1430
|
-
route.provider = resolveProviderTransport(
|
|
1431
|
-
route.providerName, route.provider, parsed.options.promptCacheKey, transportState.sentOAuthSnapshot?.apiBaseUrl,
|
|
1466
|
+
let transferred = false;
|
|
1467
|
+
try {
|
|
1468
|
+
const nextAccountId = rotateGenericOAuthAccountOn429(
|
|
1469
|
+
config, route.providerName, transportState.genericFailoverAccountId,
|
|
1470
|
+
upstreamResponse.headers.get("retry-after"),
|
|
1471
|
+
Date.now(),
|
|
1472
|
+
route.modelId,
|
|
1432
1473
|
);
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1474
|
+
let snapshot: OAuthAccessSnapshot | undefined;
|
|
1475
|
+
if (nextAccountId) {
|
|
1476
|
+
try { snapshot = await failoverAccountSnapshot(route.providerName, nextAccountId); }
|
|
1477
|
+
catch { /* Keep the original 429 body readable when the next credential is unavailable. */ }
|
|
1478
|
+
}
|
|
1479
|
+
if (snapshot && await applyFailoverSnapshot(snapshot)) {
|
|
1480
|
+
transportState.genericFailovers += 1;
|
|
1481
|
+
route.provider = resolveProviderTransport(
|
|
1482
|
+
route.providerName, route.provider, parsed.options.promptCacheKey, transportState.sentOAuthSnapshot?.apiBaseUrl,
|
|
1483
|
+
);
|
|
1484
|
+
const rotatedAdapter = resolveSelectionAdapter(
|
|
1485
|
+
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire, route.staticPolicy),
|
|
1486
|
+
config.cacheRetention,
|
|
1487
|
+
);
|
|
1488
|
+
bindRefreshedAdapter(rotatedAdapter);
|
|
1489
|
+
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already closed */ }
|
|
1490
|
+
sendBudgetState.pendingHopPermit = hop.permit;
|
|
1491
|
+
if (route.providerName === "github-copilot" && rotatedAdapter.name === "openai-chat") {
|
|
1492
|
+
transferred = true;
|
|
1493
|
+
return handoffToChat(rotatedAdapter);
|
|
1494
|
+
}
|
|
1495
|
+
const result = await rebuildAndRefetch("oauth-account-429");
|
|
1496
|
+
sendBudgetState.pendingHopPermit = undefined;
|
|
1497
|
+
if ("failed" in result) return result.failed;
|
|
1498
|
+
upstreamResponse = result;
|
|
1499
|
+
continue passthroughRecovery;
|
|
1500
|
+
}
|
|
1501
|
+
} catch (err) {
|
|
1437
1502
|
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already closed */ }
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
upstreamResponse = result;
|
|
1445
|
-
continue passthroughRecovery;
|
|
1503
|
+
return transportFailureResponse(err);
|
|
1504
|
+
} finally {
|
|
1505
|
+
if (!transferred) {
|
|
1506
|
+
if (sendBudgetState.pendingHopPermit === hop.permit) sendBudgetState.pendingHopPermit = undefined;
|
|
1507
|
+
hop.permit?.release();
|
|
1508
|
+
}
|
|
1446
1509
|
}
|
|
1447
|
-
// No credential moved, so the reservation costs nothing.
|
|
1448
|
-
hop.permit?.release();
|
|
1449
1510
|
} else {
|
|
1450
1511
|
// The activation quorum ignores cooldowns; prove that the selector has a live alternate
|
|
1451
1512
|
// before describing this as a recovery that only the shared request budget withheld.
|
|
@@ -1508,7 +1569,7 @@ export async function preparePassthroughExchange(
|
|
|
1508
1569
|
nativeControl: nativeResponseControlEligible(route.provider, options.nativeControl) && options.inboundTransport === "websocket" && !options.comboAttempt
|
|
1509
1570
|
&& responseEffects.plaintextV2AgentMessageToolNames.size === 0
|
|
1510
1571
|
? options.nativeControl : undefined,
|
|
1511
|
-
dispatchOverride: oauthDispatch(request),
|
|
1572
|
+
dispatchOverride: oauthDispatch(request, parsed, "passthrough"),
|
|
1512
1573
|
providerName: route.providerName,
|
|
1513
1574
|
modelId: route.modelId,
|
|
1514
1575
|
onCodexWsQuota: codexWsQuotaObserver(admissionState.authCtx, route.provider, route.modelId),
|
|
@@ -1622,6 +1683,7 @@ export async function preparePassthroughExchange(
|
|
|
1622
1683
|
passthroughEstimate,
|
|
1623
1684
|
stream: upstreamRequestsStream,
|
|
1624
1685
|
httpOnly: canonicalBufferedJson,
|
|
1686
|
+
preOutputRefusalHold: preOutputRefusalHold(),
|
|
1625
1687
|
onResponse: (response, retryAuthCtx, retryRequest) => {
|
|
1626
1688
|
adoptCodexWsStage(response);
|
|
1627
1689
|
captureAffinityResponse(
|
|
@@ -1648,6 +1710,8 @@ export async function preparePassthroughExchange(
|
|
|
1648
1710
|
}
|
|
1649
1711
|
}
|
|
1650
1712
|
}
|
|
1713
|
+
// #6969: a projection nothing above recovered goes on as the original stream, before any reader.
|
|
1714
|
+
upstreamResponse = preOutputRefusals.restore(upstreamResponse);
|
|
1651
1715
|
// The deterministic route record cannot classify history it never observed (restart, expiry,
|
|
1652
1716
|
// eviction, or an older transcript). Inspect only a bounded clone of a 4xx whose exact outbound
|
|
1653
1717
|
// Responses body still carries opaque state, then rebuild once through the ordinary adapter
|
|
@@ -1875,6 +1939,7 @@ export async function preparePassthroughExchange(
|
|
|
1875
1939
|
const localUpstream = isLocalUpstream(route.provider.baseUrl);
|
|
1876
1940
|
|
|
1877
1941
|
return {
|
|
1942
|
+
kind: undefined,
|
|
1878
1943
|
codexSafetyBufferingOptions,
|
|
1879
1944
|
imageGenCallAliases,
|
|
1880
1945
|
routedCustomToolNames,
|
|
@@ -1914,7 +1979,8 @@ export async function preparePassthroughExchange(
|
|
|
1914
1979
|
connectMs,
|
|
1915
1980
|
localUpstream,
|
|
1916
1981
|
upstreamResponse,
|
|
1982
|
+
terminalOutcomeRecorded: preOutputRefusals.outcomeRecorded,
|
|
1917
1983
|
};
|
|
1918
1984
|
}
|
|
1919
1985
|
|
|
1920
|
-
export type PassthroughExchange = Exclude<Awaited<ReturnType<typeof preparePassthroughExchange>>, Response>;
|
|
1986
|
+
export type PassthroughExchange = Exclude<Awaited<ReturnType<typeof preparePassthroughExchange>>, Response | PassthroughAdapterHandoff>;
|
|
@@ -8,7 +8,7 @@ import type { ResponsesTransport } from "./request-transport";
|
|
|
8
8
|
import type { ResponsesSidecarAuth } from "./request-sidecar-auth";
|
|
9
9
|
import type { ResponsesEffects } from "./response-effects";
|
|
10
10
|
import type { ResponsesSendBudget } from "./request-send-budget";
|
|
11
|
-
import { preparePassthroughExchange } from "./passthrough-dispatch";
|
|
11
|
+
import { preparePassthroughExchange, type PassthroughAdapterHandoff } from "./passthrough-dispatch";
|
|
12
12
|
import { deliverPassthroughResponse } from "./passthrough-delivery";
|
|
13
13
|
import { guardDirectPassthroughBodyInactivity } from "../../lib/response-body-inactivity";
|
|
14
14
|
import { resolveStallTimeoutMs } from "../../stall-timeout";
|
|
@@ -24,7 +24,7 @@ export async function executePassthroughResponse(
|
|
|
24
24
|
sidecarState: ResponsesSidecarAuth,
|
|
25
25
|
responseEffects: ResponsesEffects,
|
|
26
26
|
sendBudgetState: ResponsesSendBudget,
|
|
27
|
-
): Promise<Response> {
|
|
27
|
+
): Promise<Response | PassthroughAdapterHandoff> {
|
|
28
28
|
const nativeHostState: PassthroughAdmissionState = { lease: admissionState.pendingHostAdmissionLease };
|
|
29
29
|
admissionState.pendingHostAdmissionLease = null;
|
|
30
30
|
try {
|
|
@@ -37,7 +37,7 @@ export async function executePassthroughResponse(
|
|
|
37
37
|
responseEffects,
|
|
38
38
|
sendBudgetState,
|
|
39
39
|
);
|
|
40
|
-
if (nativeExchange instanceof Response) return nativeExchange;
|
|
40
|
+
if (nativeExchange instanceof Response || nativeExchange.kind === "adapter-handoff") return nativeExchange;
|
|
41
41
|
const response = await deliverPassthroughResponse(
|
|
42
42
|
requestContext,
|
|
43
43
|
admissionState,
|
|
@@ -33,6 +33,7 @@ import { resolveContextPrincipal } from "../auth-cors";
|
|
|
33
33
|
import { resolveSkillsSnapshotScopeKey, snapshotSkillsCatalogInBody } from "./skills-snapshot";
|
|
34
34
|
import {
|
|
35
35
|
isShadowSourceModel,
|
|
36
|
+
shadowCallPreservesSelectedModel,
|
|
36
37
|
shadowSourceModelPrefix,
|
|
37
38
|
shouldInterceptShadowCall,
|
|
38
39
|
} from "../../lib/shadow-call";
|
|
@@ -275,10 +276,11 @@ export async function prepareResponsesRequest(
|
|
|
275
276
|
}
|
|
276
277
|
}
|
|
277
278
|
let shadowCallIntercepted = false;
|
|
278
|
-
//
|
|
279
|
-
//
|
|
279
|
+
// Luna is also selectable by users/children. Both intercept sites use the same
|
|
280
|
+
// per-turn provenance; WS frames must not inherit the handshake's classification.
|
|
280
281
|
const threadSpawn = isThreadSpawnRequest(req.headers);
|
|
281
|
-
|
|
282
|
+
const preserveShadowModel = shadowCallPreservesSelectedModel(body, req.headers, options.inboundTransport);
|
|
283
|
+
if (!concreteSelection && !options.compactionRoutingOverride && !options.internalDecisionCall && !preserveShadowModel && !memoryModelApplies && body && typeof body === "object" && !Array.isArray(body)) {
|
|
282
284
|
const shadowIntercept = config.shadowCallIntercept;
|
|
283
285
|
const rawShadowModel = (body as { model?: unknown }).model;
|
|
284
286
|
if (shadowIntercept?.enabled && shadowIntercept.model && typeof rawShadowModel === "string"
|
|
@@ -613,7 +615,7 @@ export async function prepareResponsesRequest(
|
|
|
613
615
|
}
|
|
614
616
|
const _sci = config.shadowCallIntercept;
|
|
615
617
|
let shadowRoute: RouteResult | undefined;
|
|
616
|
-
if (!concreteSelection && !memoryRoute && !options.memoryModelPhase && !options.compactionRoutingOverride && !options.internalDecisionCall && !
|
|
618
|
+
if (!concreteSelection && !memoryRoute && !options.memoryModelPhase && !options.compactionRoutingOverride && !options.internalDecisionCall && !preserveShadowModel && _sci?.enabled && _sci.model && isShadowSourceModel(parsed.modelId, _sci.sourceModels)) {
|
|
617
619
|
const sourcePrefix = shadowSourceModelPrefix(parsed.modelId, _sci.sourceModels)!;
|
|
618
620
|
let sourceIdentity = { providerName: OPENAI_CODEX_PROVIDER_ID, modelId: sourcePrefix };
|
|
619
621
|
try {
|