@bitkyc08/opencodex 2.55.0 → 2.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/gui/dist/assets/{index-VuoiWj9J.js → index-D4zuyIxQ.js} +1 -1
- package/gui/dist/index.html +1 -1
- package/package.json +2 -1
- package/src/adapters/base.ts +21 -0
- package/src/adapters/cursor/transport-retry.ts +46 -1
- package/src/adapters/cursor.ts +4 -0
- package/src/adapters/kiro/adapter.ts +42 -1
- 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-schema.ts +477 -0
- package/src/adapters/openai-chat/wire.ts +50 -0
- package/src/adapters/openai-chat.ts +33 -1445
- 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 +611 -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 +34 -0
- package/src/bridge/internal.ts +174 -0
- package/src/bridge/response-json.ts +624 -0
- package/src/bridge/sse.ts +1444 -0
- package/src/bridge.ts +5 -2204
- package/src/chat/inbound.ts +12 -1
- package/src/codex/account-lifecycle.ts +3 -0
- package/src/codex/account-store.ts +71 -9
- 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 +554 -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 +422 -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 +95 -28
- 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 +304 -0
- package/src/codex/catalog/provider-fetch.ts +52 -2942
- package/src/codex/catalog/provider-models.ts +685 -0
- package/src/codex/catalog/restore.ts +132 -0
- package/src/codex/catalog/retained-sync.ts +706 -0
- package/src/codex/catalog/routed-gather.ts +858 -0
- package/src/codex/catalog/subagent-roster.ts +176 -0
- package/src/codex/catalog/sync.ts +52 -2698
- package/src/codex/inject/config-toml.ts +563 -0
- package/src/codex/inject/remove.ts +192 -0
- package/src/codex/inject/restore.ts +540 -0
- package/src/codex/inject/routing-classify.ts +109 -0
- package/src/codex/inject/routing-target.ts +125 -0
- package/src/codex/inject.ts +81 -1436
- package/src/codex/lineage.ts +458 -0
- package/src/codex/pool-refresh-backoff.ts +152 -0
- package/src/codex/routing/active-account.ts +194 -0
- package/src/codex/routing/cooldown-math.ts +275 -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 +703 -0
- package/src/codex/routing/thread-affinity.ts +538 -0
- package/src/codex/routing.ts +353 -2234
- 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/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/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 +539 -39
- package/src/lib/request-execution-budget.ts +69 -20
- package/src/lib/spend-reservation-ledger.ts +940 -0
- package/src/lib/upstream-retry.ts +55 -11
- package/src/lib/workflow-budget.ts +553 -30
- 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 +1221 -0
- package/src/providers/registry/entries-extended.ts +1204 -0
- package/src/providers/registry/model-seeds.ts +908 -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/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 +449 -0
- package/src/routing/probe-lease.ts +511 -0
- package/src/server/index/bounded-request.ts +88 -0
- package/src/server/index/live-sideband.ts +565 -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 +335 -0
- package/src/server/index.ts +40 -2547
- 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/request-log-conversation.ts +9 -7
- package/src/server/request-log.ts +245 -1
- package/src/server/responses/account-change-state.ts +233 -0
- package/src/server/responses/adapter-continuation.ts +514 -0
- package/src/server/responses/adapter-delivery.ts +214 -0
- package/src/server/responses/adapter-dispatch.ts +971 -0
- package/src/server/responses/compact.ts +59 -4
- package/src/server/responses/completion-policy.ts +33 -0
- package/src/server/responses/core-auth.ts +527 -0
- package/src/server/responses/core-codex-account.ts +859 -0
- package/src/server/responses/core-combo-failure.ts +210 -0
- package/src/server/responses/core-combo.ts +707 -0
- package/src/server/responses/core-errors.ts +152 -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 +225 -0
- package/src/server/responses/core.ts +192 -8893
- package/src/server/responses/passthrough-delivery.ts +856 -0
- package/src/server/responses/passthrough-dispatch.ts +1476 -0
- package/src/server/responses/passthrough-execution.ts +54 -0
- package/src/server/responses/request-prepare.ts +970 -0
- package/src/server/responses/request-send-budget.ts +164 -0
- package/src/server/responses/request-sidecar-auth.ts +149 -0
- package/src/server/responses/request-transport.ts +744 -0
- package/src/server/responses/response-effects.ts +157 -0
- package/src/server/responses/run-turn-execution.ts +448 -0
- package/src/server/responses/sidecar-execution.ts +469 -0
- package/src/server/responses-image-gen-repair.ts +1 -1
- package/src/server/workflow-refusal.ts +84 -0
- package/src/types/config.ts +30 -0
- package/src/usage/log.ts +146 -0
- package/src/usage/summary.ts +171 -21
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type { ResponsesRequestContext, ResponsesAdmissionState } from "./core-options";
|
|
2
|
+
import type { PreparedResponsesRequest } from "./request-prepare";
|
|
3
|
+
import type { ResponsesSidecarAuth } from "./request-sidecar-auth";
|
|
4
|
+
import type { OcxProviderContinuationState } from "../../types";
|
|
5
|
+
import { providerContinuationPayload } from "./core-replay";
|
|
6
|
+
import { mergeProviderContinuationPayload } from "../../responses/provider-continuation";
|
|
7
|
+
import { commitReasoningReplayServingIdentity } from "../../responses/reasoning-replay-cache";
|
|
8
|
+
import { rememberServingConversationStateIssuer } from "./account-change-state";
|
|
9
|
+
import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
|
|
10
|
+
import { contextRelayActivated } from "../../codex/context-compat";
|
|
11
|
+
import { recordContextSessionOwner } from "../../codex/context-owner";
|
|
12
|
+
import { resolveContextPrincipal } from "../auth-cors";
|
|
13
|
+
import { COMPACT_PROMPT } from "../../responses/compaction";
|
|
14
|
+
import type { RoutedNamespaceToolAliases } from "../../responses/namespace-tool-compat";
|
|
15
|
+
import type { MuseToolNameAliases } from "../../responses/muse-tool-name-alias";
|
|
16
|
+
import type { AdapterRequest } from "../../adapters/base";
|
|
17
|
+
|
|
18
|
+
/** Owns completion callbacks, replay publication, and live tool aliases. */
|
|
19
|
+
export function createResponsesEffects(
|
|
20
|
+
requestContext: Pick<ResponsesRequestContext, "options" | "req" | "config">,
|
|
21
|
+
admissionState: ResponsesAdmissionState,
|
|
22
|
+
requestState: Pick<
|
|
23
|
+
PreparedResponsesRequest,
|
|
24
|
+
| "parsed"
|
|
25
|
+
| "poolAffinityKey"
|
|
26
|
+
| "route"
|
|
27
|
+
| "substituteMainCredential"
|
|
28
|
+
>,
|
|
29
|
+
sidecarState: Pick<ResponsesSidecarAuth, "routedCompaction">,
|
|
30
|
+
) {
|
|
31
|
+
const { options, req, config } = requestContext;
|
|
32
|
+
const { parsed, poolAffinityKey, route, substituteMainCredential } = requestState;
|
|
33
|
+
const { routedCompaction } = sidecarState;
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
const recordTerminalOutcomes = options.recordTerminalOutcomes !== false;
|
|
37
|
+
let responseCompletionNotified = false;
|
|
38
|
+
let responseCompletionCancelled = false;
|
|
39
|
+
const cancelResponseCompletion = (): void => { responseCompletionCancelled = true; };
|
|
40
|
+
const notifyResponseComplete = (response: { status?: unknown; model?: unknown }): void => {
|
|
41
|
+
if (responseCompletionNotified || responseCompletionCancelled
|
|
42
|
+
|| options.abortSignal?.aborted || req.signal.aborted
|
|
43
|
+
|| response.status !== "completed"
|
|
44
|
+
|| typeof response.model !== "string" || !response.model.trim()) return;
|
|
45
|
+
responseCompletionNotified = true;
|
|
46
|
+
options.onResponseComplete?.(response.model);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const continuationStateForResponse = (
|
|
50
|
+
emitted?: OcxProviderContinuationState,
|
|
51
|
+
): OcxProviderContinuationState | undefined => {
|
|
52
|
+
const cursorConversationId = parsed._cursorConversationId;
|
|
53
|
+
const inherited = providerContinuationPayload(parsed._providerContinuation);
|
|
54
|
+
const emittedPayload = providerContinuationPayload(emitted);
|
|
55
|
+
if (!emittedPayload && !inherited && !cursorConversationId) return undefined;
|
|
56
|
+
const merged = mergeProviderContinuationPayload(
|
|
57
|
+
inherited ?? {},
|
|
58
|
+
emittedPayload ?? {},
|
|
59
|
+
) as OcxProviderContinuationState;
|
|
60
|
+
if (cursorConversationId) {
|
|
61
|
+
merged.cursor = { ...(merged.cursor ?? {}), conversationId: cursorConversationId };
|
|
62
|
+
}
|
|
63
|
+
return parsed._providerContinuationOwner
|
|
64
|
+
? { ...merged, __ocxOwner: { ...parsed._providerContinuationOwner } }
|
|
65
|
+
: merged;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Remote compaction v2 on a ROUTED model: Codex sent `compaction_trigger` and requires exactly
|
|
69
|
+
// one `{type:"compaction"}` output item (codex-rs compact_remote_v2.rs). Passthrough handles it
|
|
70
|
+
// natively upstream; here we run the routed model as a plain summarizer — no tools, no web-search
|
|
71
|
+
// sidecar — and the bridge appends the synthetic compaction item (src/responses/compaction.ts).
|
|
72
|
+
// A Responses-shaped wire does not imply support for Codex's private
|
|
73
|
+
// `compaction_trigger` item — only the canonical ChatGPT backend speaks that
|
|
74
|
+
// contract. An API-key gateway would receive the trigger, answer with an ordinary
|
|
75
|
+
// message, and leave Codex fataling on a missing compaction item (#422).
|
|
76
|
+
const commitReasoningReplayServingRoute = (outboundHeaders?: HeadersInit): void => {
|
|
77
|
+
commitReasoningReplayServingIdentity(parsed._reasoningReplayScope);
|
|
78
|
+
rememberServingConversationStateIssuer(admissionState.authCtx, poolAffinityKey);
|
|
79
|
+
// History has no model namespace. Record the account that actually accepted this
|
|
80
|
+
// final attempt, after refresh/failover, rather than guessing from mutable affinity.
|
|
81
|
+
// Recording is relay state. With the feature off there is no relay, so building an owner
|
|
82
|
+
// registry for it is out of scope for this request.
|
|
83
|
+
if (outboundHeaders && isCanonicalOpenAiForwardProvider(route.provider) && contextRelayActivated()) {
|
|
84
|
+
recordContextSessionOwner(resolveContextPrincipal(req, config, options.admission), req.headers,
|
|
85
|
+
route.provider.baseUrl, admissionState.authCtx, new Headers(outboundHeaders), substituteMainCredential);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
if (routedCompaction) {
|
|
89
|
+
delete parsed.context.tools;
|
|
90
|
+
delete parsed._webSearch;
|
|
91
|
+
delete parsed.options.toolChoice;
|
|
92
|
+
delete parsed.options.parallelToolCalls;
|
|
93
|
+
// The compaction turn is a plain prose summary; a surviving structured-output format
|
|
94
|
+
// would force schema-constrained JSON into the synthetic compaction item. The flag and
|
|
95
|
+
// the raw `text` control go too: the key-mode openai-responses adapter builds from
|
|
96
|
+
// _rawBody, so a surviving format there would still reach the upstream. (The Kiro
|
|
97
|
+
// guard no longer reads _rawBody.text; it refuses structured output only.)
|
|
98
|
+
delete parsed.options.textFormat;
|
|
99
|
+
delete parsed._structuredOutput;
|
|
100
|
+
if (parsed._rawBody && typeof parsed._rawBody === "object") {
|
|
101
|
+
delete (parsed._rawBody as Record<string, unknown>).text;
|
|
102
|
+
}
|
|
103
|
+
parsed.context.messages.push({ role: "user", content: COMPACT_PROMPT, timestamp: Date.now() });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let routedNamespaceToolAliases: RoutedNamespaceToolAliases = new Map();
|
|
107
|
+
let plaintextV2AgentMessageToolNames: ReadonlySet<string> = new Set();
|
|
108
|
+
let plaintextV2AgentMessageAliasedToolNames: ReadonlySet<string> = new Set();
|
|
109
|
+
let routedMuseToolNameAliases: MuseToolNameAliases = new Map();
|
|
110
|
+
const refreshRequestToolAliases = (builtRequest: AdapterRequest): void => {
|
|
111
|
+
routedNamespaceToolAliases = builtRequest.convertedRoutedNamespaceToolAliases ?? new Map();
|
|
112
|
+
plaintextV2AgentMessageToolNames = builtRequest.plaintextV2AgentMessageToolNames ?? new Set();
|
|
113
|
+
plaintextV2AgentMessageAliasedToolNames = builtRequest.plaintextV2AgentMessageAliasedToolNames ?? new Set();
|
|
114
|
+
routedMuseToolNameAliases = builtRequest.convertedMuseToolNameAliases ?? new Map();
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
recordTerminalOutcomes,
|
|
119
|
+
get responseCompletionCancelled(): typeof responseCompletionCancelled {
|
|
120
|
+
return responseCompletionCancelled;
|
|
121
|
+
},
|
|
122
|
+
set responseCompletionCancelled(value: typeof responseCompletionCancelled) {
|
|
123
|
+
responseCompletionCancelled = value;
|
|
124
|
+
},
|
|
125
|
+
cancelResponseCompletion,
|
|
126
|
+
notifyResponseComplete,
|
|
127
|
+
continuationStateForResponse,
|
|
128
|
+
commitReasoningReplayServingRoute,
|
|
129
|
+
get routedNamespaceToolAliases(): RoutedNamespaceToolAliases {
|
|
130
|
+
return routedNamespaceToolAliases;
|
|
131
|
+
},
|
|
132
|
+
set routedNamespaceToolAliases(value: RoutedNamespaceToolAliases) {
|
|
133
|
+
routedNamespaceToolAliases = value;
|
|
134
|
+
},
|
|
135
|
+
get plaintextV2AgentMessageToolNames(): ReadonlySet<string> {
|
|
136
|
+
return plaintextV2AgentMessageToolNames;
|
|
137
|
+
},
|
|
138
|
+
set plaintextV2AgentMessageToolNames(value: ReadonlySet<string>) {
|
|
139
|
+
plaintextV2AgentMessageToolNames = value;
|
|
140
|
+
},
|
|
141
|
+
get plaintextV2AgentMessageAliasedToolNames(): ReadonlySet<string> {
|
|
142
|
+
return plaintextV2AgentMessageAliasedToolNames;
|
|
143
|
+
},
|
|
144
|
+
set plaintextV2AgentMessageAliasedToolNames(value: ReadonlySet<string>) {
|
|
145
|
+
plaintextV2AgentMessageAliasedToolNames = value;
|
|
146
|
+
},
|
|
147
|
+
get routedMuseToolNameAliases(): MuseToolNameAliases {
|
|
148
|
+
return routedMuseToolNameAliases;
|
|
149
|
+
},
|
|
150
|
+
set routedMuseToolNameAliases(value: MuseToolNameAliases) {
|
|
151
|
+
routedMuseToolNameAliases = value;
|
|
152
|
+
},
|
|
153
|
+
refreshRequestToolAliases,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export type ResponsesEffects = Exclude<ReturnType<typeof createResponsesEffects>, Response>;
|
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import type { ResponsesRequestContext, ResponsesAdmissionState } from "./core-options";
|
|
2
|
+
import type { PreparedResponsesRequest } from "./request-prepare";
|
|
3
|
+
import type { ResponsesTransport } from "./request-transport";
|
|
4
|
+
import type { ResponsesSidecarAuth } from "./request-sidecar-auth";
|
|
5
|
+
import type { ResponsesEffects } from "./response-effects";
|
|
6
|
+
import type { ResponsesSendBudget } from "./request-send-budget";
|
|
7
|
+
import type { ResponsesCompletionPolicy } from "./completion-policy";
|
|
8
|
+
import { linkAbortSignal, runTurnAdapterSseResponses } from "./core-lifetime";
|
|
9
|
+
import { createAdapterEventQueue, preflightAdapterEvents } from "../../adapters/run-turn-queue";
|
|
10
|
+
import {
|
|
11
|
+
bindRouteReasoningReplayScope,
|
|
12
|
+
adapterNeedsForcedContinuation,
|
|
13
|
+
adapterResponseReachedServingTerminal,
|
|
14
|
+
} from "./core-replay";
|
|
15
|
+
import { sealRequestAttemptIdentity, noteAttemptSend, recordAttemptCredentialSource } from "../request-log";
|
|
16
|
+
import { waitForProviderRequestSlot, RequestPacingQueueOverloadError } from "../../providers/request-pacing";
|
|
17
|
+
import type { AdapterEventQueue } from "../../adapters/run-turn-queue";
|
|
18
|
+
import type { AttemptRecoveryKind } from "../../usage/log";
|
|
19
|
+
import { providerFetch } from "./fetch-helpers";
|
|
20
|
+
import { normalizeLogConversationId } from "../request-log-conversation";
|
|
21
|
+
import type { AdapterEvent, OcxProviderContinuationState } from "../../types";
|
|
22
|
+
import { adapterFailureFromMessage } from "../../lib/errors";
|
|
23
|
+
import {
|
|
24
|
+
GENERIC_OAUTH_MAX_FAILOVERS_PER_REQUEST,
|
|
25
|
+
isGenericOAuthFailoverEnabled,
|
|
26
|
+
rotateGenericOAuthAccountOn429,
|
|
27
|
+
failoverAccountSnapshot,
|
|
28
|
+
} from "../../oauth/generic-account-failover";
|
|
29
|
+
import { resolveWireProtocolOverride } from "../adapter-resolve";
|
|
30
|
+
import { formatErrorResponse, bridgeToResponsesSSE, buildResponseJSON } from "../../bridge";
|
|
31
|
+
import { redactSecretString } from "../../lib/redact";
|
|
32
|
+
import {
|
|
33
|
+
guardEmptyCompletionEventStream,
|
|
34
|
+
observeEmptyCompletion,
|
|
35
|
+
emptyCompletionNotice,
|
|
36
|
+
} from "./empty-completion-guard";
|
|
37
|
+
import { rememberResponseState } from "../../responses/state";
|
|
38
|
+
import { trackStreamLifetime } from "../lifecycle";
|
|
39
|
+
import { awaitThoughtSignatureDurability } from "../../responses/thought-signature-replay";
|
|
40
|
+
|
|
41
|
+
/** One responsibility of the Responses request pipeline; state owners are explicit. */
|
|
42
|
+
export async function executeResponsesRunTurn(
|
|
43
|
+
requestContext: Pick<ResponsesRequestContext, "options" | "logCtx" | "config">,
|
|
44
|
+
admissionState: ResponsesAdmissionState,
|
|
45
|
+
requestState: Pick<
|
|
46
|
+
PreparedResponsesRequest,
|
|
47
|
+
| "parsed"
|
|
48
|
+
| "route"
|
|
49
|
+
| "selectedForwardHeaders"
|
|
50
|
+
| "translatorBudget"
|
|
51
|
+
| "inboundWire"
|
|
52
|
+
| "toolBridgeMaps"
|
|
53
|
+
| "rememberKiroDeliveredFinalAnswer"
|
|
54
|
+
| "responseStateOptions"
|
|
55
|
+
>,
|
|
56
|
+
transportState: Pick<
|
|
57
|
+
ResponsesTransport,
|
|
58
|
+
| "selectionIsCurrent"
|
|
59
|
+
| "adapterBindings"
|
|
60
|
+
| "runTurnAdapter"
|
|
61
|
+
| "refreshRunTurnAdapter"
|
|
62
|
+
| "replayOAuthCredentialSnapshot"
|
|
63
|
+
| "genericFailoverAccountId"
|
|
64
|
+
| "genericFailovers"
|
|
65
|
+
| "applyFailoverSnapshot"
|
|
66
|
+
| "resolveSelectionAdapter"
|
|
67
|
+
| "adapter"
|
|
68
|
+
>,
|
|
69
|
+
sidecarState: Pick<ResponsesSidecarAuth, "routedCompaction">,
|
|
70
|
+
responseEffects: Pick<
|
|
71
|
+
ResponsesEffects,
|
|
72
|
+
| "cancelResponseCompletion"
|
|
73
|
+
| "commitReasoningReplayServingRoute"
|
|
74
|
+
| "continuationStateForResponse"
|
|
75
|
+
| "notifyResponseComplete"
|
|
76
|
+
>,
|
|
77
|
+
sendBudgetState: Pick<ResponsesSendBudget, "adapterSendBudget" | "reserveCredentialHop">,
|
|
78
|
+
completionPolicy: Pick<ResponsesCompletionPolicy, "emptyCompletionGuardEnabled">,
|
|
79
|
+
): Promise<Response> {
|
|
80
|
+
const { options, logCtx, config } = requestContext;
|
|
81
|
+
const {
|
|
82
|
+
selectionIsCurrent,
|
|
83
|
+
adapterBindings,
|
|
84
|
+
refreshRunTurnAdapter,
|
|
85
|
+
applyFailoverSnapshot,
|
|
86
|
+
resolveSelectionAdapter,
|
|
87
|
+
} = transportState;
|
|
88
|
+
const {
|
|
89
|
+
parsed,
|
|
90
|
+
route,
|
|
91
|
+
translatorBudget,
|
|
92
|
+
inboundWire,
|
|
93
|
+
toolBridgeMaps,
|
|
94
|
+
rememberKiroDeliveredFinalAnswer,
|
|
95
|
+
responseStateOptions,
|
|
96
|
+
} = requestState;
|
|
97
|
+
const { adapterSendBudget, reserveCredentialHop } = sendBudgetState;
|
|
98
|
+
const { emptyCompletionGuardEnabled } = completionPolicy;
|
|
99
|
+
const {
|
|
100
|
+
cancelResponseCompletion,
|
|
101
|
+
commitReasoningReplayServingRoute,
|
|
102
|
+
continuationStateForResponse,
|
|
103
|
+
notifyResponseComplete,
|
|
104
|
+
} = responseEffects;
|
|
105
|
+
const { routedCompaction } = sidecarState;
|
|
106
|
+
|
|
107
|
+
const runTurnAbort = new AbortController();
|
|
108
|
+
const cleanupRunTurnAbort = linkAbortSignal(runTurnAbort, options.abortSignal);
|
|
109
|
+
const queue = createAdapterEventQueue({
|
|
110
|
+
onBacklogExceeded: () => runTurnAbort.abort(),
|
|
111
|
+
});
|
|
112
|
+
const refreshRunTurnSelection = async (): Promise<void> => {
|
|
113
|
+
if (selectionIsCurrent(adapterBindings.get(transportState.runTurnAdapter))) return;
|
|
114
|
+
await refreshRunTurnAdapter(parsed);
|
|
115
|
+
bindRouteReasoningReplayScope({ parsed, providerName: route.providerName, provider: route.provider,
|
|
116
|
+
adapterName: transportState.runTurnAdapter.name, oauthCredentialSnapshot: transportState.replayOAuthCredentialSnapshot });
|
|
117
|
+
sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, transportState.runTurnAdapter.name, logCtx.accountLogLabel);
|
|
118
|
+
};
|
|
119
|
+
// Initial admission must settle before the streaming Response commits HTTP 200.
|
|
120
|
+
// Let the outer Responses facade preserve the local retryable-429 contract.
|
|
121
|
+
try {
|
|
122
|
+
await waitForProviderRequestSlot(route.providerName, route.provider, route.modelId, runTurnAbort.signal);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
cleanupRunTurnAbort();
|
|
125
|
+
queue.close();
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
// One attempt of the runTurn transport, against an explicit queue. The
|
|
129
|
+
// empty-completion guard re-invokes the IDENTICAL turn (same parsed request,
|
|
130
|
+
// same forwarded headers, same abort signal) through a fresh queue, so the
|
|
131
|
+
// attempt body must not capture the first queue. Each attempt consumes its
|
|
132
|
+
// own provider pacing slot (#1584): retries are paced like first attempts.
|
|
133
|
+
const runTurnAttempt = async (
|
|
134
|
+
targetQueue: AdapterEventQueue,
|
|
135
|
+
recovery?: AttemptRecoveryKind,
|
|
136
|
+
pacingSlotAcquired = false,
|
|
137
|
+
): Promise<void> => {
|
|
138
|
+
try {
|
|
139
|
+
if (!pacingSlotAcquired) {
|
|
140
|
+
await waitForProviderRequestSlot(route.providerName, route.provider, route.modelId, runTurnAbort.signal);
|
|
141
|
+
}
|
|
142
|
+
await refreshRunTurnSelection();
|
|
143
|
+
noteAttemptSend(logCtx.activeAttempt, logCtx.usageLogInputTokens, recovery);
|
|
144
|
+
const runTurnProviderFetch = providerFetch(
|
|
145
|
+
route.provider,
|
|
146
|
+
options.codexWsRuntimeIdentity,
|
|
147
|
+
{
|
|
148
|
+
providerName: route.providerName,
|
|
149
|
+
modelId: route.modelId,
|
|
150
|
+
// runTurnAttempt acquired this logical turn's first physical-request slot above.
|
|
151
|
+
// Cursor HTTP/1.1 consumes it for RunSSE; every BidiAppend and redial then waits on
|
|
152
|
+
// the same provider queue through this stateful wrapper.
|
|
153
|
+
pacingSlotAcquired: true,
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
await transportState.runTurnAdapter.runTurn?.(
|
|
157
|
+
parsed,
|
|
158
|
+
{
|
|
159
|
+
headers: requestState.selectedForwardHeaders,
|
|
160
|
+
abortSignal: runTurnAbort.signal,
|
|
161
|
+
translatorBudget,
|
|
162
|
+
providerFetch: runTurnProviderFetch,
|
|
163
|
+
// The only way the request budget reaches a transport the adapter owns. Without it
|
|
164
|
+
// a Cursor turn's inner ladder was three physical sends the cap read as one.
|
|
165
|
+
...(adapterSendBudget ? { sendBudget: adapterSendBudget } : {}),
|
|
166
|
+
},
|
|
167
|
+
targetQueue.push,
|
|
168
|
+
);
|
|
169
|
+
} catch (err) {
|
|
170
|
+
targetQueue.push(err instanceof RequestPacingQueueOverloadError
|
|
171
|
+
? {
|
|
172
|
+
type: "error",
|
|
173
|
+
status: 429,
|
|
174
|
+
errorType: "rate_limit_error",
|
|
175
|
+
retryable: true,
|
|
176
|
+
message: err.message,
|
|
177
|
+
}
|
|
178
|
+
: {
|
|
179
|
+
type: "error",
|
|
180
|
+
message: err instanceof Error ? err.message : String(err),
|
|
181
|
+
});
|
|
182
|
+
} finally {
|
|
183
|
+
// Cursor assigns a stable conversation id inside runTurn on the first headerless
|
|
184
|
+
// turn; backfill so Logs can filter/total that opening request (#330 / #522).
|
|
185
|
+
if (!logCtx.conversationId && parsed._cursorConversationId) {
|
|
186
|
+
logCtx.conversationId = normalizeLogConversationId(parsed._cursorConversationId);
|
|
187
|
+
}
|
|
188
|
+
targetQueue.close();
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const runTurn = async (): Promise<void> => runTurnAttempt(queue, undefined, true);
|
|
192
|
+
const rotateRunTurnAdapterOnPreflight429 = async (
|
|
193
|
+
error: Extract<AdapterEvent, { type: "error" }>,
|
|
194
|
+
): Promise<boolean> => {
|
|
195
|
+
const status = error.status ?? adapterFailureFromMessage(error.message).httpStatus;
|
|
196
|
+
if (
|
|
197
|
+
status !== 429
|
|
198
|
+
|| !transportState.genericFailoverAccountId
|
|
199
|
+
|| transportState.genericFailovers >= GENERIC_OAUTH_MAX_FAILOVERS_PER_REQUEST
|
|
200
|
+
|| !isGenericOAuthFailoverEnabled(config, route.providerName)
|
|
201
|
+
) return false;
|
|
202
|
+
// Intersection with the request's shared budget: the roster bound above answers "may this
|
|
203
|
+
// credential set rotate again", this answers "may this request send again at all". The
|
|
204
|
+
// replayed turn is dispatched by runTurnAttempt and never reaches `onSendsConsumed`, so
|
|
205
|
+
// this reservation is the charge. Refusing returns false, which leaves the preflight 429
|
|
206
|
+
// to reach the client exactly as the adapter produced it.
|
|
207
|
+
const hop = reserveCredentialHop(
|
|
208
|
+
"auth-recovery",
|
|
209
|
+
`${route.providerName}|${route.modelId}|runturn-oauth-429`,
|
|
210
|
+
);
|
|
211
|
+
if (!hop.allowed) return false;
|
|
212
|
+
const nextAccountId = rotateGenericOAuthAccountOn429(
|
|
213
|
+
config,
|
|
214
|
+
route.providerName,
|
|
215
|
+
transportState.genericFailoverAccountId,
|
|
216
|
+
null,
|
|
217
|
+
);
|
|
218
|
+
if (!nextAccountId) {
|
|
219
|
+
hop.permit?.release();
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
const snapshot = await failoverAccountSnapshot(route.providerName, nextAccountId);
|
|
224
|
+
transportState.genericFailovers += 1;
|
|
225
|
+
if (!await applyFailoverSnapshot(snapshot)) {
|
|
226
|
+
hop.permit?.release();
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
// A Cursor conversation/checkpoint is credential-scoped. The failed attempt emitted no
|
|
230
|
+
// client-visible bytes, so replay is safe, but carrying its account identity into the next
|
|
231
|
+
// account would not be. Let the rotated adapter derive a fresh identity and conversation.
|
|
232
|
+
parsed._cursorIdentityScope = undefined;
|
|
233
|
+
parsed._cursorConversationId = undefined;
|
|
234
|
+
if (parsed._providerContinuation?.cursor) {
|
|
235
|
+
const { cursor: _discardedCursor, ...otherProviderState } = parsed._providerContinuation;
|
|
236
|
+
parsed._providerContinuation = otherProviderState;
|
|
237
|
+
}
|
|
238
|
+
const rotatedProvider = resolveWireProtocolOverride(
|
|
239
|
+
route.providerName,
|
|
240
|
+
route.modelId,
|
|
241
|
+
route.provider,
|
|
242
|
+
inboundWire,
|
|
243
|
+
);
|
|
244
|
+
const rotatedAdapter = resolveSelectionAdapter(rotatedProvider, config.cacheRetention);
|
|
245
|
+
if (!rotatedAdapter.runTurn) {
|
|
246
|
+
hop.permit?.release();
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
transportState.runTurnAdapter = rotatedAdapter;
|
|
250
|
+
bindRouteReasoningReplayScope({
|
|
251
|
+
parsed,
|
|
252
|
+
providerName: route.providerName,
|
|
253
|
+
provider: rotatedProvider,
|
|
254
|
+
adapterName: rotatedAdapter.name,
|
|
255
|
+
oauthCredentialSnapshot: { accountId: snapshot.accountId, generation: snapshot.generation },
|
|
256
|
+
codexAuthContext: admissionState.authCtx,
|
|
257
|
+
forwardHeaders: requestState.selectedForwardHeaders,
|
|
258
|
+
});
|
|
259
|
+
sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, rotatedAdapter.name, logCtx.accountLogLabel);
|
|
260
|
+
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, rotatedAdapter.name);
|
|
261
|
+
// The caller replays the turn on this rotation, so the reservation is now confirmed.
|
|
262
|
+
hop.permit?.use();
|
|
263
|
+
return true;
|
|
264
|
+
} catch {
|
|
265
|
+
hop.permit?.release();
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
const preflightRunTurnFailover = async (
|
|
270
|
+
firstSource: AsyncIterable<AdapterEvent>,
|
|
271
|
+
): Promise<AsyncIterable<AdapterEvent>> => {
|
|
272
|
+
let source = firstSource;
|
|
273
|
+
while (true) {
|
|
274
|
+
const preflight = await preflightAdapterEvents(source);
|
|
275
|
+
if (!preflight.error || !(await rotateRunTurnAdapterOnPreflight429(preflight.error))) {
|
|
276
|
+
return preflight.stream;
|
|
277
|
+
}
|
|
278
|
+
const retryQueue = createAdapterEventQueue({
|
|
279
|
+
onBacklogExceeded: () => runTurnAbort.abort(),
|
|
280
|
+
});
|
|
281
|
+
void runTurnAttempt(retryQueue, "oauth-account-429");
|
|
282
|
+
source = retryQueue.stream();
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
// The empty-completion retry re-runs the turn against a fresh queue: the
|
|
286
|
+
// first queue is closed once its attempt settles, and pushing into it after
|
|
287
|
+
// close is a silent no-op.
|
|
288
|
+
const runTurnRetrySource = (): AsyncIterable<AdapterEvent> => {
|
|
289
|
+
const retryQueue = createAdapterEventQueue({
|
|
290
|
+
onBacklogExceeded: () => runTurnAbort.abort(),
|
|
291
|
+
});
|
|
292
|
+
void runTurnAttempt(retryQueue, "empty-completion");
|
|
293
|
+
return retryQueue.stream();
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const { toolNsMap, declaredToolNames, toolParameterSchemas, freeformToolNames, toolSearchToolNames } = toolBridgeMaps;
|
|
297
|
+
if (parsed.stream) {
|
|
298
|
+
void runTurn();
|
|
299
|
+
let eventSource: AsyncIterable<AdapterEvent> = queue.stream();
|
|
300
|
+
if (route.provider.authMode === "oauth" || (transportState.genericFailoverAccountId && isGenericOAuthFailoverEnabled(config, route.providerName))) {
|
|
301
|
+
// Preflight holds only heartbeats and the first meaningful event. A first-event 429 can be
|
|
302
|
+
// replayed transparently; after any output reaches the bridge, a later error stays terminal.
|
|
303
|
+
eventSource = await preflightRunTurnFailover(eventSource);
|
|
304
|
+
}
|
|
305
|
+
if (options.comboAttempt) {
|
|
306
|
+
const preflight = await preflightAdapterEvents(eventSource);
|
|
307
|
+
if (preflight.error || preflight.empty) {
|
|
308
|
+
runTurnAbort.abort();
|
|
309
|
+
queue.close();
|
|
310
|
+
const message = preflight.error?.message ?? "Adapter ended before producing a response";
|
|
311
|
+
return formatErrorResponse(502, "upstream_error", redactSecretString(message));
|
|
312
|
+
}
|
|
313
|
+
eventSource = preflight.stream;
|
|
314
|
+
}
|
|
315
|
+
const guardedSource = emptyCompletionGuardEnabled
|
|
316
|
+
? guardEmptyCompletionEventStream({
|
|
317
|
+
firstEvents: eventSource,
|
|
318
|
+
// Identical-turn retry: same parsed request, same headers, same
|
|
319
|
+
// signal — run the adapter transport again against a fresh queue.
|
|
320
|
+
continuation: runTurnRetrySource,
|
|
321
|
+
})
|
|
322
|
+
// Guard off (the default): leave the stream alone, but record that the turn ended
|
|
323
|
+
// empty so the user has something to correlate instead of an unexplained blank
|
|
324
|
+
// result (#2472). Retrying by default would re-send a turn that may already have had
|
|
325
|
+
// billable side effects, so the honest default is observability, not recovery.
|
|
326
|
+
: observeEmptyCompletion(eventSource, () => {
|
|
327
|
+
console.warn(emptyCompletionNotice(route.providerName, route.modelId));
|
|
328
|
+
});
|
|
329
|
+
const sseStream = bridgeToResponsesSSE(
|
|
330
|
+
guardedSource, parsed._responseModelId ?? parsed.modelId, toolNsMap, freeformToolNames, toolSearchToolNames,
|
|
331
|
+
() => {
|
|
332
|
+
cancelResponseCompletion();
|
|
333
|
+
runTurnAbort.abort();
|
|
334
|
+
queue.close();
|
|
335
|
+
}, 2_000,
|
|
336
|
+
{
|
|
337
|
+
translatorBudget,
|
|
338
|
+
replayCacheScope: parsed._reasoningReplayScope,
|
|
339
|
+
...(options.forceEmptyResponseId ? { responseId: "" } : {}),
|
|
340
|
+
stallTimeoutSec: config.stallTimeoutSec,
|
|
341
|
+
hideThinkingSummary: parsed.options.hideThinkingSummary,
|
|
342
|
+
declaredToolNames,
|
|
343
|
+
toolParameterSchemas,
|
|
344
|
+
...(options.onFirstOutput ? { onFirstOutput: options.onFirstOutput } : {}),
|
|
345
|
+
...(routedCompaction ? { compaction: true } : {}),
|
|
346
|
+
// grok-build's strict decoder dies on the typed response.heartbeat frame; its
|
|
347
|
+
// eventsource layer tolerates comment keep-alives. Codex needs the opposite.
|
|
348
|
+
...(logCtx.surface === "grok" ? { heartbeatStyle: "comment" as const } : {}),
|
|
349
|
+
onUsage: usage => {
|
|
350
|
+
// Raw adapter usage, pre wire-normalization: the bridged SSE now always carries
|
|
351
|
+
// zero-default detail objects, so provenance must come from here (cache_detail_missing).
|
|
352
|
+
logCtx.usageFromBridge = true;
|
|
353
|
+
if (usage) {
|
|
354
|
+
logCtx.usage = usage;
|
|
355
|
+
if (logCtx.activeAttempt) logCtx.activeAttempt.usage = usage;
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
onCompletedResponse: (response: Record<string, unknown>, providerState?: OcxProviderContinuationState) => {
|
|
359
|
+
commitReasoningReplayServingRoute();
|
|
360
|
+
rememberKiroDeliveredFinalAnswer(transportState.adapter.name, response);
|
|
361
|
+
if (!routedCompaction) {
|
|
362
|
+
rememberResponseState(
|
|
363
|
+
parsed._rawBody,
|
|
364
|
+
response,
|
|
365
|
+
continuationStateForResponse(providerState),
|
|
366
|
+
responseStateOptions(adapterNeedsForcedContinuation(transportState.adapter.name)),
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
notifyResponseComplete(response);
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
);
|
|
373
|
+
const bridgeTurnAc = new AbortController();
|
|
374
|
+
const trackedSse = trackStreamLifetime(sseStream, bridgeTurnAc, undefined, options.turnAdmissionLease);
|
|
375
|
+
const response = new Response(trackedSse, {
|
|
376
|
+
headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no" },
|
|
377
|
+
});
|
|
378
|
+
runTurnAdapterSseResponses.add(response);
|
|
379
|
+
return response;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
await runTurn();
|
|
383
|
+
const firstAttemptEvents = await queue.collect();
|
|
384
|
+
let runTurnEvents: AdapterEvent[] = firstAttemptEvents;
|
|
385
|
+
if (route.provider.authMode === "oauth" || (transportState.genericFailoverAccountId && isGenericOAuthFailoverEnabled(config, route.providerName))) {
|
|
386
|
+
runTurnEvents = [];
|
|
387
|
+
for await (const event of await preflightRunTurnFailover(
|
|
388
|
+
(async function* () { yield* firstAttemptEvents; })(),
|
|
389
|
+
)) runTurnEvents.push(event);
|
|
390
|
+
}
|
|
391
|
+
let events: AdapterEvent[];
|
|
392
|
+
if (emptyCompletionGuardEnabled) {
|
|
393
|
+
events = [];
|
|
394
|
+
for await (const event of guardEmptyCompletionEventStream({
|
|
395
|
+
firstEvents: (async function* () { yield* runTurnEvents; })(),
|
|
396
|
+
continuation: runTurnRetrySource,
|
|
397
|
+
})) events.push(event);
|
|
398
|
+
} else {
|
|
399
|
+
events = runTurnEvents;
|
|
400
|
+
}
|
|
401
|
+
if (options.comboAttempt) {
|
|
402
|
+
const firstMeaningful = events.find(event => event.type !== "heartbeat");
|
|
403
|
+
if (!firstMeaningful || firstMeaningful.type === "error") {
|
|
404
|
+
const message = firstMeaningful?.type === "error"
|
|
405
|
+
? firstMeaningful.message
|
|
406
|
+
: "Adapter ended before producing a response";
|
|
407
|
+
return formatErrorResponse(502, "upstream_error", redactSecretString(message));
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
let providerState: OcxProviderContinuationState | undefined;
|
|
411
|
+
const json = buildResponseJSON(events, parsed._responseModelId ?? parsed.modelId, {
|
|
412
|
+
translatorBudget,
|
|
413
|
+
replayCacheScope: parsed._reasoningReplayScope,
|
|
414
|
+
hideThinkingSummary: parsed.options.hideThinkingSummary,
|
|
415
|
+
toolNsMap,
|
|
416
|
+
declaredToolNames,
|
|
417
|
+
toolParameterSchemas,
|
|
418
|
+
freeformToolNames,
|
|
419
|
+
toolSearchToolNames,
|
|
420
|
+
...(routedCompaction ? { compaction: true } : {}),
|
|
421
|
+
onProviderState: state => { providerState = state; },
|
|
422
|
+
onUsage: usage => {
|
|
423
|
+
logCtx.usageFromBridge = true;
|
|
424
|
+
if (usage) {
|
|
425
|
+
logCtx.usage = usage;
|
|
426
|
+
if (logCtx.activeAttempt) logCtx.activeAttempt.usage = usage;
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
if (!routedCompaction) {
|
|
431
|
+
rememberKiroDeliveredFinalAnswer(transportState.adapter.name, json);
|
|
432
|
+
rememberResponseState(
|
|
433
|
+
parsed._rawBody,
|
|
434
|
+
json,
|
|
435
|
+
continuationStateForResponse(providerState),
|
|
436
|
+
responseStateOptions(adapterNeedsForcedContinuation(transportState.adapter.name)),
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
// #1926 gap 2: the buffered path queued its signature persists inside
|
|
440
|
+
// buildResponseJSON; bound the durability window before the JSON becomes
|
|
441
|
+
// externally visible.
|
|
442
|
+
await awaitThoughtSignatureDurability();
|
|
443
|
+
if (adapterResponseReachedServingTerminal(events, json)) {
|
|
444
|
+
commitReasoningReplayServingRoute();
|
|
445
|
+
}
|
|
446
|
+
notifyResponseComplete(json);
|
|
447
|
+
return new Response(JSON.stringify(json), { headers: { "Content-Type": "application/json" } });
|
|
448
|
+
}
|