@bitkyc08/opencodex 2.81.0 → 2.83.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/bin/ocx.mjs +166 -73
- package/gui/dist/assets/App-Ba8uG696.js +54 -0
- package/gui/dist/assets/{Tray-EwAOsP0S.js → Tray-IImbNPRy.js} +1 -1
- package/gui/dist/assets/{index-Brk6F3fn.css → index-BAbcj51J.css} +1 -1
- package/gui/dist/assets/{index-RsoaAxXY.js → index-BHRZdJuv.js} +13 -13
- package/gui/dist/assets/{quota-summary-D2lN8tDm.js → quota-summary-DiJO6sYY.js} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic.ts +1 -1
- 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 +18 -14
- package/src/adapters/openai-responses/compaction-search-history.ts +109 -0
- package/src/adapters/openai-responses/passthrough.ts +11 -4
- 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/tool-output-recovery.ts +20 -0
- package/src/bridge/errors.ts +7 -1
- package/src/claude/agents-inject.ts +14 -3
- package/src/claude/context-windows.ts +22 -20
- package/src/claude/desktop-3p.ts +88 -12
- package/src/claude/desktop-picker.ts +1 -1
- package/src/claude/desktop-profile.ts +2 -1
- package/src/claude/gateway-cache.ts +18 -1
- package/src/claude/inbound.ts +77 -3
- package/src/claude/intercept/cli-catalog.ts +3 -0
- package/src/claude/intercept/picker-ca-persistence.ts +7 -6
- 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-auth.ts +20 -12
- package/src/cli/agent.ts +9 -5
- package/src/cli/aside-profile-recovery.ts +33 -0
- package/src/cli/capabilities-base.ts +5 -5
- package/src/cli/capabilities-integrations.ts +2 -2
- package/src/cli/cli-path-diagnostics.ts +132 -0
- package/src/cli/desktop-runtime-guidance.ts +65 -0
- package/src/cli/dispatch.ts +5 -0
- package/src/cli/doctor.ts +16 -3
- package/src/cli/index.ts +14 -9
- package/src/cli/integration-aside-sync.ts +2 -1
- package/src/cli/integrations.ts +16 -3
- package/src/cli/resolve.ts +26 -3
- 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/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 +1 -1
- package/src/clients/lazycodex.ts +1 -1
- package/src/clients/omo-role-models.ts +32 -7
- package/src/codex/auth-api/account-selection.ts +107 -0
- package/src/codex/auth-api/pool-mode-gate.ts +4 -5
- package/src/codex/auth-api/routes.ts +10 -49
- package/src/codex/auth-context.ts +28 -4
- package/src/codex/autostart-health.ts +12 -3
- package/src/codex/catalog/build-entries.ts +18 -0
- package/src/codex/catalog/bundled.ts +155 -26
- package/src/codex/catalog/combo-member.ts +3 -2
- package/src/codex/catalog/effort.ts +26 -5
- package/src/codex/catalog/gather-capture.ts +5 -0
- package/src/codex/catalog/model-hints.ts +15 -8
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/native-models.ts +1 -1
- package/src/codex/catalog/operator-tiers.ts +38 -0
- package/src/codex/catalog/provider-models.ts +68 -35
- 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/convergence.ts +2 -1
- 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/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/main-account.ts +6 -7
- 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-service-exchange.ts +281 -0
- package/src/combos/jev.ts +34 -220
- package/src/config/atomic-write.ts +1 -1
- package/src/config/codex-ws-reuse-setting.ts +10 -0
- package/src/config/live-reconcile.ts +17 -0
- package/src/config/load-degrade.ts +8 -0
- package/src/config/rebase-provenance.ts +11 -0
- package/src/config/schema/config-schema.ts +4 -0
- package/src/generated/compatibility-version.json +375 -227
- package/src/generated/model-metadata.ts +2 -2
- package/src/grok/inject.ts +276 -206
- package/src/grok/reset-coupon-ledger.ts +223 -57
- package/src/grok/reset-coupons.ts +13 -6
- package/src/grok/sync.ts +2 -1
- package/src/images/loop.ts +29 -11
- package/src/integrations/aside-profiles.ts +10 -3
- package/src/integrations/catalog-refresh.ts +18 -2
- package/src/integrations/cursor-detect.ts +1 -1
- package/src/integrations/writer.ts +9 -3
- 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/errors.ts +25 -0
- package/src/lib/redact.ts +2 -2
- package/src/lib/request-execution-budget.ts +8 -0
- 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/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/oauth/anthropic-account-refusal.ts +39 -1
- package/src/oauth/anthropic-routing.ts +1 -1
- package/src/oauth/anthropic-send-ownership.ts +3 -3
- package/src/oauth/chatgpt.ts +113 -41
- package/src/oauth/index.ts +7 -3
- package/src/oauth/meta-muse-device.ts +1 -1
- package/src/oauth/store.ts +1 -1
- package/src/oauth/types.ts +1 -1
- package/src/providers/anthropic-fast.ts +1 -1
- package/src/providers/anthropic-reset-grant-ledger.ts +1 -1
- package/src/providers/azure-model-metadata.ts +115 -0
- package/src/providers/derive.ts +21 -0
- 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/model-presets.ts +1 -1
- package/src/providers/muse-subscription-usage.ts +1 -1
- package/src/providers/quota/vendor-probes-key.ts +127 -17
- package/src/providers/quota.ts +1 -1
- package/src/providers/registry/entries-core.ts +6 -6
- package/src/providers/registry/entries-extended.ts +4 -0
- package/src/providers/registry/model-seeds.ts +3 -3
- package/src/providers/xai-fast-model.ts +1 -1
- package/src/responses/reasoning-envelope.ts +29 -1
- package/src/responses/reasoning-replay-cache.ts +17 -1
- package/src/responses/spill-store.ts +15 -2
- package/src/responses/state/metrics.ts +8 -0
- package/src/responses/state/spill-failure.ts +12 -0
- package/src/responses/state/spill-queue.ts +12 -5
- package/src/responses/state.ts +18 -10
- package/src/responses/task-input.ts +17 -0
- package/src/server/background-lifecycle.ts +27 -0
- package/src/server/chat-completions.ts +1 -0
- package/src/server/chat-native.ts +28 -12
- package/src/server/claude-messages.ts +43 -6
- package/src/server/inbound-body-admission.ts +25 -7
- package/src/server/index/claude-intercept-lifecycle.ts +3 -1
- package/src/server/index/link-listener.ts +30 -17
- package/src/server/index/serve-options.ts +18 -4
- package/src/server/index/spend-ledger-lifecycle.ts +2 -1
- package/src/server/index/startup-warnings.ts +19 -0
- package/src/server/index.ts +2 -1
- package/src/server/lifecycle.ts +31 -4
- package/src/server/management/agent-settings-routes.ts +93 -32
- 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 +59 -8
- package/src/server/management/context.ts +2 -2
- package/src/server/management/grok-coupon-routes.ts +277 -52
- package/src/server/management/link-routes.ts +42 -20
- package/src/server/management/route-registry.ts +2 -2
- package/src/server/management/shared.ts +3 -3
- package/src/server/messages-native-oauth.ts +9 -8
- package/src/server/messages-native.ts +49 -13
- package/src/server/messages-response-headers.ts +14 -0
- package/src/server/relay-eager.ts +2 -0
- package/src/server/relay.ts +18 -7
- package/src/server/request-log-conversation.ts +48 -5
- package/src/server/request-log-failure-attribution.ts +3 -0
- package/src/server/request-log-terminal-status.ts +186 -0
- package/src/server/request-log.ts +15 -89
- package/src/server/responses/adapter-continuation.ts +11 -3
- package/src/server/responses/adapter-dispatch.ts +99 -19
- 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 +3 -2
- package/src/server/responses/core-codex-account.ts +22 -1
- package/src/server/responses/core-combo-native.ts +20 -3
- package/src/server/responses/core-combo.ts +22 -14
- package/src/server/responses/core-lifetime.ts +3 -36
- package/src/server/responses/core-opaque-recovery.ts +5 -5
- package/src/server/responses/core-options.ts +5 -0
- package/src/server/responses/core-replay.ts +73 -0
- package/src/server/responses/core.ts +9 -9
- package/src/server/responses/empty-completion-guard.ts +18 -2
- package/src/server/responses/fetch-helpers.ts +26 -3
- package/src/server/responses/jev-model-invoke.ts +1 -1
- package/src/server/responses/passthrough-delivery.ts +24 -2
- package/src/server/responses/passthrough-dispatch.ts +134 -62
- package/src/server/responses/passthrough-execution.ts +3 -3
- package/src/server/responses/request-prepare.ts +4 -0
- package/src/server/responses/request-send-budget.ts +112 -49
- package/src/server/responses/request-transport.ts +125 -5
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +15 -5
- package/src/server/responses/sidecar-execution.ts +30 -16
- package/src/server/responses/sidecar-send-budget.ts +58 -0
- package/src/server/responses/spend-storage-error.ts +33 -0
- package/src/server/responses/ws-upstream.ts +19 -6
- package/src/server/startup-health-cache.ts +2 -1
- package/src/server/workflow-refusal.ts +3 -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/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/storage/policy-job.ts +5 -1
- package/src/storage/policy-worker.ts +3 -1
- package/src/storage/policy.ts +9 -0
- package/src/types/config.ts +21 -0
- package/src/types/request.ts +10 -0
- package/src/types/wire.ts +1 -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/expected-prices.ts +1 -1
- package/src/web-search/loop.ts +5 -2
- package/gui/dist/assets/App-7yhoys8a.js +0 -54
|
@@ -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,10 +9,11 @@ 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";
|
|
16
|
+
import { renewMainQuotaDispatchForAttempt } from "../../codex/main-account-cache";
|
|
15
17
|
import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
|
|
16
18
|
import { isLocalUpstream } from "../../lib/local-upstream";
|
|
17
19
|
import { codexSafetyBufferingFilterOptions, terminalStatusFromParsed } from "../relay";
|
|
@@ -165,6 +167,9 @@ import { isTransientConsoleGoUploadRejection } from "../../providers/opencode-ze
|
|
|
165
167
|
import { planReasoningEffortDowngrade } from "../../providers/reasoning-metadata";
|
|
166
168
|
import { unboundPoolSpendRefusalResponse } from "../workflow-refusal";
|
|
167
169
|
|
|
170
|
+
/** Transfer an uncommitted Copilot exchange to the existing translated pipeline without new admission. */
|
|
171
|
+
export interface PassthroughAdapterHandoff { kind: "adapter-handoff" }
|
|
172
|
+
|
|
168
173
|
/** Prepares and recovers one native Responses exchange before client commitment. */
|
|
169
174
|
export async function preparePassthroughExchange(
|
|
170
175
|
requestContext: Pick<ResponsesRequestContext, "config" | "logCtx" | "options" | "req">,
|
|
@@ -191,6 +196,12 @@ export async function preparePassthroughExchange(
|
|
|
191
196
|
| "passiveQuotaWriterGeneration"
|
|
192
197
|
| "oauthDispatch"
|
|
193
198
|
| "resolveSelectionAdapter"
|
|
199
|
+
| "resolveCopilotSelection"
|
|
200
|
+
| "copilotRefusalResponse"
|
|
201
|
+
| "oauth401ReplayAttempted"
|
|
202
|
+
| "activeAdapter"
|
|
203
|
+
| "runTurnAdapter"
|
|
204
|
+
| "invalidateSameTargetRequest"
|
|
194
205
|
| "isOAuth401ReplayProvider"
|
|
195
206
|
| "sentOAuthSnapshot"
|
|
196
207
|
| "refreshResolvedOAuthSelection"
|
|
@@ -225,6 +236,10 @@ export async function preparePassthroughExchange(
|
|
|
225
236
|
| "pendingHopPermit"
|
|
226
237
|
| "workflowRootId"
|
|
227
238
|
| "sendsUsed"
|
|
239
|
+
| "targetSendsUsed"
|
|
240
|
+
| "initialSendAllowance"
|
|
241
|
+
| "noteInitialDispatch"
|
|
242
|
+
| "adapterSendBudget"
|
|
228
243
|
>,
|
|
229
244
|
) {
|
|
230
245
|
const { config, logCtx, options, req } = requestContext;
|
|
@@ -243,6 +258,8 @@ export async function preparePassthroughExchange(
|
|
|
243
258
|
passiveQuotaWriterGeneration,
|
|
244
259
|
oauthDispatch,
|
|
245
260
|
resolveSelectionAdapter,
|
|
261
|
+
resolveCopilotSelection,
|
|
262
|
+
copilotRefusalResponse,
|
|
246
263
|
isOAuth401ReplayProvider,
|
|
247
264
|
refreshResolvedOAuthSelection,
|
|
248
265
|
applyFailoverSnapshot,
|
|
@@ -727,7 +744,7 @@ export async function preparePassthroughExchange(
|
|
|
727
744
|
// consumer's cancel to a signalled fetch, so we pass the signal and relay through relayWithAbort,
|
|
728
745
|
// whose cancel() aborts the upstream — preventing leaked connections (RC2, passthrough path).
|
|
729
746
|
const upstream = new AbortController();
|
|
730
|
-
linkAbortSignal(upstream, options.abortSignal);
|
|
747
|
+
const cleanupUpstreamAbort = linkAbortSignal(upstream, options.abortSignal ?? req.signal);
|
|
731
748
|
const connectMs = config.connectTimeoutMs ?? 200_000;
|
|
732
749
|
let upstreamResponse: Response;
|
|
733
750
|
/**
|
|
@@ -753,7 +770,7 @@ export async function preparePassthroughExchange(
|
|
|
753
770
|
const transientSendPolicy = () => transientRetryPolicyFor(route.provider);
|
|
754
771
|
const transientSendAttempts = (): number => transientSendCapFor(
|
|
755
772
|
transientSendPolicy()?.attempts,
|
|
756
|
-
sendBudgetState.
|
|
773
|
+
sendBudgetState.targetSendsUsed,
|
|
757
774
|
);
|
|
758
775
|
const configuredTransientSendBudgetExhausted = (): boolean =>
|
|
759
776
|
transientSendPolicy() !== null && transientSendAttempts() === 0;
|
|
@@ -798,6 +815,9 @@ export async function preparePassthroughExchange(
|
|
|
798
815
|
const sendAmbiguousReplacement = (
|
|
799
816
|
signal: AbortSignal = upstream.signal,
|
|
800
817
|
): Promise<Response> => {
|
|
818
|
+
if (admissionState.authCtx.kind === "main" && admissionState.authCtx.mainQuotaDispatch) {
|
|
819
|
+
admissionState.authCtx.mainQuotaDispatch = renewMainQuotaDispatchForAttempt(admissionState.authCtx.mainQuotaDispatch);
|
|
820
|
+
}
|
|
801
821
|
const report = transientSendReporter();
|
|
802
822
|
let started = false;
|
|
803
823
|
const run = () => fetchWithHeaderTimeout(
|
|
@@ -814,7 +834,7 @@ export async function preparePassthroughExchange(
|
|
|
814
834
|
httpOnly: true,
|
|
815
835
|
providerName: route.providerName,
|
|
816
836
|
modelId: route.modelId,
|
|
817
|
-
dispatchOverride: oauthDispatch(request),
|
|
837
|
+
dispatchOverride: oauthDispatch(request, parsed, "passthrough"),
|
|
818
838
|
beforeDispatch: headers => {
|
|
819
839
|
if (signal.aborted) throw signal.reason;
|
|
820
840
|
if (!transportState.selectionIsCurrent(transportState.requestBindings.get(request))) {
|
|
@@ -888,9 +908,10 @@ export async function preparePassthroughExchange(
|
|
|
888
908
|
describeOutboundBodyRefusal(result),
|
|
889
909
|
);
|
|
890
910
|
};
|
|
891
|
-
const transportFailureResponse = (err: unknown): Response => {
|
|
911
|
+
const transportFailureResponse = (err: unknown): Response | PassthroughAdapterHandoff => {
|
|
912
|
+
cleanupUpstreamAbort();
|
|
892
913
|
upstream.abort();
|
|
893
|
-
if (options.abortSignal?.aborted) {
|
|
914
|
+
if (options.abortSignal?.aborted || req.signal.aborted) {
|
|
894
915
|
releaseUpstreamHostAdmission(nativeHostState.lease);
|
|
895
916
|
nativeHostState.lease = null;
|
|
896
917
|
releaseCodexAuthContextProbeLease(admissionState.authCtx);
|
|
@@ -907,6 +928,17 @@ export async function preparePassthroughExchange(
|
|
|
907
928
|
?? formatErrorResponse(429, "request_send_budget_exhausted", err.message);
|
|
908
929
|
}
|
|
909
930
|
const refusal = unwrapUpstreamRetryEvidenceError(err);
|
|
931
|
+
if (refusal instanceof CopilotAutoAdapterHandoff) {
|
|
932
|
+
bindRefreshedAdapter(refusal.adapter);
|
|
933
|
+
return handoffToChat(refusal.adapter);
|
|
934
|
+
}
|
|
935
|
+
const copilotRefusal = copilotRefusalResponse(refusal);
|
|
936
|
+
if (copilotRefusal) {
|
|
937
|
+
releaseUpstreamHostAdmission(nativeHostState.lease);
|
|
938
|
+
nativeHostState.lease = null;
|
|
939
|
+
releaseCodexAuthContextProbeLease(admissionState.authCtx);
|
|
940
|
+
return copilotRefusal;
|
|
941
|
+
}
|
|
910
942
|
// Pacing may outlive the selected account's admission. No fetch occurred, so do
|
|
911
943
|
// not turn an operator pause into a 502 or charge it to host/account health.
|
|
912
944
|
if (refusal instanceof OAuthAccountPausedError || refusal instanceof OAuthLoginRequiredError || refusal instanceof AnthropicAccountCooldownError) {
|
|
@@ -922,7 +954,7 @@ export async function preparePassthroughExchange(
|
|
|
922
954
|
}
|
|
923
955
|
return formatErrorResponse(403, "permission_error", publicOAuthAuthenticationErrorMessage(refusal));
|
|
924
956
|
}
|
|
925
|
-
const localRefusal = mapCodexAuthContextErrorToResponse(refusal, {
|
|
957
|
+
const localRefusal = spendLedgerStorageErrorResponse(err, logCtx) ?? mapCodexAuthContextErrorToResponse(refusal, {
|
|
926
958
|
now: Date.now(), accountSelector: route.codexAccountNamespace,
|
|
927
959
|
});
|
|
928
960
|
if (localRefusal) {
|
|
@@ -968,6 +1000,11 @@ export async function preparePassthroughExchange(
|
|
|
968
1000
|
};
|
|
969
1001
|
const initialBodyRefusal = refuseOversizedOutboundBody(request);
|
|
970
1002
|
if (initialBodyRefusal) return initialBodyRefusal;
|
|
1003
|
+
// A combo-owned first send is settled by the physical-dispatch receipt unless spend
|
|
1004
|
+
// enforcement is on, where the shared reporter already owns the permit lifecycle.
|
|
1005
|
+
// Stable per request: the Combo booking (`reserveDispatch`) already started and froze the spend
|
|
1006
|
+
// policy, so `spendEnforced` cannot change before dispatch even though no reporter starts it here.
|
|
1007
|
+
const receiptMode = !sendBudgetState.adapterSendBudget?.spendEnforced && Boolean(options.comboInitialSend);
|
|
971
1008
|
try {
|
|
972
1009
|
// Transient-5xx pre-stream retry (devlog/_plan/260716_claudecode_hardening/010):
|
|
973
1010
|
// the ChatGPT backend emits transient 502/520s that an immediate retry absorbs.
|
|
@@ -991,9 +1028,10 @@ export async function preparePassthroughExchange(
|
|
|
991
1028
|
nativeControl: nativeResponseControlEligible(route.provider, options.nativeControl) && options.inboundTransport === "websocket" && !options.comboAttempt
|
|
992
1029
|
&& responseEffects.plaintextV2AgentMessageToolNames.size === 0
|
|
993
1030
|
? options.nativeControl : undefined,
|
|
994
|
-
dispatchOverride: oauthDispatch(request),
|
|
1031
|
+
dispatchOverride: oauthDispatch(request, parsed, "passthrough"),
|
|
995
1032
|
providerName: route.providerName,
|
|
996
1033
|
modelId: route.modelId,
|
|
1034
|
+
onPhysicalDispatch: receiptMode ? sendBudgetState.noteInitialDispatch : undefined,
|
|
997
1035
|
onCodexWsQuota: codexWsQuotaObserver(admissionState.authCtx, route.provider, route.modelId),
|
|
998
1036
|
beforeDispatch: isCanonicalOpenAiForwardProvider(route.provider)
|
|
999
1037
|
? createCodexAuthDispatchGuard(admissionState.authCtx, options.codexAuthPolicy ?? config, route.modelId, options.admission, options.visionDescribeTerminal === true) : undefined,
|
|
@@ -1004,7 +1042,10 @@ export async function preparePassthroughExchange(
|
|
|
1004
1042
|
.then(adoptObservedResponse);
|
|
1005
1043
|
},
|
|
1006
1044
|
{ abortSignal: upstream.signal, label: safeHostLabel(request.url),
|
|
1007
|
-
attempts:
|
|
1045
|
+
attempts: sendBudgetState.initialSendAllowance(transientSendAttempts()),
|
|
1046
|
+
// Reporter path: direct requests and enforced spend. Receipt-mode sends are settled
|
|
1047
|
+
// by `onPhysicalDispatch`; a reporter as well would count them twice.
|
|
1048
|
+
...(receiptMode ? {} : { onSendsConsumed: transientSendReporter() }),
|
|
1008
1049
|
claimAmbiguousResend: claimPreHeaderResend,
|
|
1009
1050
|
},
|
|
1010
1051
|
);
|
|
@@ -1017,7 +1058,33 @@ export async function preparePassthroughExchange(
|
|
|
1017
1058
|
const opaqueBlobRecoveryGuard: OpaqueBlobRecoveryGuard = { attempted: false };
|
|
1018
1059
|
// At most one reasoning-effort downgrade per request.
|
|
1019
1060
|
const reasoningEffortDowngradeGuard: { attempted: boolean } = { attempted: false };
|
|
1020
|
-
|
|
1061
|
+
/** Rebind the refreshed wire before transferring this request to the Chat pipeline. */
|
|
1062
|
+
function bindRefreshedAdapter(refreshedAdapter: typeof transportState.adapter) {
|
|
1063
|
+
bindRouteReasoningReplayScope({
|
|
1064
|
+
parsed,
|
|
1065
|
+
providerName: route.providerName,
|
|
1066
|
+
provider: route.provider,
|
|
1067
|
+
adapterName: refreshedAdapter.name,
|
|
1068
|
+
oauthCredentialSnapshot: transportState.replayOAuthCredentialSnapshot,
|
|
1069
|
+
});
|
|
1070
|
+
logCtx.providerAdapter = refreshedAdapter.name;
|
|
1071
|
+
sealRequestAttemptIdentity(
|
|
1072
|
+
logCtx.activeAttempt,
|
|
1073
|
+
logCtx.provider,
|
|
1074
|
+
refreshedAdapter.name,
|
|
1075
|
+
logCtx.accountLogLabel,
|
|
1076
|
+
);
|
|
1077
|
+
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, refreshedAdapter.name);
|
|
1078
|
+
}
|
|
1079
|
+
/** Transfer adapter ownership without retaining the native abort link or request cache. */
|
|
1080
|
+
function handoffToChat(refreshedAdapter: typeof transportState.adapter) {
|
|
1081
|
+
cleanupUpstreamAbort();
|
|
1082
|
+
upstream.abort();
|
|
1083
|
+
transportState.adapter = transportState.activeAdapter = transportState.runTurnAdapter = refreshedAdapter;
|
|
1084
|
+
transportState.invalidateSameTargetRequest();
|
|
1085
|
+
return { kind: "adapter-handoff" as const };
|
|
1086
|
+
}
|
|
1087
|
+
let oauth401ReplayAttempted = transportState.oauth401ReplayAttempted;
|
|
1021
1088
|
let codex401ReplayKind: "main" | "stored" | null = null;
|
|
1022
1089
|
// Console Go answers a transient 400 "Invalid upload request." for bodies it accepts
|
|
1023
1090
|
// moments later; at most one byte-identical replay is allowed per request.
|
|
@@ -1026,7 +1093,7 @@ export async function preparePassthroughExchange(
|
|
|
1026
1093
|
let rateLimitRetries = 0;
|
|
1027
1094
|
const rebuildAndRefetch = async (
|
|
1028
1095
|
recovery: AttemptRecoveryKind,
|
|
1029
|
-
): Promise<Response | { failed: Response }> => {
|
|
1096
|
+
): Promise<Response | { failed: Response | PassthroughAdapterHandoff }> => {
|
|
1030
1097
|
const retryAdapter = resolveSelectionAdapter(
|
|
1031
1098
|
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire, route.staticPolicy),
|
|
1032
1099
|
config.cacheRetention,
|
|
@@ -1096,7 +1163,7 @@ export async function preparePassthroughExchange(
|
|
|
1096
1163
|
nativeControl: nativeResponseControlEligible(route.provider, options.nativeControl) && options.inboundTransport === "websocket" && !options.comboAttempt
|
|
1097
1164
|
&& responseEffects.plaintextV2AgentMessageToolNames.size === 0
|
|
1098
1165
|
? options.nativeControl : undefined,
|
|
1099
|
-
dispatchOverride: oauthDispatch(request),
|
|
1166
|
+
dispatchOverride: oauthDispatch(request, parsed, "passthrough"),
|
|
1100
1167
|
providerName: route.providerName,
|
|
1101
1168
|
modelId: route.modelId,
|
|
1102
1169
|
onCodexWsQuota: codexWsQuotaObserver(admissionState.authCtx, route.provider, route.modelId),
|
|
@@ -1203,7 +1270,7 @@ export async function preparePassthroughExchange(
|
|
|
1203
1270
|
nativeControl: nativeResponseControlEligible(route.provider, options.nativeControl) && options.inboundTransport === "websocket" && !options.comboAttempt
|
|
1204
1271
|
&& responseEffects.plaintextV2AgentMessageToolNames.size === 0
|
|
1205
1272
|
? options.nativeControl : undefined,
|
|
1206
|
-
dispatchOverride: oauthDispatch(request),
|
|
1273
|
+
dispatchOverride: oauthDispatch(request, parsed, "passthrough"),
|
|
1207
1274
|
providerName: route.providerName,
|
|
1208
1275
|
modelId: route.modelId,
|
|
1209
1276
|
onCodexWsQuota: codexWsQuotaObserver(admissionState.authCtx, route.provider, route.modelId),
|
|
@@ -1265,7 +1332,7 @@ export async function preparePassthroughExchange(
|
|
|
1265
1332
|
// fault and throw away the credential evidence the client needs.
|
|
1266
1333
|
&& !sendBudgetExhausted(transientSendAttempts())
|
|
1267
1334
|
) {
|
|
1268
|
-
oauth401ReplayAttempted = true;
|
|
1335
|
+
oauth401ReplayAttempted = transportState.oauth401ReplayAttempted = true;
|
|
1269
1336
|
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ }
|
|
1270
1337
|
let refreshed: OAuthAccessSnapshot;
|
|
1271
1338
|
try {
|
|
@@ -1288,7 +1355,7 @@ export async function preparePassthroughExchange(
|
|
|
1288
1355
|
if (route.providerName === "kiro") {
|
|
1289
1356
|
parsed._kiroAuthContext = { ...(refreshed.kiro ?? {}) };
|
|
1290
1357
|
}
|
|
1291
|
-
|
|
1358
|
+
let refreshedProvider = resolveProviderTransport(
|
|
1292
1359
|
route.providerName,
|
|
1293
1360
|
{
|
|
1294
1361
|
...route.provider,
|
|
@@ -1301,29 +1368,21 @@ export async function preparePassthroughExchange(
|
|
|
1301
1368
|
: undefined,
|
|
1302
1369
|
);
|
|
1303
1370
|
route.provider = refreshedProvider;
|
|
1371
|
+
try { await resolveCopilotSelection(parsed); }
|
|
1372
|
+
catch (err) { return transportFailureResponse(err); }
|
|
1373
|
+
refreshedProvider = route.provider;
|
|
1304
1374
|
const refreshedAdapter = resolveSelectionAdapter(
|
|
1305
1375
|
resolveWireProtocolOverride(route.providerName, route.modelId, refreshedProvider, inboundWire, route.staticPolicy),
|
|
1306
1376
|
config.cacheRetention,
|
|
1307
1377
|
);
|
|
1378
|
+
bindRefreshedAdapter(refreshedAdapter);
|
|
1308
1379
|
if (!("passthrough" in refreshedAdapter) || !refreshedAdapter.passthrough) {
|
|
1380
|
+
if (route.providerName === "github-copilot" && refreshedAdapter.name === "openai-chat") {
|
|
1381
|
+
return handoffToChat(refreshedAdapter);
|
|
1382
|
+
}
|
|
1309
1383
|
upstream.abort();
|
|
1310
1384
|
return formatErrorResponse(502, "upstream_error", "OAuth refresh changed the provider wire unexpectedly");
|
|
1311
1385
|
}
|
|
1312
|
-
bindRouteReasoningReplayScope({
|
|
1313
|
-
parsed,
|
|
1314
|
-
providerName: route.providerName,
|
|
1315
|
-
provider: refreshedProvider,
|
|
1316
|
-
adapterName: refreshedAdapter.name,
|
|
1317
|
-
oauthCredentialSnapshot: transportState.replayOAuthCredentialSnapshot,
|
|
1318
|
-
});
|
|
1319
|
-
logCtx.providerAdapter = refreshedAdapter.name;
|
|
1320
|
-
sealRequestAttemptIdentity(
|
|
1321
|
-
logCtx.activeAttempt,
|
|
1322
|
-
logCtx.provider,
|
|
1323
|
-
refreshedAdapter.name,
|
|
1324
|
-
logCtx.accountLogLabel,
|
|
1325
|
-
);
|
|
1326
|
-
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, refreshedAdapter.name);
|
|
1327
1386
|
try {
|
|
1328
1387
|
request = await refreshedAdapter.buildRequest(parsed, {
|
|
1329
1388
|
headers: requestState.selectedForwardHeaders,
|
|
@@ -1356,7 +1415,7 @@ export async function preparePassthroughExchange(
|
|
|
1356
1415
|
nativeControl: nativeResponseControlEligible(route.provider, options.nativeControl) && options.inboundTransport === "websocket" && !options.comboAttempt
|
|
1357
1416
|
&& responseEffects.plaintextV2AgentMessageToolNames.size === 0
|
|
1358
1417
|
? options.nativeControl : undefined,
|
|
1359
|
-
dispatchOverride: oauthDispatch(request),
|
|
1418
|
+
dispatchOverride: oauthDispatch(request, parsed, "passthrough"),
|
|
1360
1419
|
providerName: route.providerName,
|
|
1361
1420
|
modelId: route.modelId,
|
|
1362
1421
|
onCodexWsQuota: codexWsQuotaObserver(admissionState.authCtx, route.provider, route.modelId),
|
|
@@ -1397,38 +1456,50 @@ export async function preparePassthroughExchange(
|
|
|
1397
1456
|
true,
|
|
1398
1457
|
);
|
|
1399
1458
|
if (hop.allowed) {
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
if (nextAccountId) {
|
|
1408
|
-
try { snapshot = await failoverAccountSnapshot(route.providerName, nextAccountId); }
|
|
1409
|
-
catch { /* Keep the original 429 body readable when the next credential is unavailable. */ }
|
|
1410
|
-
}
|
|
1411
|
-
if (snapshot && await applyFailoverSnapshot(snapshot)) {
|
|
1412
|
-
transportState.genericFailovers += 1;
|
|
1413
|
-
route.provider = resolveProviderTransport(
|
|
1414
|
-
route.providerName, route.provider, parsed.options.promptCacheKey, transportState.sentOAuthSnapshot?.apiBaseUrl,
|
|
1459
|
+
let transferred = false;
|
|
1460
|
+
try {
|
|
1461
|
+
const nextAccountId = rotateGenericOAuthAccountOn429(
|
|
1462
|
+
config, route.providerName, transportState.genericFailoverAccountId,
|
|
1463
|
+
upstreamResponse.headers.get("retry-after"),
|
|
1464
|
+
Date.now(),
|
|
1465
|
+
route.modelId,
|
|
1415
1466
|
);
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1467
|
+
let snapshot: OAuthAccessSnapshot | undefined;
|
|
1468
|
+
if (nextAccountId) {
|
|
1469
|
+
try { snapshot = await failoverAccountSnapshot(route.providerName, nextAccountId); }
|
|
1470
|
+
catch { /* Keep the original 429 body readable when the next credential is unavailable. */ }
|
|
1471
|
+
}
|
|
1472
|
+
if (snapshot && await applyFailoverSnapshot(snapshot)) {
|
|
1473
|
+
transportState.genericFailovers += 1;
|
|
1474
|
+
route.provider = resolveProviderTransport(
|
|
1475
|
+
route.providerName, route.provider, parsed.options.promptCacheKey, transportState.sentOAuthSnapshot?.apiBaseUrl,
|
|
1476
|
+
);
|
|
1477
|
+
const rotatedAdapter = resolveSelectionAdapter(
|
|
1478
|
+
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire, route.staticPolicy),
|
|
1479
|
+
config.cacheRetention,
|
|
1480
|
+
);
|
|
1481
|
+
bindRefreshedAdapter(rotatedAdapter);
|
|
1482
|
+
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already closed */ }
|
|
1483
|
+
sendBudgetState.pendingHopPermit = hop.permit;
|
|
1484
|
+
if (route.providerName === "github-copilot" && rotatedAdapter.name === "openai-chat") {
|
|
1485
|
+
transferred = true;
|
|
1486
|
+
return handoffToChat(rotatedAdapter);
|
|
1487
|
+
}
|
|
1488
|
+
const result = await rebuildAndRefetch("oauth-account-429");
|
|
1489
|
+
sendBudgetState.pendingHopPermit = undefined;
|
|
1490
|
+
if ("failed" in result) return result.failed;
|
|
1491
|
+
upstreamResponse = result;
|
|
1492
|
+
continue passthroughRecovery;
|
|
1493
|
+
}
|
|
1494
|
+
} catch (err) {
|
|
1420
1495
|
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already closed */ }
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
upstreamResponse = result;
|
|
1428
|
-
continue passthroughRecovery;
|
|
1496
|
+
return transportFailureResponse(err);
|
|
1497
|
+
} finally {
|
|
1498
|
+
if (!transferred) {
|
|
1499
|
+
if (sendBudgetState.pendingHopPermit === hop.permit) sendBudgetState.pendingHopPermit = undefined;
|
|
1500
|
+
hop.permit?.release();
|
|
1501
|
+
}
|
|
1429
1502
|
}
|
|
1430
|
-
// No credential moved, so the reservation costs nothing.
|
|
1431
|
-
hop.permit?.release();
|
|
1432
1503
|
} else {
|
|
1433
1504
|
// The activation quorum ignores cooldowns; prove that the selector has a live alternate
|
|
1434
1505
|
// before describing this as a recovery that only the shared request budget withheld.
|
|
@@ -1491,7 +1562,7 @@ export async function preparePassthroughExchange(
|
|
|
1491
1562
|
nativeControl: nativeResponseControlEligible(route.provider, options.nativeControl) && options.inboundTransport === "websocket" && !options.comboAttempt
|
|
1492
1563
|
&& responseEffects.plaintextV2AgentMessageToolNames.size === 0
|
|
1493
1564
|
? options.nativeControl : undefined,
|
|
1494
|
-
dispatchOverride: oauthDispatch(request),
|
|
1565
|
+
dispatchOverride: oauthDispatch(request, parsed, "passthrough"),
|
|
1495
1566
|
providerName: route.providerName,
|
|
1496
1567
|
modelId: route.modelId,
|
|
1497
1568
|
onCodexWsQuota: codexWsQuotaObserver(admissionState.authCtx, route.provider, route.modelId),
|
|
@@ -1858,6 +1929,7 @@ export async function preparePassthroughExchange(
|
|
|
1858
1929
|
const localUpstream = isLocalUpstream(route.provider.baseUrl);
|
|
1859
1930
|
|
|
1860
1931
|
return {
|
|
1932
|
+
kind: undefined,
|
|
1861
1933
|
codexSafetyBufferingOptions,
|
|
1862
1934
|
imageGenCallAliases,
|
|
1863
1935
|
routedCustomToolNames,
|
|
@@ -1900,4 +1972,4 @@ export async function preparePassthroughExchange(
|
|
|
1900
1972
|
};
|
|
1901
1973
|
}
|
|
1902
1974
|
|
|
1903
|
-
export type PassthroughExchange = Exclude<Awaited<ReturnType<typeof preparePassthroughExchange>>, Response>;
|
|
1975
|
+
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,
|
|
@@ -381,6 +381,10 @@ export async function prepareResponsesRequest(
|
|
|
381
381
|
let toolBridgeMaps: ReturnType<typeof buildToolBridgeMaps>;
|
|
382
382
|
try {
|
|
383
383
|
parsed = parseRequest(body);
|
|
384
|
+
if (options.inboundWire === "anthropic") {
|
|
385
|
+
parsed._nativeReasoningReplay = options.nativeReasoningReplay;
|
|
386
|
+
parsed._nativeReasoningMint = options.nativeReasoningMint;
|
|
387
|
+
}
|
|
384
388
|
parsed._promptCacheKeyIsSharedCohort = options.promptCacheKeyIsSharedCohort;
|
|
385
389
|
// The body may have been rebuilt since the inbound observation (previous-response
|
|
386
390
|
// expansion); alias the parsed raw body to the same draft so the outbound
|