@bitkyc08/opencodex 2.55.0 → 2.57.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 +10 -0
- package/gui/dist/assets/{index-BBOZWGB6.css → index-C5-RdDmD.css} +1 -1
- package/gui/dist/assets/{index-VuoiWj9J.js → index-Cz7CLdif.js} +21 -21
- package/gui/dist/index.html +2 -2
- package/package.json +4 -3
- package/src/adapters/base.ts +21 -0
- package/src/adapters/codebuddy/adapter.ts +2 -1
- package/src/adapters/codebuddy/scaffold-guard.ts +248 -0
- package/src/adapters/command-code.ts +1 -1
- package/src/adapters/cursor/envelope-echo.ts +8 -2
- package/src/adapters/cursor/transport-retry.ts +46 -1
- package/src/adapters/cursor.ts +4 -0
- package/src/adapters/google.ts +7 -7
- package/src/adapters/kiro/adapter.ts +42 -1
- package/src/adapters/kiro/payload.ts +17 -3
- package/src/adapters/kiro/reasoning.ts +70 -7
- package/src/adapters/kiro/stream.ts +8 -2
- package/src/adapters/kiro/wire.ts +2 -1
- package/src/adapters/kiro-events.ts +21 -13
- package/src/adapters/kiro-retry.ts +23 -4
- package/src/adapters/openai-chat/errors.ts +116 -0
- package/src/adapters/openai-chat/messages.ts +346 -0
- package/src/adapters/openai-chat/passthrough.ts +146 -0
- package/src/adapters/openai-chat/response-events.ts +117 -0
- package/src/adapters/openai-chat/tool-call-validation.ts +200 -0
- package/src/adapters/openai-chat/tool-name-registry.ts +166 -0
- package/src/adapters/openai-chat/tool-schema.ts +495 -0
- package/src/adapters/openai-chat/wire.ts +50 -0
- package/src/adapters/openai-chat.ts +40 -1452
- package/src/adapters/openai-responses/canonical-forward.ts +202 -0
- package/src/adapters/openai-responses/image-gen.ts +406 -0
- package/src/adapters/openai-responses/internal.ts +3 -0
- package/src/adapters/openai-responses/passthrough.ts +642 -0
- package/src/adapters/openai-responses/prompt-cache.ts +83 -0
- package/src/adapters/openai-responses/reasoning.ts +220 -0
- package/src/adapters/openai-responses/request-strips.ts +185 -0
- package/src/adapters/openai-responses/tool-output-recovery.ts +509 -0
- package/src/adapters/openai-responses/tool-schema.ts +293 -0
- package/src/adapters/openai-responses/web-search.ts +156 -0
- package/src/adapters/openai-responses.ts +4 -2625
- package/src/bridge/errors.ts +58 -0
- package/src/bridge/internal.ts +174 -0
- package/src/bridge/response-json.ts +630 -0
- package/src/bridge/sse.ts +1462 -0
- package/src/bridge.ts +5 -2204
- package/src/chat/inbound.ts +12 -1
- package/src/claude/desktop-profile.ts +66 -9
- package/src/claude/outbound.ts +18 -0
- package/src/cli/account-main.ts +1 -1
- package/src/cli/capabilities.ts +2 -2
- package/src/cli/combo.ts +10 -1
- package/src/cli/index.ts +48 -5
- package/src/cli/registry.ts +2 -1
- package/src/cli/system-command.ts +4 -4
- package/src/clients/config-export.ts +7 -3
- package/src/codex/account-label.ts +14 -3
- package/src/codex/account-lifecycle.ts +3 -0
- package/src/codex/account-store.ts +184 -35
- package/src/codex/account-usability.ts +21 -0
- package/src/codex/auth-api/account-list.ts +507 -0
- package/src/codex/auth-api/http.ts +32 -0
- package/src/codex/auth-api/login-flow.ts +566 -0
- package/src/codex/auth-api/login-state.ts +64 -0
- package/src/codex/auth-api/main-account-probe.ts +331 -0
- package/src/codex/auth-api/pool-mode-gate.ts +274 -0
- package/src/codex/auth-api/pool-quota-probe.ts +512 -0
- package/src/codex/auth-api/reset-credit-service.ts +431 -0
- package/src/codex/auth-api/routes.ts +425 -0
- package/src/codex/auth-api/runtime-config.ts +48 -0
- package/src/codex/auth-api.ts +27 -3118
- package/src/codex/auth-context.ts +252 -35
- package/src/codex/catalog/aggregation.ts +80 -1
- package/src/codex/catalog/auto-review.ts +507 -0
- package/src/codex/catalog/build-entries.ts +981 -0
- package/src/codex/catalog/combo-member.ts +375 -0
- package/src/codex/catalog/derive-entry.ts +229 -0
- package/src/codex/catalog/effort.ts +0 -1
- package/src/codex/catalog/gated-native-warn.ts +63 -0
- package/src/codex/catalog/gather-capture.ts +533 -0
- package/src/codex/catalog/model-hints.ts +691 -0
- package/src/codex/catalog/model-visibility.ts +305 -0
- package/src/codex/catalog/provider-fetch.ts +52 -2942
- package/src/codex/catalog/provider-models.ts +685 -0
- package/src/codex/catalog/remote.ts +30 -0
- package/src/codex/catalog/restore.ts +132 -0
- package/src/codex/catalog/retained-sync.ts +714 -0
- package/src/codex/catalog/routed-gather.ts +895 -0
- package/src/codex/catalog/subagent-roster.ts +176 -0
- package/src/codex/catalog/sync.ts +52 -2698
- package/src/codex/cli-install-provenance.ts +7 -1
- package/src/codex/convergence.ts +7 -2
- package/src/codex/desktop-app/types.ts +11 -2
- package/src/codex/desktop-app/windows.ts +5 -5
- package/src/codex/inject/config-toml.ts +563 -0
- package/src/codex/inject/remove.ts +192 -0
- package/src/codex/inject/restore.ts +567 -0
- package/src/codex/inject/routing-classify.ts +109 -0
- package/src/codex/inject/routing-target.ts +125 -0
- package/src/codex/inject.ts +89 -1444
- package/src/codex/lineage.ts +458 -0
- package/src/codex/model-entitlements.ts +152 -15
- package/src/codex/pool-refresh-backoff.ts +161 -0
- package/src/codex/quota-rejection.ts +104 -15
- package/src/codex/routing/active-account.ts +194 -0
- package/src/codex/routing/cache-affinity.ts +70 -0
- package/src/codex/routing/cooldown-math.ts +285 -0
- package/src/codex/routing/health-store.ts +402 -0
- package/src/codex/routing/probe-lease.ts +358 -0
- package/src/codex/routing/selection.ts +780 -0
- package/src/codex/routing/thread-affinity.ts +586 -0
- package/src/codex/routing/transient-hold-dispatch.ts +141 -0
- package/src/codex/routing.ts +370 -2271
- package/src/codex/shim-fingerprint.ts +223 -0
- package/src/codex/shim-inspect.ts +175 -0
- package/src/codex/shim-probe.ts +367 -0
- package/src/codex/shim-restore-lock.ts +169 -0
- package/src/codex/shim-state-file.ts +151 -0
- package/src/codex/shim-templates.ts +265 -0
- package/src/codex/shim.ts +48 -1268
- package/src/codex/warmup.ts +1 -1
- package/src/combos/failover.ts +85 -0
- package/src/combos/request.ts +17 -10
- package/src/combos/types.ts +23 -2
- package/src/config/diagnostics.ts +705 -0
- package/src/config/feature-flags.ts +55 -0
- package/src/config/live-reconcile.ts +403 -0
- package/src/config/load-degrade.ts +880 -0
- package/src/config/mutation-lock.ts +244 -0
- package/src/config/openai-tier-backup.ts +268 -0
- package/src/config/pending-teardown.ts +31 -0
- package/src/config/persist-unlocked.ts +92 -0
- package/src/config/proxy-env.ts +188 -0
- package/src/config/salvage.ts +244 -0
- package/src/config/schema/config-schema.ts +640 -0
- package/src/config/schema/leaf-validators.ts +855 -0
- package/src/config/warn-memo.ts +28 -0
- package/src/config.ts +234 -4481
- package/src/generated/compatibility-version.json +649 -121
- package/src/images/loop.ts +1 -1
- package/src/lib/errors.ts +17 -0
- package/src/lib/request-execution-budget.ts +198 -23
- package/src/lib/spend-reservation-ledger.ts +958 -0
- package/src/lib/state-store-registrations.ts +6 -2
- package/src/lib/test-home-guard.ts +85 -1
- package/src/lib/upstream-retry.ts +132 -21
- package/src/lib/windows-elevation.ts +76 -14
- package/src/lib/workflow-budget.ts +553 -30
- package/src/oauth/index.ts +2 -2
- package/src/oauth/key-providers.ts +2 -2
- package/src/providers/kiro-models.ts +4 -3
- package/src/providers/label.ts +19 -1
- package/src/providers/model-discovery.ts +16 -0
- package/src/providers/quota/account-cache.ts +441 -0
- package/src/providers/quota/antigravity.ts +295 -0
- package/src/providers/quota/report-cache.ts +320 -0
- package/src/providers/quota/vendor-probes-key.ts +1243 -0
- package/src/providers/quota/vendor-probes-oauth.ts +590 -0
- package/src/providers/quota.ts +324 -3079
- package/src/providers/registry/entries-core.ts +1228 -0
- package/src/providers/registry/entries-extended.ts +1213 -0
- package/src/providers/registry/model-seeds.ts +912 -0
- package/src/providers/registry/types.ts +352 -0
- package/src/providers/registry.ts +24 -3536
- package/src/responses/continuation-ownership.ts +29 -0
- package/src/responses/reasoning-envelope.ts +6 -3
- package/src/responses/state/replay-fingerprint.ts +80 -0
- package/src/responses/state/snapshot-codec.ts +104 -0
- package/src/responses/state/spill-failure.ts +118 -0
- package/src/responses/state/spill-queue.ts +665 -0
- package/src/responses/state/temp-recovery.ts +257 -0
- package/src/responses/state.ts +82 -1143
- package/src/routing/identity-domains.ts +456 -0
- package/src/routing/probe-lease.ts +613 -0
- package/src/server/chat-completions.ts +3 -1
- package/src/server/chat-native.ts +37 -9
- package/src/server/index/bounded-request.ts +88 -0
- package/src/server/index/live-sideband.ts +601 -0
- package/src/server/index/serve-options.ts +1766 -0
- package/src/server/index/startup-warnings.ts +213 -0
- package/src/server/index/websocket-handler.ts +339 -0
- package/src/server/index.ts +45 -2552
- package/src/server/inspection-tee.ts +107 -0
- package/src/server/live.ts +46 -1
- package/src/server/management/combo-routes.ts +10 -1
- package/src/server/management/route-registry.ts +26 -23
- package/src/server/management/shared.ts +8 -5
- package/src/server/management/workflow-budget-routes.ts +133 -0
- package/src/server/management-api.ts +12 -0
- package/src/server/relay-eager.ts +2 -0
- package/src/server/relay.ts +14 -19
- package/src/server/request-log-conversation.ts +9 -7
- package/src/server/request-log.ts +372 -4
- package/src/server/response-log-body.ts +153 -0
- package/src/server/responses/account-change-state.ts +307 -0
- package/src/server/responses/adapter-continuation.ts +540 -0
- package/src/server/responses/adapter-delivery.ts +208 -0
- package/src/server/responses/adapter-dispatch.ts +1042 -0
- package/src/server/responses/codex-ws-wire.ts +5 -0
- package/src/server/responses/collaboration.ts +74 -4
- package/src/server/responses/combo-session-recall.ts +68 -8
- package/src/server/responses/compact.ts +113 -17
- package/src/server/responses/completion-policy.ts +33 -0
- package/src/server/responses/core-auth.ts +529 -0
- package/src/server/responses/core-codex-account.ts +907 -0
- package/src/server/responses/core-combo-failure.ts +210 -0
- package/src/server/responses/core-combo.ts +787 -0
- package/src/server/responses/core-errors.ts +170 -0
- package/src/server/responses/core-lifetime.ts +95 -0
- package/src/server/responses/core-normalize.ts +350 -0
- package/src/server/responses/core-opaque-recovery.ts +380 -0
- package/src/server/responses/core-options.ts +159 -0
- package/src/server/responses/core-replay.ts +298 -0
- package/src/server/responses/core.ts +192 -8893
- package/src/server/responses/encrypted-payload.ts +0 -1
- package/src/server/responses/input-admission.ts +126 -6
- package/src/server/responses/passthrough-delivery.ts +869 -0
- package/src/server/responses/passthrough-dispatch.ts +1494 -0
- package/src/server/responses/passthrough-error.ts +38 -2
- package/src/server/responses/passthrough-execution.ts +54 -0
- package/src/server/responses/request-prepare.ts +1080 -0
- package/src/server/responses/request-send-budget.ts +259 -0
- package/src/server/responses/request-sidecar-auth.ts +149 -0
- package/src/server/responses/request-spend.ts +147 -0
- package/src/server/responses/request-transport.ts +803 -0
- package/src/server/responses/response-effects.ts +157 -0
- package/src/server/responses/run-turn-execution.ts +476 -0
- package/src/server/responses/sidecar-execution.ts +463 -0
- package/src/server/responses/terminal-guard.ts +65 -4
- package/src/server/responses-image-gen-repair.ts +1 -1
- package/src/server/responses-undeclared-tool-guard.ts +9 -5
- package/src/server/workflow-refusal.ts +84 -0
- package/src/service/windows-ops.ts +210 -16
- package/src/service/windows-scheduler.ts +28 -21
- package/src/service.ts +1 -1
- package/src/types/config.ts +34 -1
- package/src/types/request.ts +8 -5
- package/src/types/tools.ts +24 -0
- package/src/types.ts +2 -0
- package/src/update/index.ts +10 -0
- package/src/update/stop-contract.d.mts +1 -0
- package/src/update/stop-contract.mjs +19 -0
- package/src/update/stop-decision.d.mts +1 -1
- package/src/update/stop-decision.mjs +12 -3
- package/src/usage/log.ts +147 -1
- package/src/usage/summary.ts +171 -21
- package/src/vision/anthropic-describe.ts +1 -1
- package/src/vision/describe.ts +5 -5
- package/src/web-search/anthropic-executor.ts +1 -1
- package/src/web-search/exa-executor.ts +1 -1
- package/src/web-search/executor.ts +1 -1
- package/src/web-search/gemini-executor.ts +1 -1
- package/src/web-search/loop.ts +1 -1
- package/src/web-search/ollama-executor.ts +1 -1
- package/src/web-search/parse.ts +67 -14
- package/src/web-search/passthrough-bridge.ts +64 -31
- package/src/web-search/xai-executor.ts +1 -1
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
OcxProviderContinuationOwner,
|
|
3
|
+
OcxProviderContinuationState,
|
|
4
|
+
OcxParsedRequest,
|
|
5
|
+
OcxProviderConfig,
|
|
6
|
+
OcxReasoningReplayIdentity,
|
|
7
|
+
AdapterEvent,
|
|
8
|
+
} from "../../types";
|
|
9
|
+
import {
|
|
10
|
+
isValidProviderContinuationOwner,
|
|
11
|
+
sameProviderContinuationOwner,
|
|
12
|
+
providerContinuationOwnerFromReplayIdentity,
|
|
13
|
+
providerContinuationRouteScope,
|
|
14
|
+
} from "../../responses/provider-continuation";
|
|
15
|
+
import {
|
|
16
|
+
reasoningReplayDestinationIdentity,
|
|
17
|
+
reasoningReplayOAuthCredentialIdentity,
|
|
18
|
+
durableReplayCredentialIdentity,
|
|
19
|
+
reasoningReplayCodexCredentialIdentity,
|
|
20
|
+
reasoningReplayKeyCredentialIdentity,
|
|
21
|
+
durableReplayDestinationIdentity,
|
|
22
|
+
bindReasoningReplayScope,
|
|
23
|
+
reasoningReplayServingIdentityChanged,
|
|
24
|
+
reasoningReplayOpaqueBlobRejectionMemoized,
|
|
25
|
+
} from "../../responses/reasoning-replay-cache";
|
|
26
|
+
import type { OAuthAccessSnapshot } from "../../oauth";
|
|
27
|
+
import type { CodexAuthContext } from "../../codex/auth-context";
|
|
28
|
+
import { thoughtSignatureReplaySalt } from "../../responses/thought-signature-replay";
|
|
29
|
+
import { randomUUID } from "node:crypto";
|
|
30
|
+
import { requiresPlaintextReasoningReplay } from "../../adapters/openai-responses/passthrough";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Adapters whose continuation state must survive Codex's store:false requests.
|
|
34
|
+
*/
|
|
35
|
+
export function adapterNeedsForcedContinuation(name: string): boolean {
|
|
36
|
+
return name === "kiro" || name === "cursor";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
export type ContinuationOwnerRead =
|
|
41
|
+
| { kind: "missing" }
|
|
42
|
+
| { kind: "invalid" }
|
|
43
|
+
| { kind: "valid"; owner: OcxProviderContinuationOwner };
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
export function readProviderContinuationOwner(
|
|
47
|
+
state: OcxProviderContinuationState | undefined,
|
|
48
|
+
): ContinuationOwnerRead {
|
|
49
|
+
if (!state || state.__ocxOwner === undefined) return { kind: "missing" };
|
|
50
|
+
const owner = state.__ocxOwner;
|
|
51
|
+
if (!isValidProviderContinuationOwner(owner)) return { kind: "invalid" };
|
|
52
|
+
return { kind: "valid", owner: { ...owner } };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
export function providerContinuationPayload(
|
|
57
|
+
state: OcxProviderContinuationState | undefined,
|
|
58
|
+
): OcxProviderContinuationState | undefined {
|
|
59
|
+
if (!state) return undefined;
|
|
60
|
+
const cloned = structuredClone(state);
|
|
61
|
+
delete cloned.__ocxOwner;
|
|
62
|
+
return Object.keys(cloned).length > 0 ? cloned : undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
export function bindProviderContinuationForRoute(
|
|
67
|
+
parsed: OcxParsedRequest,
|
|
68
|
+
currentOwner: OcxProviderContinuationOwner | undefined,
|
|
69
|
+
): void {
|
|
70
|
+
const candidate = parsed._providerContinuationCandidate;
|
|
71
|
+
const storedOwner = readProviderContinuationOwner(candidate);
|
|
72
|
+
const mayRestore = storedOwner.kind === "valid"
|
|
73
|
+
&& !!currentOwner
|
|
74
|
+
&& sameProviderContinuationOwner(storedOwner.owner, currentOwner);
|
|
75
|
+
const restored = mayRestore ? providerContinuationPayload(candidate) : undefined;
|
|
76
|
+
if (restored) parsed._providerContinuation = restored;
|
|
77
|
+
else delete parsed._providerContinuation;
|
|
78
|
+
const cursorConversationId = restored?.cursor?.conversationId;
|
|
79
|
+
if (cursorConversationId) parsed._cursorConversationId = cursorConversationId;
|
|
80
|
+
else delete parsed._cursorConversationId;
|
|
81
|
+
if (currentOwner) parsed._providerContinuationOwner = { ...currentOwner };
|
|
82
|
+
else delete parsed._providerContinuationOwner;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
export function providerContinuationDestinationIdentity(
|
|
87
|
+
parsed: OcxParsedRequest,
|
|
88
|
+
provider: OcxProviderConfig,
|
|
89
|
+
): string | undefined {
|
|
90
|
+
const kiroContext = parsed._kiroAuthContext;
|
|
91
|
+
return reasoningReplayDestinationIdentity(JSON.stringify([
|
|
92
|
+
provider.baseUrl.trim().replace(/\/+$/, ""),
|
|
93
|
+
provider.responsesPath ?? "",
|
|
94
|
+
kiroContext?.profileArn ?? "",
|
|
95
|
+
kiroContext?.apiRegion ?? "",
|
|
96
|
+
kiroContext?.ssoRegion ?? "",
|
|
97
|
+
]));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
export function bindRouteReasoningReplayScope(args: {
|
|
102
|
+
parsed: OcxParsedRequest;
|
|
103
|
+
providerName: string;
|
|
104
|
+
provider: OcxProviderConfig;
|
|
105
|
+
adapterName: string;
|
|
106
|
+
oauthCredentialSnapshot?: Pick<OAuthAccessSnapshot, "accountId" | "generation">;
|
|
107
|
+
codexAuthContext?: CodexAuthContext;
|
|
108
|
+
forwardHeaders?: Headers;
|
|
109
|
+
}): void {
|
|
110
|
+
const { parsed, providerName, provider, adapterName } = args;
|
|
111
|
+
const replayIdentity = routeReasoningReplayIdentity({
|
|
112
|
+
...args,
|
|
113
|
+
modelId: parsed.modelId,
|
|
114
|
+
});
|
|
115
|
+
const continuationDestinationIdentity = providerContinuationDestinationIdentity(parsed, provider);
|
|
116
|
+
const continuationOwner = providerContinuationOwnerFromReplayIdentity(
|
|
117
|
+
replayIdentity && continuationDestinationIdentity
|
|
118
|
+
? { ...replayIdentity, providerDestinationIdentity: continuationDestinationIdentity }
|
|
119
|
+
: undefined,
|
|
120
|
+
);
|
|
121
|
+
if (adapterName === "cursor") {
|
|
122
|
+
// The final route owner is authoritative for Cursor and supersedes the account-derived
|
|
123
|
+
// seed assigned before route binding. A Cursor conversation must be scoped to the exact
|
|
124
|
+
// provider/destination/adapter/model/credential that serves it.
|
|
125
|
+
if (continuationOwner) parsed._cursorIdentityScope = providerContinuationRouteScope(continuationOwner);
|
|
126
|
+
else if (!parsed._cursorIdentityScope?.startsWith("cursor-unowned:")) {
|
|
127
|
+
// Prevent the adapter's token-only fallback from recreating a provider-private id after the
|
|
128
|
+
// route owner failed closed. The sentinel is per parsed request and contains no credential.
|
|
129
|
+
parsed._cursorIdentityScope = `cursor-unowned:${randomUUID()}`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
bindReasoningReplayScope(
|
|
133
|
+
parsed._reasoningReplayScope,
|
|
134
|
+
replayIdentity,
|
|
135
|
+
);
|
|
136
|
+
// Keep this sticky for the whole outbound request: a later auth/key rebind may compare equal
|
|
137
|
+
// after the first mismatch, but it cannot make history minted by the prior route decodable.
|
|
138
|
+
if (reasoningReplayServingIdentityChanged(parsed._reasoningReplayScope)) {
|
|
139
|
+
parsed._stripReasoningEncryptedContent = true;
|
|
140
|
+
}
|
|
141
|
+
if (reasoningReplayOpaqueBlobRejectionMemoized(parsed._reasoningReplayScope)) {
|
|
142
|
+
parsed._stripReasoningEncryptedContent = true;
|
|
143
|
+
}
|
|
144
|
+
bindProviderContinuationForRoute(parsed, continuationOwner);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
function routeReasoningReplayIdentity(args: {
|
|
149
|
+
providerName: string;
|
|
150
|
+
provider: OcxProviderConfig;
|
|
151
|
+
adapterName: string;
|
|
152
|
+
modelId: string;
|
|
153
|
+
oauthCredentialSnapshot?: Pick<OAuthAccessSnapshot, "accountId" | "generation">;
|
|
154
|
+
codexAuthContext?: CodexAuthContext;
|
|
155
|
+
forwardHeaders?: Headers;
|
|
156
|
+
}): OcxReasoningReplayIdentity | undefined {
|
|
157
|
+
const { providerName, provider, adapterName, modelId } = args;
|
|
158
|
+
let credentialIdentity: string | undefined;
|
|
159
|
+
let credentialDurableIdentity: string | undefined;
|
|
160
|
+
const durableSalt = thoughtSignatureReplaySalt();
|
|
161
|
+
if (provider.authMode === "oauth") {
|
|
162
|
+
credentialIdentity = reasoningReplayOAuthCredentialIdentity(
|
|
163
|
+
args.oauthCredentialSnapshot,
|
|
164
|
+
provider.headers,
|
|
165
|
+
);
|
|
166
|
+
// The persisted account-slot id survives token refresh and restarts; the rotating
|
|
167
|
+
// generation deliberately does NOT participate (#1926 design: rotation-safe).
|
|
168
|
+
credentialDurableIdentity = durableReplayCredentialIdentity(
|
|
169
|
+
"oauth",
|
|
170
|
+
args.oauthCredentialSnapshot?.accountId,
|
|
171
|
+
provider.headers,
|
|
172
|
+
durableSalt,
|
|
173
|
+
);
|
|
174
|
+
} else if (provider.authMode === "forward") {
|
|
175
|
+
const poolContext = args.codexAuthContext?.kind === "pool"
|
|
176
|
+
|| args.codexAuthContext?.kind === "main-pool"
|
|
177
|
+
? args.codexAuthContext
|
|
178
|
+
: undefined;
|
|
179
|
+
credentialIdentity = reasoningReplayCodexCredentialIdentity({
|
|
180
|
+
authorization: poolContext
|
|
181
|
+
? `Bearer ${poolContext.accessToken}`
|
|
182
|
+
: args.forwardHeaders?.get("authorization"),
|
|
183
|
+
chatgptAccountId: poolContext?.chatgptAccountId
|
|
184
|
+
?? args.forwardHeaders?.get("chatgpt-account-id"),
|
|
185
|
+
accountId: poolContext?.accountId,
|
|
186
|
+
credentialGeneration: poolContext?.kind === "pool"
|
|
187
|
+
? poolContext.generation
|
|
188
|
+
: undefined,
|
|
189
|
+
writerGeneration: poolContext?.writerGeneration,
|
|
190
|
+
headers: provider.headers,
|
|
191
|
+
});
|
|
192
|
+
// Durable identity requires a STABLE, TRUSTED account handle. Pool context comes from
|
|
193
|
+
// our own account store; a client-supplied chatgpt-account-id header is attacker
|
|
194
|
+
// -influenceable bucket selection and a bearer alone is rotating material — both are
|
|
195
|
+
// refused, so direct-forward turns get no durable scope (fail closed; the in-process
|
|
196
|
+
// cache still covers same-process replay).
|
|
197
|
+
const codexDurableHandle = poolContext?.accountId
|
|
198
|
+
?? poolContext?.chatgptAccountId
|
|
199
|
+
?? undefined;
|
|
200
|
+
credentialDurableIdentity = durableReplayCredentialIdentity(
|
|
201
|
+
"codex",
|
|
202
|
+
codexDurableHandle ?? undefined,
|
|
203
|
+
provider.headers,
|
|
204
|
+
durableSalt,
|
|
205
|
+
);
|
|
206
|
+
} else if (provider.authMode !== "local") {
|
|
207
|
+
credentialIdentity = reasoningReplayKeyCredentialIdentity(provider);
|
|
208
|
+
credentialDurableIdentity = durableReplayCredentialIdentity(
|
|
209
|
+
"key",
|
|
210
|
+
nonEmptyProviderApiKey(provider),
|
|
211
|
+
provider.headers,
|
|
212
|
+
durableSalt,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
const providerDestinationIdentity = reasoningReplayDestinationIdentity(provider.baseUrl);
|
|
216
|
+
return credentialIdentity && providerDestinationIdentity
|
|
217
|
+
? {
|
|
218
|
+
providerName,
|
|
219
|
+
providerDestinationIdentity,
|
|
220
|
+
providerDestinationDurableIdentity: durableReplayDestinationIdentity(provider.baseUrl),
|
|
221
|
+
adapterName,
|
|
222
|
+
modelId,
|
|
223
|
+
credentialIdentity,
|
|
224
|
+
...(credentialDurableIdentity ? { credentialDurableIdentity } : {}),
|
|
225
|
+
}
|
|
226
|
+
: undefined;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Whether this exact route cannot satisfy its documented plaintext reasoning replay contract.
|
|
232
|
+
*
|
|
233
|
+
* A generic plaintext-preserving gateway is not made ineligible. Unknown serving provenance also
|
|
234
|
+
* stays eligible; only a proven route mismatch may suppress a combo candidate.
|
|
235
|
+
*/
|
|
236
|
+
export function mandatoryResponsesReasoningReplayUnavailable(args: {
|
|
237
|
+
body: unknown;
|
|
238
|
+
clientThreadId: string | undefined;
|
|
239
|
+
providerName: string;
|
|
240
|
+
provider: OcxProviderConfig;
|
|
241
|
+
adapterName: string;
|
|
242
|
+
modelId: string;
|
|
243
|
+
}): boolean {
|
|
244
|
+
const { body, clientThreadId, provider, adapterName } = args;
|
|
245
|
+
if (
|
|
246
|
+
adapterName !== "openai-responses"
|
|
247
|
+
|| !requiresPlaintextReasoningReplay(provider)
|
|
248
|
+
|| !clientThreadId
|
|
249
|
+
|| !requestCarriesTools(body)
|
|
250
|
+
|| !hasOpaqueOnlyReasoningItem(body)
|
|
251
|
+
) return false;
|
|
252
|
+
|
|
253
|
+
const current = routeReasoningReplayIdentity(args);
|
|
254
|
+
return reasoningReplayServingIdentityChanged({ clientThreadId, current });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
function requestCarriesTools(body: unknown): boolean {
|
|
259
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return false;
|
|
260
|
+
return Array.isArray((body as { tools?: unknown }).tools)
|
|
261
|
+
&& (body as { tools: unknown[] }).tools.length > 0;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
function hasOpaqueOnlyReasoningItem(body: unknown): boolean {
|
|
266
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return false;
|
|
267
|
+
const input = (body as { input?: unknown }).input;
|
|
268
|
+
if (!Array.isArray(input)) return false;
|
|
269
|
+
return input.some(item => {
|
|
270
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return false;
|
|
271
|
+
const reasoning = item as Record<string, unknown>;
|
|
272
|
+
if (reasoning.type !== "reasoning" || typeof reasoning.encrypted_content !== "string") return false;
|
|
273
|
+
const content = reasoning.content;
|
|
274
|
+
const hasPlaintext = Array.isArray(content) && content.some(part =>
|
|
275
|
+
!!part && typeof part === "object" && !Array.isArray(part)
|
|
276
|
+
&& (part as Record<string, unknown>).type === "reasoning_text"
|
|
277
|
+
&& typeof (part as Record<string, unknown>).text === "string"
|
|
278
|
+
&& ((part as Record<string, unknown>).text as string).length > 0
|
|
279
|
+
);
|
|
280
|
+
return !hasPlaintext;
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
export function adapterResponseReachedServingTerminal(
|
|
286
|
+
events: readonly AdapterEvent[],
|
|
287
|
+
response: Readonly<Record<string, unknown>>,
|
|
288
|
+
): boolean {
|
|
289
|
+
return (response.status === "completed" || response.status === "incomplete")
|
|
290
|
+
&& events.some(event => event.type === "done" || event.type === "incomplete");
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
export function nonEmptyProviderApiKey(provider: OcxProviderConfig): string | undefined {
|
|
295
|
+
return typeof provider.apiKey === "string" && provider.apiKey.trim().length > 0
|
|
296
|
+
? provider.apiKey
|
|
297
|
+
: undefined;
|
|
298
|
+
}
|