@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
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
type RequestLogContext,
|
|
33
33
|
} from "../request-log";
|
|
34
34
|
import { captureRouteStaticPolicy, routeConcreteModel } from "../../router";
|
|
35
|
-
import { comboDefaultEffort, concreteComboRequestBody, getCombo } from "../../combos";
|
|
35
|
+
import { comboDefaultEffort, concreteComboRequestBody, getCombo, type JevDecision } from "../../combos";
|
|
36
36
|
import { supportedLadderFor } from "../effort-policy";
|
|
37
37
|
import { applyDroidReasoningDefault } from "../droid-reasoning-default";
|
|
38
38
|
import { resolveWireProtocolOverride } from "../adapter-resolve";
|
|
@@ -94,6 +94,7 @@ export interface ComboProtocolLanes {
|
|
|
94
94
|
target: ComboTarget,
|
|
95
95
|
targetRoute: RouteResult,
|
|
96
96
|
targetSendBudget: TransientSendBudget | undefined,
|
|
97
|
+
initialDecision?: Pick<JevDecision, "effort">,
|
|
97
98
|
): NativeComboChildPlan | undefined;
|
|
98
99
|
}
|
|
99
100
|
|
|
@@ -101,6 +102,7 @@ interface CandidateVerdict {
|
|
|
101
102
|
skip?: ProtocolFeature[];
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
/** Build opt-in native eligibility and child plans; only the initial plan applies a JEV choice. */
|
|
104
106
|
export function createComboProtocolLanes(input: {
|
|
105
107
|
source: ComboProtocolSource | undefined;
|
|
106
108
|
req: Request;
|
|
@@ -203,7 +205,7 @@ export function createComboProtocolLanes(input: {
|
|
|
203
205
|
400, unrepresentableMessage(features), "invalid_request_error", "unsupported_feature",
|
|
204
206
|
), "chat");
|
|
205
207
|
},
|
|
206
|
-
nativeChild: (target, targetRoute, targetSendBudget) => {
|
|
208
|
+
nativeChild: (target, targetRoute, targetSendBudget, initialDecision) => {
|
|
207
209
|
// Without the request's execution budget a native child could not share its sends, so it
|
|
208
210
|
// keeps the bridge rather than opening a tracker of its own.
|
|
209
211
|
if (!isRequestExecutionBudget(targetSendBudget)) return undefined;
|
|
@@ -214,7 +216,22 @@ export function createComboProtocolLanes(input: {
|
|
|
214
216
|
provider: targetRoute.provider,
|
|
215
217
|
modelId: targetRoute.modelId,
|
|
216
218
|
});
|
|
217
|
-
|
|
219
|
+
if (initialDecision) {
|
|
220
|
+
// A validated JEV choice owns only this first dispatch, including explicit no-effort.
|
|
221
|
+
// Do not run ordinary force defaults for null: they require a non-null default.
|
|
222
|
+
delete body.service_tier;
|
|
223
|
+
delete body.thinking_budget;
|
|
224
|
+
delete body.thinking;
|
|
225
|
+
const reasoning = body.reasoning;
|
|
226
|
+
if (reasoning && typeof reasoning === "object" && !Array.isArray(reasoning)) {
|
|
227
|
+
const preserved = { ...reasoning as Rec };
|
|
228
|
+
delete preserved.effort;
|
|
229
|
+
if (Object.keys(preserved).length > 0) body.reasoning = preserved;
|
|
230
|
+
else delete body.reasoning;
|
|
231
|
+
}
|
|
232
|
+
if (initialDecision.effort === null) delete body.reasoning_effort;
|
|
233
|
+
else body.reasoning_effort = initialDecision.effort;
|
|
234
|
+
} else applyComboEffort(body, config, comboId, target, targetRoute);
|
|
218
235
|
return { route, body, sendBudget: targetSendBudget };
|
|
219
236
|
},
|
|
220
237
|
};
|
|
@@ -717,30 +717,32 @@ export async function executeComboResponses(
|
|
|
717
717
|
const targetRoute = routeConcreteModel(config, `${pick.target.provider}/${pick.target.model}`);
|
|
718
718
|
// The inherited spend tracker observes this parent log, before the child has its own label.
|
|
719
719
|
logCtx.spendPoolId = targetRoute.providerName;
|
|
720
|
-
//
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
720
|
+
// Derive the target's allowance before booking its initial send; the booking occupies it.
|
|
721
|
+
const targetSendBudget = comboSendScope
|
|
722
|
+
? comboTargetSendBudget(comboSendScope, combo.targets.length - 1 - comboTargetsDispatched)
|
|
723
|
+
: options.sendBudget;
|
|
724
|
+
// Every target prepays one send. Only its child may consume that booking; later targets
|
|
725
|
+
// remain bounded by the shared total, transition ledger and per-target holdback.
|
|
726
|
+
// `countedExternally` is required: the child charges its own physical sends against this
|
|
727
|
+
// booking through the exact permit, and charging here as well would halve the cap.
|
|
724
728
|
const hopDecision = comboSendScope?.reserveDispatch({
|
|
725
729
|
sendClass: firstComboTarget ? "initial" : "combo-failover",
|
|
726
730
|
targetKey: `${pick.target.provider}/${pick.target.model}`,
|
|
727
731
|
countedExternally: true,
|
|
728
732
|
});
|
|
729
|
-
if (hopDecision && hopDecision.allowed)
|
|
730
|
-
else if (hopDecision && firstComboTarget) {
|
|
733
|
+
if (hopDecision && !hopDecision.allowed && firstComboTarget) {
|
|
731
734
|
// A refused initial reservation authorizes no child send and has no upstream failure to return.
|
|
732
735
|
return formatErrorResponse(429, SEND_BUDGET_EXHAUSTED_CODE, "request send budget exhausted before combo dispatch");
|
|
733
736
|
}
|
|
734
|
-
else if (hopDecision) {
|
|
737
|
+
else if (hopDecision && !hopDecision.allowed) {
|
|
735
738
|
// Out of budget is not this target's failure. The established exhaustion contract is to
|
|
736
739
|
// return the last real upstream answer with its status, headers and any quota body
|
|
737
740
|
// intact rather than to mint a synthetic error, and a later target only exists because
|
|
738
741
|
// an earlier one already recorded one.
|
|
739
742
|
return exhaustedFailure();
|
|
740
743
|
}
|
|
741
|
-
const
|
|
742
|
-
|
|
743
|
-
: options.sendBudget;
|
|
744
|
+
const initialSend = hopDecision?.allowed ? { permit: hopDecision.permit, producerOwned: false } : undefined;
|
|
745
|
+
try {
|
|
744
746
|
comboTargetsDispatched += 1;
|
|
745
747
|
const childLog: RequestLogContext = {
|
|
746
748
|
model: pick.target.model,
|
|
@@ -764,7 +766,7 @@ export async function executeComboResponses(
|
|
|
764
766
|
initialJevDecision ? initialJevDecision.effort : comboDefaultEffort(config, comboId),
|
|
765
767
|
initialJevDecision?.effort === null ? [] : targetReasoningEfforts,
|
|
766
768
|
combo.reasoningEffortMode,
|
|
767
|
-
initialJevDecision
|
|
769
|
+
initialJevDecision ? (initialJevDecision.effort === null ? "fallback" : "force") : combo.defaultEffortMode,
|
|
768
770
|
);
|
|
769
771
|
if (initialJevDecision) {
|
|
770
772
|
delete childBody.service_tier;
|
|
@@ -860,7 +862,7 @@ export async function executeComboResponses(
|
|
|
860
862
|
&& targetEligible(target)
|
|
861
863
|
&& !isComboTargetInCooldown(comboId, target),
|
|
862
864
|
);
|
|
863
|
-
const nativeChild = protocolLanes?.nativeChild(pick.target, targetRoute, targetSendBudget);
|
|
865
|
+
const nativeChild = protocolLanes?.nativeChild(pick.target, targetRoute, targetSendBudget, initialJevDecision);
|
|
864
866
|
response = nativeChild ? await dispatchNativeComboChild({
|
|
865
867
|
source: options.protocolSource!,
|
|
866
868
|
plan: nativeChild,
|
|
@@ -890,6 +892,7 @@ export async function executeComboResponses(
|
|
|
890
892
|
// parent arrived with.
|
|
891
893
|
sendBudget: targetSendBudget,
|
|
892
894
|
comboAttempt: true,
|
|
895
|
+
comboInitialSend: initialSend,
|
|
893
896
|
comboDispatchPermit: hopDecision?.allowed ? hopDecision.permit : undefined,
|
|
894
897
|
comboReplaySnapshot,
|
|
895
898
|
deferCodexResetDerivedCooldown,
|
|
@@ -909,8 +912,8 @@ export async function executeComboResponses(
|
|
|
909
912
|
onNativePassthroughCancel: callbackGate.onCancel,
|
|
910
913
|
onResponseComplete: callbackGate.onResponseComplete,
|
|
911
914
|
});
|
|
912
|
-
//
|
|
913
|
-
restoreOriginalRequestedEffort(childLog,
|
|
915
|
+
// Both lanes applied the initial choice before provider pins/caps; retain those transitions.
|
|
916
|
+
restoreOriginalRequestedEffort(childLog, initialJevDecision?.effort);
|
|
914
917
|
} catch (error) {
|
|
915
918
|
callbackGate.discard();
|
|
916
919
|
if (options.abortSignal?.aborted) {
|
|
@@ -1185,6 +1188,11 @@ export async function executeComboResponses(
|
|
|
1185
1188
|
// Waiting or recovery may have observed cancellation after the check above.
|
|
1186
1189
|
if (options.abortSignal?.aborted) return clientCancelledResponse();
|
|
1187
1190
|
}
|
|
1191
|
+
} finally {
|
|
1192
|
+
// External receipts make release a no-op after real work. runTurn owns its asynchronous
|
|
1193
|
+
// producer; core transfers cleanup there before returning a streaming response.
|
|
1194
|
+
if (!initialSend?.producerOwned) initialSend?.permit.release();
|
|
1195
|
+
}
|
|
1188
1196
|
}
|
|
1189
1197
|
const failure = exhaustedFailure();
|
|
1190
1198
|
if (
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TranslatorBudget } from "../../lib/translator-budget";
|
|
2
|
+
import { finalizeTranslatorBudgetResponse } from "../../lib/translator-budget";
|
|
2
3
|
import {
|
|
3
4
|
isNativePassthroughSseResponse,
|
|
4
5
|
markNativePassthroughSseResponse,
|
|
@@ -37,42 +38,8 @@ export const UPSTREAM_JSON_BODY_READ_OPTIONS = {
|
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
|
|
40
|
-
export function finalizeOwnedTranslatorBudget(response: Response, budget: TranslatorBudget): Response {
|
|
41
|
-
|
|
42
|
-
budget.dispose();
|
|
43
|
-
return response;
|
|
44
|
-
}
|
|
45
|
-
const reader = response.body.getReader();
|
|
46
|
-
let finalized = false;
|
|
47
|
-
const finalize = () => {
|
|
48
|
-
if (finalized) return;
|
|
49
|
-
finalized = true;
|
|
50
|
-
budget.dispose();
|
|
51
|
-
};
|
|
52
|
-
const body = new ReadableStream<Uint8Array>({
|
|
53
|
-
async pull(controller) {
|
|
54
|
-
try {
|
|
55
|
-
const result = await reader.read();
|
|
56
|
-
if (result.done) {
|
|
57
|
-
finalize();
|
|
58
|
-
controller.close();
|
|
59
|
-
} else {
|
|
60
|
-
controller.enqueue(result.value);
|
|
61
|
-
}
|
|
62
|
-
} catch (error) {
|
|
63
|
-
finalize();
|
|
64
|
-
controller.error(error);
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
async cancel(reason) {
|
|
68
|
-
try { await reader.cancel(reason); } finally { finalize(); }
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
const finalizedResponse = new Response(body, {
|
|
72
|
-
status: response.status,
|
|
73
|
-
statusText: response.statusText,
|
|
74
|
-
headers: response.headers,
|
|
75
|
-
});
|
|
41
|
+
export function finalizeOwnedTranslatorBudget(response: Response, budget: TranslatorBudget, signal?: AbortSignal): Response {
|
|
42
|
+
const finalizedResponse = finalizeTranslatorBudgetResponse(response, budget, signal);
|
|
76
43
|
if (isNativePassthroughSseResponse(response)) {
|
|
77
44
|
markNativePassthroughSseResponse(finalizedResponse);
|
|
78
45
|
}
|
|
@@ -465,13 +465,13 @@ export function resetStreamedOpaqueBlobLogContext(logCtx: RequestLogContext): vo
|
|
|
465
465
|
export type OpaqueBlobRecoveryGuard = { attempted: boolean };
|
|
466
466
|
|
|
467
467
|
|
|
468
|
-
export type OpaqueBlobRecoveryResult =
|
|
468
|
+
export type OpaqueBlobRecoveryResult<Failure = Response> =
|
|
469
469
|
| { kind: "skipped" }
|
|
470
470
|
| { kind: "recovered"; response: Response }
|
|
471
|
-
| { kind: "failed"; response:
|
|
471
|
+
| { kind: "failed"; response: Failure };
|
|
472
472
|
|
|
473
473
|
|
|
474
|
-
export async function attemptOpaqueBlobRecovery(
|
|
474
|
+
export async function attemptOpaqueBlobRecovery<Failure = Response>(
|
|
475
475
|
args: {
|
|
476
476
|
response: Response;
|
|
477
477
|
outboundBody?: string;
|
|
@@ -480,8 +480,8 @@ export async function attemptOpaqueBlobRecovery(
|
|
|
480
480
|
guard: OpaqueBlobRecoveryGuard;
|
|
481
481
|
signal: AbortSignal;
|
|
482
482
|
},
|
|
483
|
-
rebuild: (kind: AttemptRecoveryKind) => Promise<Response | { failed:
|
|
484
|
-
): Promise<OpaqueBlobRecoveryResult
|
|
483
|
+
rebuild: (kind: AttemptRecoveryKind) => Promise<Response | { failed: Failure }>,
|
|
484
|
+
): Promise<OpaqueBlobRecoveryResult<Failure>> {
|
|
485
485
|
const errorBody = await opaqueBlobRejectionBodyForRecovery(
|
|
486
486
|
args.response,
|
|
487
487
|
args.outboundBody,
|
|
@@ -16,6 +16,7 @@ import type { ExplicitOpenAiCallerAuth } from "../../providers/openai-sidecar";
|
|
|
16
16
|
import type { CallerDirectAuth } from "../../providers/caller-authorization";
|
|
17
17
|
import type { CompactionRoutingOverride } from "./compaction-routing";
|
|
18
18
|
import type { TranslatorBudget } from "../../lib/translator-budget";
|
|
19
|
+
import type { NativeReasoningOwner } from "../../responses/reasoning-replay-cache";
|
|
19
20
|
import type { TransientSendBudget } from "../../lib/upstream-retry";
|
|
20
21
|
import type { RequestLogContext } from "../request-log";
|
|
21
22
|
import type { UpstreamHostAdmissionLease } from "../../codex/upstream-host-health";
|
|
@@ -134,6 +135,8 @@ export interface HandleResponsesOptions {
|
|
|
134
135
|
* it. Omitted means a genuine Responses inbound.
|
|
135
136
|
*/
|
|
136
137
|
inboundWire?: InboundWire;
|
|
138
|
+
nativeReasoningReplay?: ReadonlyMap<string, string>;
|
|
139
|
+
nativeReasoningMint?: { owner?: NativeReasoningOwner };
|
|
137
140
|
/** Droid's per-request effort default; each concrete combo or policy target applies it only if its ladder allows it. */
|
|
138
141
|
droidDefaultEffort?: string;
|
|
139
142
|
/** PF-07: the Chat source a combo child may send natively; set only by the Chat ingress. */
|
|
@@ -158,6 +161,8 @@ export interface HandleResponsesOptions {
|
|
|
158
161
|
callerDirectAuth?: CallerDirectAuth | null;
|
|
159
162
|
/** Internal recursion guard; callers outside this module must not set it. */
|
|
160
163
|
comboAttempt?: boolean;
|
|
164
|
+
/** Child-owned prepaid initial send; capacity remains charged until dispatch or unsent release. */
|
|
165
|
+
comboInitialSend?: { permit: SingleUseDispatchPermit; producerOwned?: boolean };
|
|
161
166
|
/** Exact externally booked combo hop, used for its child's spend preflight and send reports. */
|
|
162
167
|
comboDispatchPermit?: SingleUseDispatchPermit;
|
|
163
168
|
/** Internal handoff: this combo was selected by shadow-call interception. */
|
|
@@ -19,6 +19,9 @@ import {
|
|
|
19
19
|
reasoningReplayCodexCredentialIdentity,
|
|
20
20
|
reasoningReplayKeyCredentialIdentity,
|
|
21
21
|
durableReplayDestinationIdentity,
|
|
22
|
+
nativeReasoningTag,
|
|
23
|
+
nativeReasoningTagMatches,
|
|
24
|
+
type NativeReasoningOwner,
|
|
22
25
|
bindReasoningReplayScope,
|
|
23
26
|
reasoningReplayServingIdentityChanged,
|
|
24
27
|
reasoningReplayItemStoreChanged,
|
|
@@ -118,6 +121,16 @@ export function bindRouteReasoningReplayScope(args: {
|
|
|
118
121
|
...args,
|
|
119
122
|
modelId: parsed.modelId,
|
|
120
123
|
});
|
|
124
|
+
const owner = nativeReasoningOwnerForRoute(args);
|
|
125
|
+
if (owner) parsed._nativeReasoningBoundOwner = owner;
|
|
126
|
+
else delete parsed._nativeReasoningBoundOwner;
|
|
127
|
+
if (parsed._nativeReasoningReplay?.size) {
|
|
128
|
+
for (const [blob, tag] of parsed._nativeReasoningReplay) {
|
|
129
|
+
if (!nativeReasoningTagMatches(nativeReasoningTag(owner, blob), tag)) {
|
|
130
|
+
stripNativeReasoningBlob(parsed, blob);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
121
134
|
const continuationDestinationIdentity = providerContinuationDestinationIdentity(parsed, provider);
|
|
122
135
|
const continuationOwner = providerContinuationOwnerFromReplayIdentity(
|
|
123
136
|
replayIdentity && continuationDestinationIdentity
|
|
@@ -156,6 +169,66 @@ export function bindRouteReasoningReplayScope(args: {
|
|
|
156
169
|
bindProviderContinuationForRoute(parsed, continuationOwner);
|
|
157
170
|
}
|
|
158
171
|
|
|
172
|
+
function stripNativeReasoningBlob(parsed: OcxParsedRequest, blob: string): void {
|
|
173
|
+
const input = (parsed._rawBody as { input?: unknown } | undefined)?.input;
|
|
174
|
+
if (Array.isArray(input)) {
|
|
175
|
+
for (const item of input) {
|
|
176
|
+
if (item && typeof item === "object" && item.type === "reasoning" && item.encrypted_content === blob) {
|
|
177
|
+
delete item.encrypted_content;
|
|
178
|
+
delete item.id;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
for (const message of parsed.context.messages) {
|
|
183
|
+
if (message.role !== "assistant" || !Array.isArray(message.content)) continue;
|
|
184
|
+
for (const part of message.content) {
|
|
185
|
+
if (part.type !== "thinking" || typeof part.signature !== "string") continue;
|
|
186
|
+
try {
|
|
187
|
+
const item: unknown = JSON.parse(part.signature);
|
|
188
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
189
|
+
const reasoning = item as Record<string, unknown>;
|
|
190
|
+
if (reasoning.type !== "reasoning" || reasoning.encrypted_content !== blob) continue;
|
|
191
|
+
delete reasoning.encrypted_content;
|
|
192
|
+
delete reasoning.id;
|
|
193
|
+
part.signature = JSON.stringify(reasoning);
|
|
194
|
+
delete part.itemId;
|
|
195
|
+
} catch { /* A provider signature is not the parser's JSON carrier. */ }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function nativeReasoningOwnerForRoute(args: {
|
|
201
|
+
provider: OcxProviderConfig;
|
|
202
|
+
oauthCredentialSnapshot?: Pick<OAuthAccessSnapshot, "accountId" | "generation">;
|
|
203
|
+
codexAuthContext?: CodexAuthContext;
|
|
204
|
+
}): NativeReasoningOwner | undefined {
|
|
205
|
+
const { provider } = args;
|
|
206
|
+
const salt = thoughtSignatureReplaySalt();
|
|
207
|
+
if (!salt || salt.length < 16) return undefined;
|
|
208
|
+
const destination = durableReplayDestinationIdentity(JSON.stringify([
|
|
209
|
+
provider.baseUrl.trim().replace(/\/+$/, ""), provider.responsesPath ?? "",
|
|
210
|
+
]));
|
|
211
|
+
let credential: string | undefined;
|
|
212
|
+
if (provider.authMode === "oauth") {
|
|
213
|
+
const snapshot = args.oauthCredentialSnapshot;
|
|
214
|
+
if (snapshot?.accountId && snapshot.generation !== undefined) credential = durableReplayCredentialIdentity(
|
|
215
|
+
"oauth", `${snapshot.accountId}\0${snapshot.generation}`, provider.headers, salt,
|
|
216
|
+
);
|
|
217
|
+
} else if (provider.authMode === "forward") {
|
|
218
|
+
const context = args.codexAuthContext;
|
|
219
|
+
if (context && (context.kind === "pool" || context.kind === "main-pool")
|
|
220
|
+
&& context.accountId && context.writerGeneration !== undefined
|
|
221
|
+
&& (context.kind !== "pool" || context.generation !== undefined)) {
|
|
222
|
+
credential = durableReplayCredentialIdentity("codex", JSON.stringify([
|
|
223
|
+
context.accountId, context.kind === "pool" ? context.generation : null, context.writerGeneration,
|
|
224
|
+
]), provider.headers, salt);
|
|
225
|
+
}
|
|
226
|
+
} else if (provider.authMode !== "local") {
|
|
227
|
+
credential = durableReplayCredentialIdentity("key", nonEmptyProviderApiKey(provider), provider.headers, salt);
|
|
228
|
+
}
|
|
229
|
+
return destination && credential ? { destination, credential } : undefined;
|
|
230
|
+
}
|
|
231
|
+
|
|
159
232
|
|
|
160
233
|
function routeReasoningReplayIdentity(args: {
|
|
161
234
|
providerName: string;
|
|
@@ -28,9 +28,6 @@ import { deliverAdapterResponse } from "./adapter-delivery";
|
|
|
28
28
|
import { releaseUpstreamHostAdmission } from "../../codex/upstream-host-health";
|
|
29
29
|
import { releaseCodexAuthContextProbeLease } from "../../codex/auth-context";
|
|
30
30
|
import { runWithCompactionRecovery } from "./compaction-recovery";
|
|
31
|
-
|
|
32
|
-
/** Public Responses entry and compatibility exports. Implementations live with their owners. */
|
|
33
|
-
|
|
34
31
|
/**
|
|
35
32
|
* Route one `/v1/responses` request through the adapter pipeline: recovery loop, passthrough
|
|
36
33
|
* wire, image/web-search bridges, and the terminal-guard continuation.
|
|
@@ -66,16 +63,17 @@ export async function handleResponses(
|
|
|
66
63
|
// Once at ingress, spend observer included: a combo child inherits the parent's holder.
|
|
67
64
|
sendBudget: options.sendBudget ?? createInferenceSendBudget(req, logCtx),
|
|
68
65
|
}, handleResponsesInner);
|
|
69
|
-
const finalResponse = ownsBudget ? finalizeOwnedTranslatorBudget(response, translatorBudget) : response;
|
|
66
|
+
const finalResponse = ownsBudget ? finalizeOwnedTranslatorBudget(response, translatorBudget, abortSignal) : response;
|
|
70
67
|
if (!accountLoad.lease) { release(); return finalResponse; }
|
|
71
68
|
return finalizeAccountLease(finalResponse, release);
|
|
72
69
|
} catch (error) {
|
|
73
70
|
release();
|
|
74
71
|
if (ownsBudget) translatorBudget.dispose();
|
|
75
72
|
throw error;
|
|
73
|
+
} finally {
|
|
74
|
+
if (!options.comboInitialSend?.producerOwned) options.comboInitialSend?.permit.release();
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
|
-
|
|
79
77
|
export async function handleComboResponses(
|
|
80
78
|
req: Request,
|
|
81
79
|
rawBody: unknown,
|
|
@@ -94,7 +92,6 @@ export async function handleComboResponses(
|
|
|
94
92
|
requestDispatchers,
|
|
95
93
|
);
|
|
96
94
|
}
|
|
97
|
-
|
|
98
95
|
/** Compose request phases while retaining the original admission-finally ownership. */
|
|
99
96
|
async function handleResponsesInner(
|
|
100
97
|
req: Request,
|
|
@@ -107,6 +104,7 @@ async function handleResponsesInner(
|
|
|
107
104
|
pendingHostAdmissionLease: null,
|
|
108
105
|
authCtx: { kind: "main", accountId: null },
|
|
109
106
|
};
|
|
107
|
+
let releasePendingSend = () => {};
|
|
110
108
|
try {
|
|
111
109
|
const requestState = await prepareResponsesRequest(requestContext, admissionState, requestDispatchers);
|
|
112
110
|
if (requestState instanceof Response) return requestState;
|
|
@@ -124,7 +122,7 @@ async function handleResponsesInner(
|
|
|
124
122
|
const sendBudgetState = createResponsesSendBudget(requestContext);
|
|
125
123
|
if (sendBudgetState instanceof Response) return sendBudgetState;
|
|
126
124
|
if ("passthrough" in transportState.adapter && transportState.adapter.passthrough && !sidecarState.routedCompaction) {
|
|
127
|
-
|
|
125
|
+
const passthroughResult = await executePassthroughResponse(
|
|
128
126
|
requestContext,
|
|
129
127
|
admissionState,
|
|
130
128
|
requestState,
|
|
@@ -133,6 +131,9 @@ async function handleResponsesInner(
|
|
|
133
131
|
responseEffects,
|
|
134
132
|
sendBudgetState,
|
|
135
133
|
);
|
|
134
|
+
if (passthroughResult instanceof Response) return passthroughResult;
|
|
135
|
+
const unclaimedHop = sendBudgetState.pendingHopPermit;
|
|
136
|
+
releasePendingSend = () => { if (sendBudgetState.pendingHopPermit === unclaimedHop) { unclaimedHop?.release(); sendBudgetState.pendingHopPermit = undefined; } };
|
|
136
137
|
}
|
|
137
138
|
const sidecarPlans = await executeResponsesSidecars(
|
|
138
139
|
requestContext,
|
|
@@ -182,15 +183,14 @@ async function handleResponsesInner(
|
|
|
182
183
|
continuationState,
|
|
183
184
|
);
|
|
184
185
|
} finally {
|
|
186
|
+
releasePendingSend();
|
|
185
187
|
if (admissionState.pendingHostAdmissionLease) {
|
|
186
188
|
releaseUpstreamHostAdmission(admissionState.pendingHostAdmissionLease);
|
|
187
189
|
releaseCodexAuthContextProbeLease(admissionState.authCtx);
|
|
188
190
|
}
|
|
189
191
|
}
|
|
190
192
|
}
|
|
191
|
-
|
|
192
193
|
const requestDispatchers: ResponsesDispatchers = { handleResponses, handleComboResponses };
|
|
193
|
-
|
|
194
194
|
export { adapterNeedsForcedContinuation } from "./core-replay";
|
|
195
195
|
export {
|
|
196
196
|
sidecarOutcomeRecorder, codexLogAccountId, usesCodexForwardPoolAuth, preAuthUpstreamHostCircuitKey,
|
|
@@ -293,11 +293,27 @@ export async function* guardEmptyCompletionEventStream(
|
|
|
293
293
|
return;
|
|
294
294
|
}
|
|
295
295
|
if (event.type === "error") {
|
|
296
|
-
if (
|
|
296
|
+
if (event.replaySafeBeforeOutput === true && retries < maxRetries) {
|
|
297
|
+
// The upstream failed the turn before any text or tool call (#6876: Vertex dropped a
|
|
298
|
+
// malformed function call). Nothing actionable reached the client, so this is the same
|
|
299
|
+
// replay-safe shape as an empty completion: retry the identical turn once.
|
|
300
|
+
usage = mergeUsage(usage, event.usage);
|
|
301
|
+
retries += 1;
|
|
302
|
+
try {
|
|
303
|
+
source = await options.continuation();
|
|
304
|
+
} catch {
|
|
305
|
+
yield emptyCompletionRetryFailedEvent(usage, true);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
terminalSeen = true;
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
if (retries > 0 && event.status !== 499 && event.replaySafeBeforeOutput !== true) {
|
|
297
312
|
// The retry failed upstream. Its body cannot reach the client (the
|
|
298
313
|
// 200 head went out with the first attempt), so state the failure in
|
|
299
314
|
// the stream's own error framing — same move as the router's
|
|
300
|
-
// empty_completion_retry_failed. Client cancels (499) pass through
|
|
315
|
+
// empty_completion_retry_failed. Client cancels (499) pass through, and a
|
|
316
|
+
// replay-safe failure that repeats keeps its own, more specific message.
|
|
301
317
|
yield emptyCompletionRetryFailedEvent(mergeUsage(usage, event.usage), true);
|
|
302
318
|
return;
|
|
303
319
|
}
|
|
@@ -43,6 +43,10 @@ const pacingWebsocketDowngradeWarned = new Set<string>();
|
|
|
43
43
|
*/
|
|
44
44
|
const EGRESS_DECIDED = Symbol.for("opencodex.provider-egress.decided");
|
|
45
45
|
const UPSTREAM_REWRITTEN = Symbol.for("opencodex.plugins.upstream-rewritten");
|
|
46
|
+
// Survives queued credential rebuilds and executor replacement without importing budget owners.
|
|
47
|
+
const PHYSICAL_DISPATCH = Symbol.for("opencodex.physical-dispatch");
|
|
48
|
+
// One WS attempt and its HTTP fallback are one logical physical-send receipt.
|
|
49
|
+
const WS_FALLBACK_RECEIPT = Symbol.for("opencodex.ws-fallback-receipt");
|
|
46
50
|
|
|
47
51
|
/**
|
|
48
52
|
* Announce once, per provider, that an explicit egress route moved this provider off the
|
|
@@ -200,6 +204,7 @@ export function sendWithConnectionPolicy(
|
|
|
200
204
|
const largeCodexBody = typeof body === "string"
|
|
201
205
|
&& /^https:\/\/chatgpt\.com\/backend-api\/codex\/responses(?:\/compact)?$/.test(target)
|
|
202
206
|
&& Buffer.byteLength(body, "utf8") >= 1024 * 1024;
|
|
207
|
+
(init as Record<symbol, (() => void) | undefined> | undefined)?.[PHYSICAL_DISPATCH]?.();
|
|
203
208
|
return physicalFetch(input, {
|
|
204
209
|
...init,
|
|
205
210
|
...(largeCodexBody ? { body: Buffer.from(body, "utf8") } : {}),
|
|
@@ -233,10 +238,13 @@ export interface ProviderFetchOptions {
|
|
|
233
238
|
onCodexWsQuota?: CodexWsQuotaObserver;
|
|
234
239
|
/** Synchronous admission at actual credential dispatch, after pacing/backoff. */
|
|
235
240
|
beforeDispatch?: (headers: Headers) => void;
|
|
241
|
+
/** One-shot receipt after final local HTTP admission, immediately before executor entry. */
|
|
242
|
+
onPhysicalDispatch?: () => void;
|
|
236
243
|
/** Revalidate/rebuild a queued request at its physical send boundary, after pacing. */
|
|
237
244
|
dispatchOverride?: (input: Parameters<typeof globalThis.fetch>[0], init: RequestInit, execute: typeof globalThis.fetch) => Promise<Response>;
|
|
238
245
|
}
|
|
239
246
|
|
|
247
|
+
/** Compose provider pacing and transport admission; receipts run only at executor entry. */
|
|
240
248
|
export function providerFetch(
|
|
241
249
|
provider: OcxProviderConfig,
|
|
242
250
|
runtime: BunRuntimeGateInput = currentBunRuntimeIdentity(),
|
|
@@ -301,7 +309,14 @@ export function providerFetch(
|
|
|
301
309
|
options.beforeDispatch?.(new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined)));
|
|
302
310
|
// No proxy option is attached here: a `dispatchOverride` may rebuild this request against
|
|
303
311
|
// a different destination, so the route is decided at the physical send instead.
|
|
304
|
-
const
|
|
312
|
+
const sharedReceipt = (init as Record<symbol, (() => void) | undefined> | undefined)?.[WS_FALLBACK_RECEIPT];
|
|
313
|
+
let notified = false;
|
|
314
|
+
const dispatchInit = { ...withUpstreamHttpVersion(input, init, provider), timeout: 0,
|
|
315
|
+
...(options.onPhysicalDispatch ? { [PHYSICAL_DISPATCH]: () => {
|
|
316
|
+
if (sharedReceipt) sharedReceipt();
|
|
317
|
+
else if (!notified) { options.onPhysicalDispatch!(); notified = true; }
|
|
318
|
+
} } : {}),
|
|
319
|
+
};
|
|
305
320
|
return options.dispatchOverride
|
|
306
321
|
? options.dispatchOverride(input, dispatchInit, dispatch)
|
|
307
322
|
: dispatch(input, dispatchInit);
|
|
@@ -332,8 +347,16 @@ export function providerFetch(
|
|
|
332
347
|
// used, protocol pin included: a WS turn that falls back is serving the
|
|
333
348
|
// request over HTTP, and dropping the provider's `upstreamHttpVersion`
|
|
334
349
|
// there would silently negotiate a transport the operator ruled out.
|
|
335
|
-
|
|
336
|
-
|
|
350
|
+
let notified = false;
|
|
351
|
+
const notifyOnce = (): void => {
|
|
352
|
+
if (notified) return;
|
|
353
|
+
notified = true;
|
|
354
|
+
options.onPhysicalDispatch?.();
|
|
355
|
+
};
|
|
356
|
+
const fallback = ((url: Parameters<typeof globalThis.fetch>[0], fallbackInit?: RequestInit) =>
|
|
357
|
+
httpFetch(url, { ...fallbackInit, [WS_FALLBACK_RECEIPT]: notifyOnce } as RequestInit)) as typeof globalThis.fetch;
|
|
358
|
+
return codexWsUpstreamFetch(input, init, fallback, runtime, options.onCodexWsQuota, options.beforeDispatch, options.nativeControl,
|
|
359
|
+
async () => { (await waitForPacing(init.signal ?? undefined))?.release(); }, notifyOnce);
|
|
337
360
|
}
|
|
338
361
|
return httpFetch(input, init);
|
|
339
362
|
};
|
|
@@ -136,7 +136,7 @@ export function createJevModelInvoker(context: JevModelInvokerContext): JevModel
|
|
|
136
136
|
if (new TextEncoder().encode(body).byteLength > JEV_MAX_REQUEST_BYTES) {
|
|
137
137
|
throw new JevModelInvokeError("malformed", "decision request too large");
|
|
138
138
|
}
|
|
139
|
-
const lease = tryAdmitTurn();
|
|
139
|
+
const lease = tryAdmitTurn(undefined, signal);
|
|
140
140
|
if (!lease) throw new JevModelInvokeError("network", "decision turn not admitted");
|
|
141
141
|
const childLog: RequestLogContext = { model, provider: "unknown", inboundProtocol: "responses" };
|
|
142
142
|
let usage: Record<string, number> | undefined;
|
|
@@ -24,13 +24,16 @@ import { teeWithBoundedInspection } from "../inspection-tee";
|
|
|
24
24
|
import {
|
|
25
25
|
codexForwardTerminalOutcomeRecorder,
|
|
26
26
|
usesCodexForwardPoolAuth,
|
|
27
|
+
liveMainQuotaDispatch,
|
|
27
28
|
codexQuotaOutcomeMeta,
|
|
28
29
|
codexDenialOutcomeMeta,
|
|
29
30
|
isFixedCodexAccount,
|
|
30
31
|
shouldDeferCodexResetDerivedCooldown,
|
|
31
32
|
} from "./core-codex-account";
|
|
33
|
+
import { isMainQuotaDispatchLive, isMainQuotaDispatchWsClaimed } from "../../codex/main-account-cache";
|
|
34
|
+
import { MAIN_CODEX_ACCOUNT_ID } from "../../codex/account-id";
|
|
32
35
|
import type { ResponsesTerminalStatus } from "../../bridge";
|
|
33
|
-
import { isCodexWsQuotaObservedResponse, isCodexWsUpstreamResponse } from "./ws-upstream";
|
|
36
|
+
import { isCodexWsQuotaObservedResponse, isCodexWsUpstreamResponse, isCodexWsPreludeProjection } from "./ws-upstream";
|
|
34
37
|
import { recordSubagentQuotaFailureForThreadSpawn } from "../../codex/subagent-model-fallback";
|
|
35
38
|
import { recordCodexUpstreamOutcome } from "../../codex/routing";
|
|
36
39
|
import { codexProbeLeaseId, codexProbeQuotaScope, codexTransientProbeGrant, releaseCodexAuthContextProbeLease } from "../../codex/auth-context";
|
|
@@ -139,6 +142,7 @@ import {
|
|
|
139
142
|
httpStatusFromTerminalError,
|
|
140
143
|
inspectResponseLogJson,
|
|
141
144
|
inspectResponseLogSsePayloadParsed,
|
|
145
|
+
noteUpstreamRequestId,
|
|
142
146
|
} from "../request-log";
|
|
143
147
|
import { restoreRoutedCustomCallsInJson } from "../../responses/custom-tool-compat";
|
|
144
148
|
import { restoreRoutedToolSearchCallsInJson } from "../../responses/tool-search-compat";
|
|
@@ -406,6 +410,9 @@ export async function deliverPassthroughResponse(
|
|
|
406
410
|
| "localUpstream"
|
|
407
411
|
>,
|
|
408
412
|
): Promise<Response> {
|
|
413
|
+
const { route } = requestState;
|
|
414
|
+
// The proof must belong to the response whose headers are published.
|
|
415
|
+
const arrivalMainDispatch = liveMainQuotaDispatch(admissionState.authCtx, route.provider);
|
|
409
416
|
const { logCtx, config, options, req } = requestContext;
|
|
410
417
|
const {
|
|
411
418
|
codexSafetyBufferingOptions,
|
|
@@ -429,7 +436,7 @@ export async function deliverPassthroughResponse(
|
|
|
429
436
|
normalizeFunctionCompletionJson,
|
|
430
437
|
} = nativeExchange;
|
|
431
438
|
const { commitReasoningReplayServingRoute, recordTerminalOutcomes } = responseEffects;
|
|
432
|
-
const { parsed,
|
|
439
|
+
const { parsed, subagentQuotaFailureModel, clientRequestedStream, translatorBudget, inboundWire } = requestState;
|
|
433
440
|
const enforceDeclaredToolNames = inboundWire !== "chat" && inboundWire !== "anthropic";
|
|
434
441
|
const { openAiSidecar } = sidecarState;
|
|
435
442
|
const { requestBindings } = transportState;
|
|
@@ -452,6 +459,7 @@ export async function deliverPassthroughResponse(
|
|
|
452
459
|
}
|
|
453
460
|
|
|
454
461
|
const headers = sanitizePassthroughHeaders(upstreamResponse.headers, codexSafetyBufferingOptions);
|
|
462
|
+
if (!upstreamResponse.ok) noteUpstreamRequestId(logCtx, headers);
|
|
455
463
|
const resolvedModel = headers.get("openai-model")?.trim();
|
|
456
464
|
if (resolvedModel) {
|
|
457
465
|
logCtx.servedModel = resolvedModel;
|
|
@@ -535,6 +543,20 @@ export async function deliverPassthroughResponse(
|
|
|
535
543
|
...(admissionState.authCtx.kind === "pool" ? { credentialGeneration: admissionState.authCtx.generation } : {}),
|
|
536
544
|
});
|
|
537
545
|
}
|
|
546
|
+
} else {
|
|
547
|
+
// The WS observer is the only plain-main publisher for a WebSocket exchange;
|
|
548
|
+
// a prelude projection carries the prelude snapshot, not fresh evidence.
|
|
549
|
+
// The dispatch claim is authoritative because downstream wrappers can replace the Response.
|
|
550
|
+
if (arrivalMainDispatch && !isMainQuotaDispatchWsClaimed(arrivalMainDispatch)
|
|
551
|
+
&& !(isCodexWsUpstreamResponse(upstreamResponse) || isCodexWsPreludeProjection(upstreamResponse))) {
|
|
552
|
+
const { applyAccountQuotaFromUpstreamHeaders } = await import("../../codex/auth-api");
|
|
553
|
+
// Import yields; same-account token replacement leaves the identity writer live.
|
|
554
|
+
// Re-check the credential fence with no await before publication.
|
|
555
|
+
if (isMainQuotaDispatchLive(arrivalMainDispatch)) {
|
|
556
|
+
applyAccountQuotaFromUpstreamHeaders(MAIN_CODEX_ACCOUNT_ID, upstreamResponse.headers,
|
|
557
|
+
arrivalMainDispatch.configGeneration, arrivalMainDispatch.writer, { modelId: route.modelId });
|
|
558
|
+
}
|
|
559
|
+
}
|
|
538
560
|
}
|
|
539
561
|
|
|
540
562
|
// Non-2xx passthrough failures must never reach Codex as an empty body —
|