@openclaw/ai 2026.7.2-beta.7 → 2026.8.1-beta.2
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/dist/{anthropic-CH4UUnZr.mjs → anthropic-B6dLpq5L.mjs} +55 -203
- package/dist/{src-QkygScBs.mjs → anthropic-JsNA5KCu.mjs} +0 -1
- package/dist/anthropic-compaction-replay-8lJNKXOE.mjs +840 -0
- package/dist/anthropic-payload-policy-CiEuQS72.d.mts +49 -0
- package/dist/{api-registry-DlMgPR39.d.mts → api-registry-k3zTz0cV.d.mts} +1 -1
- package/dist/{azure-openai-responses-CImcwB83.mjs → azure-openai-responses-mxIOtUnn.mjs} +23 -29
- package/dist/diagnostics.d.mts +24 -1
- package/dist/diagnostics.mjs +2 -1
- package/dist/{event-stream-YjaPW20U.d.mts → event-stream-BP6AWT8j.d.mts} +4 -1
- package/dist/{event-stream-D8n2uFee.mjs → event-stream-uSMZJ3FA.mjs} +26 -5
- package/dist/event-stream.d.mts +1 -1
- package/dist/event-stream.mjs +1 -1
- package/dist/{google-CtSg0iTS.mjs → google-C7h2QzDX.mjs} +5 -5
- package/dist/{google-shared-DNBz5rcD.mjs → google-shared-B0Qr9OR7.mjs} +146 -89
- package/dist/google-thinking-level-C-V3tecN.mjs +9 -0
- package/dist/{google-vertex-31f1uS9L.mjs → google-vertex-Bhc3TjDl.mjs} +11 -7
- package/dist/{llm-request-activity-BjtkplhG.mjs → headers-DdOQtGuU.mjs} +9 -1
- package/dist/host-DTqNc7ad.mjs +466 -0
- package/dist/{host-B9GUmcra.d.mts → host-vWgMMhiJ.d.mts} +9 -4
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +7 -5
- package/dist/internal/anthropic.d.mts +29 -5
- package/dist/internal/anthropic.mjs +5 -5
- package/dist/internal/openai-responses-payload-policy.d.mts +3 -0
- package/dist/internal/openai-responses-payload-policy.mjs +3 -0
- package/dist/internal/openai.d.mts +6 -6
- package/dist/internal/openai.mjs +8 -7
- package/dist/internal/runtime.d.mts +17 -5
- package/dist/internal/runtime.mjs +85 -73
- package/dist/internal/shared.d.mts +1 -6
- package/dist/internal/shared.mjs +3 -5
- package/dist/{json-parse-BvXNt1-7.mjs → json-parse-CDnesDM_.mjs} +4 -6
- package/dist/{mistral-CWmpvWYh.mjs → mistral-CEWoQI_g.mjs} +45 -48
- package/dist/number-coercion-H9qHik3g.mjs +71 -0
- package/dist/openai-chatgpt-jwt-KWcgd0d_.mjs +19 -0
- package/dist/{openai-chatgpt-responses-B84Ibtrd.mjs → openai-chatgpt-responses-CrPmqERt.mjs} +284 -240
- package/dist/{openai-completions-DsOxhOD1.mjs → openai-completions-BPnt4Sml.mjs} +65 -99
- package/dist/{openai-completions-compat-DBWjXoMZ.d.mts → openai-completions-compat-Dt3dcawL.d.mts} +2 -2
- package/dist/{openai-responses-BT7A3sLu.mjs → openai-responses-DhIKtOup.mjs} +18 -41
- package/dist/openai-responses-contracts-CyfIkQi5.mjs +253 -0
- package/dist/openai-responses-contracts-XpZJxrRG.d.mts +68 -0
- package/dist/openai-responses-payload-policy-BDxV-W0c.mjs +206 -0
- package/dist/openai-responses-payload-policy-BSs371VM.d.mts +40 -0
- package/dist/openai-responses-prompt-observer-internal-DgNTYnRY.mjs +46 -0
- package/dist/{openai-responses-stream-internal-Cw5txaGW.mjs → openai-responses-shared-DXIt3iY5.mjs} +1406 -940
- package/dist/{openai-reasoning-compat-YgeLncHw.mjs → openai-stop-reason-BkFkqqK0.mjs} +204 -37
- package/dist/openai-tool-projection-CY04OcvQ.mjs +338 -0
- package/dist/provider-error-BUwEnjXq.mjs +429 -0
- package/dist/provider-error-CzNw4BWX.d.mts +12 -0
- package/dist/{provider-options-D8bB3z9b.d.mts → provider-options-B96RdNpH.d.mts} +9 -3
- package/dist/provider-transcript-transform-ePx-Bbfr.mjs +155 -0
- package/dist/provider-types.d.mts +31 -0
- package/dist/provider-types.mjs +8 -0
- package/dist/providers.d.mts +1 -1
- package/dist/providers.mjs +17 -19
- package/dist/{reasoning-tag-text-partitioner-CGDyLWUR.mjs → reasoning-tag-text-partitioner-rnPwX2pg.mjs} +14 -8
- package/dist/record-coerce-DdXsgUd_.mjs +23 -0
- package/dist/sanitize-unicode-BYqrYtC_.mjs +90 -0
- package/dist/session-resources-CkR4WWy1.mjs +21 -0
- package/dist/simple-options-D58D5Kvw.mjs +117 -0
- package/dist/src-D2H6yKkH.mjs +2 -0
- package/dist/{stream-first-event-timeout-BBys9hSb.mjs → stream-first-event-timeout-MK28puvq.mjs} +2 -2
- package/dist/string-coerce-fsri9iCu.mjs +34 -0
- package/dist/{tool-schema-json-projection-BwNu3nDi.mjs → tool-schema-json-projection-q5d7QX5c.mjs} +32 -7
- package/dist/transport-utils-DJqkxbhC.mjs +138 -0
- package/dist/transports.d.mts +166 -241
- package/dist/transports.mjs +1979 -1825
- package/dist/types-BDdaOVi2.mjs +6 -0
- package/dist/{types-bzp5k29J.d.mts → types-BHNrPS1l.d.mts} +23 -1
- package/dist/types.d.mts +4 -4
- package/dist/types.mjs +6 -4
- package/dist/utf16-slice-CvGodqok.mjs +29 -0
- package/dist/{validation-DAa_yFOM.mjs → validation-B61OhAio.mjs} +6 -6
- package/dist/{validation-B-j7cOYp.d.mts → validation-DT9SrFn3.d.mts} +1 -1
- package/dist/validation.d.mts +1 -1
- package/dist/validation.mjs +1 -1
- package/package.json +15 -1
- package/dist/anthropic-usage-DWU-x8MI.mjs +0 -459
- package/dist/error-coercion-DgxlWC0n.mjs +0 -15
- package/dist/headers-B_e4-1J0.mjs +0 -9
- package/dist/host-Dog2WQiR.mjs +0 -369
- package/dist/number-coercion-DvG7SNMg.mjs +0 -129
- package/dist/openai-chatgpt-jwt-DhAAzLkj.mjs +0 -39
- package/dist/openai-responses-shared-pXl6Wd8S.mjs +0 -392
- package/dist/openai-tool-projection-OhX64DoP.mjs +0 -215
- package/dist/provider-error-CAEvRjry.mjs +0 -47
- package/dist/sanitize-unicode-DT5o51ur.mjs +0 -26
- package/dist/simple-options-9lhRrN73.mjs +0 -50
- package/dist/tool-result-text-CTpIRbYd.mjs +0 -225
- package/dist/transform-messages-C8mBqZxF.mjs +0 -2
- package/dist/transport-stream-shared-D81p90xq.mjs +0 -297
package/dist/{openai-chatgpt-responses-B84Ibtrd.mjs → openai-chatgpt-responses-CrPmqERt.mjs}
RENAMED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { n as getEnvApiKey } from "./env-api-keys-DrgeBuva.mjs";
|
|
2
2
|
import { i as formatThrownValue, n as createAssistantMessageDiagnostic, t as appendAssistantMessageDiagnostic } from "./diagnostics-COpOtRwq.mjs";
|
|
3
|
-
import { t as AssistantMessageEventStream } from "./event-stream-
|
|
4
|
-
import { n as getAiTransportHost, r as resolveAiTransportHeaderSentinels } from "./host-
|
|
3
|
+
import { t as AssistantMessageEventStream } from "./event-stream-uSMZJ3FA.mjs";
|
|
4
|
+
import { n as getAiTransportHost, r as resolveAiTransportHeaderSentinels } from "./host-DTqNc7ad.mjs";
|
|
5
|
+
import { n as projectProviderError } from "./provider-error-BUwEnjXq.mjs";
|
|
6
|
+
import { n as toErrorObject, t as createResponsesPromptEgressObserver } from "./openai-responses-prompt-observer-internal-DgNTYnRY.mjs";
|
|
7
|
+
import { c as resolveTimerTimeoutMs, i as clampTimerTimeoutMs } from "./number-coercion-H9qHik3g.mjs";
|
|
8
|
+
import { S as supportsOpenAITemperature, m as responsesPromptObserver } from "./openai-responses-contracts-CyfIkQi5.mjs";
|
|
5
9
|
import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
|
|
6
|
-
import {
|
|
7
|
-
import { t as
|
|
8
|
-
import { n as
|
|
10
|
+
import { l as stripSystemPromptCacheBoundary, n as buildBaseOptions } from "./simple-options-D58D5Kvw.mjs";
|
|
11
|
+
import { t as headersToRecord } from "./headers-DdOQtGuU.mjs";
|
|
12
|
+
import { F as commitResponsesEncryptedContentAttempt, G as suppressOpenAIResponsesCompaction, L as isInvalidEncryptedContentError, U as buildOpenAIResponsesReasoningReplayMetadata, a as resolveResponsesReasoningEffort, c as processResponsesStream, i as createResponsesAssistantOutput, n as applyResponsesServiceTierPricing, p as ResponsesStreamFailure, r as convertResponsesMessages, s as convertResponsesToolPayload, z as resolveNextResponsesEncryptedContentAttempt } from "./openai-responses-shared-DXIt3iY5.mjs";
|
|
13
|
+
import { f as transportAbortError, p as withProviderResponseHook } from "./provider-transcript-transform-ePx-Bbfr.mjs";
|
|
9
14
|
import { parseRetryAfterHttpDateMs } from "./internal/retry-after.mjs";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
15
|
-
import {
|
|
16
|
-
import { i as registerSessionResourceCleanup, n as resolveOpenAICodexAccountId } from "./openai-chatgpt-jwt-DhAAzLkj.mjs";
|
|
15
|
+
import { t as MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE } from "./transport-utils-DJqkxbhC.mjs";
|
|
16
|
+
import { o as createOpenAIResponseHook } from "./openai-tool-projection-CY04OcvQ.mjs";
|
|
17
|
+
import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-MK28puvq.mjs";
|
|
18
|
+
import { n as registerSessionResourceCleanup } from "./session-resources-CkR4WWy1.mjs";
|
|
19
|
+
import "./diagnostics.mjs";
|
|
20
|
+
import { n as resolveOpenAICodexAccountId } from "./openai-chatgpt-jwt-KWcgd0d_.mjs";
|
|
17
21
|
import { t as createSseByteGuard } from "./streaming-byte-guard-BrbkbwUu.mjs";
|
|
18
22
|
import { t as inspectTlsCertificateError } from "./tls-certificate-errors-DXSpluKI.mjs";
|
|
19
23
|
//#region packages/ai/src/internal/retry-sleep.ts
|
|
@@ -36,6 +40,63 @@ function sleepWithAbort(ms, signal) {
|
|
|
36
40
|
});
|
|
37
41
|
}
|
|
38
42
|
//#endregion
|
|
43
|
+
//#region packages/ai/src/providers/openai-chatgpt-responses-protocol.ts
|
|
44
|
+
const OPENAI_CHATGPT_RESPONSES_SUCCESS_BODY_MAX_BYTES = 16 * 1024 * 1024;
|
|
45
|
+
var CodexProtocolError = class extends Error {
|
|
46
|
+
constructor(message, options) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = "CodexProtocolError";
|
|
49
|
+
this.payload = options?.payload;
|
|
50
|
+
this.cause = options?.cause;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
async function* parseOpenAIChatGptResponsesSse(response) {
|
|
54
|
+
if (!response.body) return;
|
|
55
|
+
const reader = response.body.getReader();
|
|
56
|
+
const guard = createSseByteGuard(reader, {
|
|
57
|
+
maxBytes: OPENAI_CHATGPT_RESPONSES_SUCCESS_BODY_MAX_BYTES,
|
|
58
|
+
onOverflow: ({ size, maxBytes }) => /* @__PURE__ */ new Error(`OpenAI ChatGPT Responses success body exceeded ${maxBytes} bytes (received ${size})`)
|
|
59
|
+
});
|
|
60
|
+
const decoder = new TextDecoder();
|
|
61
|
+
let buffer = "";
|
|
62
|
+
try {
|
|
63
|
+
while (true) {
|
|
64
|
+
const { done, value } = await guard.read();
|
|
65
|
+
if (value) buffer += decoder.decode(value, { stream: true });
|
|
66
|
+
if (done) buffer += decoder.decode();
|
|
67
|
+
while (true) {
|
|
68
|
+
const searchable = !done && buffer.endsWith("\r") && !buffer.endsWith("\r\r") && !buffer.endsWith("\n\r") ? buffer.slice(0, -1) : buffer;
|
|
69
|
+
const boundary = /(?:\r\n|\r(?!\n)|\n)(?:\r\n|\r(?!\n)|\n)/.exec(searchable);
|
|
70
|
+
if (!boundary) break;
|
|
71
|
+
const chunk = buffer.slice(0, boundary.index);
|
|
72
|
+
buffer = buffer.slice(boundary.index + boundary[0].length);
|
|
73
|
+
const dataLines = chunk.split(/\r\n|\r|\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim());
|
|
74
|
+
if (dataLines.length > 0) {
|
|
75
|
+
const data = dataLines.join("\n").trim();
|
|
76
|
+
if (data && data !== "[DONE]") {
|
|
77
|
+
let event;
|
|
78
|
+
try {
|
|
79
|
+
event = JSON.parse(data);
|
|
80
|
+
} catch (cause) {
|
|
81
|
+
if (!(cause instanceof SyntaxError)) throw cause;
|
|
82
|
+
throw new CodexProtocolError(MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE, { cause });
|
|
83
|
+
}
|
|
84
|
+
yield event;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (done) break;
|
|
89
|
+
}
|
|
90
|
+
} finally {
|
|
91
|
+
try {
|
|
92
|
+
await guard.cancel();
|
|
93
|
+
} catch {}
|
|
94
|
+
try {
|
|
95
|
+
reader.releaseLock();
|
|
96
|
+
} catch {}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
39
100
|
//#region packages/ai/src/providers/openai-chatgpt-responses.ts
|
|
40
101
|
const dynamicImport = (specifier) => import(specifier);
|
|
41
102
|
function loadNodeOs() {
|
|
@@ -51,7 +112,6 @@ const CODEX_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set(["openai", "opencode"]
|
|
|
51
112
|
const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
|
|
52
113
|
const WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE = "websocket_connection_limit_reached";
|
|
53
114
|
const OPENAI_CHATGPT_RESPONSES_ERROR_BODY_MAX_BYTES = 16 * 1024;
|
|
54
|
-
const OPENAI_CHATGPT_RESPONSES_SUCCESS_BODY_MAX_BYTES = 16 * 1024 * 1024;
|
|
55
115
|
const CODEX_RESPONSE_STATUSES = /* @__PURE__ */ new Set([
|
|
56
116
|
"completed",
|
|
57
117
|
"incomplete",
|
|
@@ -116,29 +176,7 @@ const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
116
176
|
let requestTimeoutSignal;
|
|
117
177
|
let activeSignal;
|
|
118
178
|
let firstEventAbort;
|
|
119
|
-
const output =
|
|
120
|
-
role: "assistant",
|
|
121
|
-
content: [],
|
|
122
|
-
api: "openai-chatgpt-responses",
|
|
123
|
-
provider: model.provider,
|
|
124
|
-
model: model.id,
|
|
125
|
-
usage: {
|
|
126
|
-
input: 0,
|
|
127
|
-
output: 0,
|
|
128
|
-
cacheRead: 0,
|
|
129
|
-
cacheWrite: 0,
|
|
130
|
-
totalTokens: 0,
|
|
131
|
-
cost: {
|
|
132
|
-
input: 0,
|
|
133
|
-
output: 0,
|
|
134
|
-
cacheRead: 0,
|
|
135
|
-
cacheWrite: 0,
|
|
136
|
-
total: 0
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
stopReason: "stop",
|
|
140
|
-
timestamp: Date.now()
|
|
141
|
-
};
|
|
179
|
+
const output = createResponsesAssistantOutput(model);
|
|
142
180
|
try {
|
|
143
181
|
const unresolvedApiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
144
182
|
if (!unresolvedApiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
@@ -146,9 +184,18 @@ const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
146
184
|
const modelHeaders = resolveAiTransportHeaderSentinels(model.headers);
|
|
147
185
|
const optionHeaders = resolveAiTransportHeaderSentinels(options?.headers);
|
|
148
186
|
const accountId = extractOpenAICodexAccountId(apiKey);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
187
|
+
const buildBody = async (replayMode) => {
|
|
188
|
+
let body = buildRequestBody(model, context, options, replayMode);
|
|
189
|
+
const nextBody = await options?.onPayload?.(body, model);
|
|
190
|
+
if (nextBody !== void 0) body = nextBody;
|
|
191
|
+
return body;
|
|
192
|
+
};
|
|
193
|
+
let semanticAttempt = {
|
|
194
|
+
kind: "initial",
|
|
195
|
+
request: await buildBody("checkpoint")
|
|
196
|
+
};
|
|
197
|
+
const commitSemanticAttempt = (attempt) => commitResponsesEncryptedContentAttempt(attempt, (checkpoint) => suppressOpenAIResponsesCompaction(output, model, options, checkpoint));
|
|
198
|
+
const observePromptEgress = createResponsesPromptEgressObserver(options, context.systemPrompt);
|
|
152
199
|
const sessionId = clampOpenAIPromptCacheKey(options?.sessionId);
|
|
153
200
|
requestTimeoutMs = resolveRequestTimeoutMs(options);
|
|
154
201
|
requestTimeoutSignal = buildRequestSignal(options?.signal, requestTimeoutMs);
|
|
@@ -163,14 +210,21 @@ const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
163
210
|
if (transport !== "sse" && !websocketDisabledForSession) {
|
|
164
211
|
const websocketHeaders = buildWebSocketHeaders(modelHeaders, optionHeaders, accountId, apiKey, sessionId || createCodexRequestId());
|
|
165
212
|
let websocketStarted = false;
|
|
213
|
+
let websocketRequestSent = false;
|
|
166
214
|
let retriedWebSocketConnectionLimit = false;
|
|
167
215
|
while (true) {
|
|
216
|
+
const activeAttempt = semanticAttempt;
|
|
168
217
|
websocketStarted = false;
|
|
218
|
+
websocketRequestSent = false;
|
|
169
219
|
try {
|
|
170
|
-
await processWebSocketStream(resolveCodexWebSocketUrl(model.baseUrl),
|
|
220
|
+
await processWebSocketStream(resolveCodexWebSocketUrl(model.baseUrl), activeAttempt.request, websocketHeaders, output, stream, model, () => {
|
|
221
|
+
commitSemanticAttempt(activeAttempt);
|
|
171
222
|
websocketStarted = true;
|
|
172
|
-
}, requestOptions, firstEventAbort.abort)
|
|
223
|
+
}, requestOptions, firstEventAbort.abort, observePromptEgress, activeAttempt.kind, () => {
|
|
224
|
+
websocketRequestSent = true;
|
|
225
|
+
});
|
|
173
226
|
if (activeSignal?.aborted) throw transportAbortError(activeSignal);
|
|
227
|
+
if (output.stopReason === "aborted" || output.stopReason === "error") throw new CodexApiError(output.errorMessage ?? "An unknown error occurred");
|
|
174
228
|
stream.push({
|
|
175
229
|
type: "done",
|
|
176
230
|
reason: output.stopReason,
|
|
@@ -180,6 +234,12 @@ const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
180
234
|
return;
|
|
181
235
|
} catch (error) {
|
|
182
236
|
const aborted = activeSignal?.aborted;
|
|
237
|
+
const nextSemanticAttempt = !aborted && websocketRequestSent && !websocketStarted && error instanceof CodexApiError && isInvalidEncryptedContentError(error) ? await resolveNextResponsesEncryptedContentAttempt(activeAttempt, error, { buildFullHistoryRequest: () => buildBody("full-history") }) : void 0;
|
|
238
|
+
if (nextSemanticAttempt) {
|
|
239
|
+
semanticAttempt = nextSemanticAttempt;
|
|
240
|
+
retriedWebSocketConnectionLimit = false;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
183
243
|
const connectionLimitBeforeStart = !websocketStarted && isWebSocketConnectionLimitReachedError(error);
|
|
184
244
|
if (!aborted && connectionLimitBeforeStart && !retriedWebSocketConnectionLimit) {
|
|
185
245
|
retriedWebSocketConnectionLimit = true;
|
|
@@ -191,7 +251,7 @@ const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
191
251
|
fallbackTransport: transport === "auto" && !websocketStarted ? "sse" : void 0,
|
|
192
252
|
eventsEmitted: websocketStarted,
|
|
193
253
|
phase: websocketStarted ? "after_message_stream_start" : "before_message_stream_start",
|
|
194
|
-
requestBytes: new TextEncoder().encode(JSON.stringify(
|
|
254
|
+
requestBytes: new TextEncoder().encode(JSON.stringify(activeAttempt.request)).byteLength
|
|
195
255
|
}));
|
|
196
256
|
if (transport === "auto" && options?.sessionId) websocketSseFallbackSessions.add(options.sessionId);
|
|
197
257
|
if (websocketStarted || transport !== "auto") throw error;
|
|
@@ -199,61 +259,104 @@ const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
199
259
|
}
|
|
200
260
|
}
|
|
201
261
|
}
|
|
202
|
-
const sseHeaders = buildSSEHeaders(modelHeaders, optionHeaders, accountId, apiKey, sessionId);
|
|
203
|
-
const bodyJson = JSON.stringify(body);
|
|
204
|
-
const compressedBody = model.provider === "openai" && !sseHeaders.has("content-encoding") ? compressRequestBodyZstd(bodyJson) : null;
|
|
205
|
-
if (compressedBody) sseHeaders.set("content-encoding", "zstd");
|
|
206
|
-
const sseBody = compressedBody ?? bodyJson;
|
|
207
262
|
let response;
|
|
208
|
-
let lastError;
|
|
209
263
|
const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
264
|
+
while (true) {
|
|
265
|
+
const activeAttempt = semanticAttempt;
|
|
266
|
+
response = void 0;
|
|
267
|
+
const sseHeaders = buildSSEHeaders(modelHeaders, optionHeaders, accountId, apiKey, sessionId);
|
|
268
|
+
const bodyJson = JSON.stringify(activeAttempt.request);
|
|
269
|
+
const compressedBody = model.provider === "openai" && !sseHeaders.has("content-encoding") ? compressRequestBodyZstd(bodyJson) : null;
|
|
270
|
+
if (compressedBody) sseHeaders.set("content-encoding", "zstd");
|
|
271
|
+
const sseBody = compressedBody ?? bodyJson;
|
|
272
|
+
let terminalResponseError;
|
|
273
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
274
|
+
if (activeSignal?.aborted) throw transportAbortError(activeSignal);
|
|
275
|
+
observePromptEgress?.(activeAttempt.request, {
|
|
276
|
+
egress: "native-codex-sse",
|
|
277
|
+
payloadVariant: activeAttempt.kind
|
|
220
278
|
});
|
|
279
|
+
let attemptResponse;
|
|
280
|
+
try {
|
|
281
|
+
attemptResponse = await fetch(resolveCodexUrl(model.baseUrl), {
|
|
282
|
+
method: "POST",
|
|
283
|
+
headers: sseHeaders,
|
|
284
|
+
body: sseBody,
|
|
285
|
+
signal: activeSignal
|
|
286
|
+
});
|
|
287
|
+
} catch (error) {
|
|
288
|
+
if (error instanceof Error) {
|
|
289
|
+
if (isRequestTimeoutError(error, options?.signal, requestTimeoutSignal, requestTimeoutMs) && requestTimeoutMs !== void 0) throw formatRequestTimeoutError(requestTimeoutMs, error);
|
|
290
|
+
if (error.name === "AbortError" || error.message === "Request was aborted") throw new Error("Request was aborted", { cause: error });
|
|
291
|
+
if (error.name === "TimeoutError" && requestTimeoutMs !== void 0) throw new Error(`Request timed out after ${requestTimeoutMs}ms`, { cause: error });
|
|
292
|
+
}
|
|
293
|
+
const tlsCertificateError = inspectTlsCertificateError(error);
|
|
294
|
+
const lastError = toErrorObject(error, String(error));
|
|
295
|
+
if (attempt < maxRetries && !lastError.message.includes("usage limit") && !tlsCertificateError) {
|
|
296
|
+
await sleepWithAbort(BASE_DELAY_MS * 2 ** attempt, activeSignal);
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
throw lastError;
|
|
300
|
+
}
|
|
221
301
|
response = attemptResponse;
|
|
222
|
-
await options?.onResponse?.({
|
|
223
|
-
status: attemptResponse.status,
|
|
224
|
-
headers: headersToRecord(attemptResponse.headers)
|
|
225
|
-
}, model);
|
|
226
302
|
if (attemptResponse.ok) break;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
303
|
+
const hookStream = withFirstStreamEventTimeout(withProviderResponseHook({
|
|
304
|
+
signal: firstEventAbort.signal,
|
|
305
|
+
abort: firstEventAbort.abort,
|
|
306
|
+
hook: createOpenAIResponseHook(options?.onResponse, attemptResponse, model)
|
|
307
|
+
}), {
|
|
308
|
+
provider: model.provider,
|
|
309
|
+
api: model.api,
|
|
310
|
+
model: model.id,
|
|
311
|
+
timeoutMs: getFirstStreamEventTimeoutMs(options) ?? 0,
|
|
312
|
+
stage: "responses",
|
|
313
|
+
abort: firstEventAbort.abort,
|
|
314
|
+
onTimeout: getFirstStreamEventTimeoutHandler(options)
|
|
315
|
+
});
|
|
316
|
+
const [errorText] = await Promise.all([readChatGptResponsesErrorTextLimited(attemptResponse, activeSignal), hookStream[Symbol.asyncIterator]().next()]);
|
|
317
|
+
if (attempt < maxRetries && isRetryableError(attemptResponse.status, errorText)) {
|
|
318
|
+
await sleepWithAbort(resolveHttpRetryDelayMs(attemptResponse, attempt), activeSignal);
|
|
238
319
|
continue;
|
|
239
320
|
}
|
|
240
|
-
|
|
321
|
+
const info = parseErrorResponseText(errorText, attemptResponse.status, attemptResponse.statusText);
|
|
322
|
+
terminalResponseError = new CodexApiError(info.friendlyMessage || info.message, { code: info.code });
|
|
323
|
+
break;
|
|
241
324
|
}
|
|
242
|
-
if (
|
|
243
|
-
|
|
244
|
-
|
|
325
|
+
if (response?.ok) {
|
|
326
|
+
if (!response.body) throw new Error("No response body");
|
|
327
|
+
if (activeSignal?.aborted) throw transportAbortError(activeSignal);
|
|
328
|
+
commitSemanticAttempt(activeAttempt);
|
|
329
|
+
break;
|
|
245
330
|
}
|
|
246
|
-
|
|
247
|
-
|
|
331
|
+
if (activeSignal?.aborted) throw transportAbortError(activeSignal);
|
|
332
|
+
const nextSemanticAttempt = terminalResponseError ? await resolveNextResponsesEncryptedContentAttempt(activeAttempt, terminalResponseError, { buildFullHistoryRequest: () => buildBody("full-history") }) : void 0;
|
|
333
|
+
if (!nextSemanticAttempt) throw terminalResponseError ?? /* @__PURE__ */ new Error("Failed after retries");
|
|
334
|
+
semanticAttempt = nextSemanticAttempt;
|
|
248
335
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
336
|
+
await processResponsesStream(withProviderResponseHook({
|
|
337
|
+
stream: mapCodexEvents(parseOpenAIChatGptResponsesSse(response)),
|
|
338
|
+
signal: firstEventAbort.signal,
|
|
339
|
+
abort: firstEventAbort.abort,
|
|
340
|
+
hook: createOpenAIResponseHook(options?.onResponse, response, model),
|
|
341
|
+
onReady: () => stream.push({
|
|
342
|
+
type: "start",
|
|
343
|
+
partial: output
|
|
344
|
+
})
|
|
345
|
+
}), output, stream, model, {
|
|
346
|
+
serviceTier: options?.serviceTier,
|
|
347
|
+
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
|
|
348
|
+
abortFirstEventStream: firstEventAbort.abort,
|
|
349
|
+
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
350
|
+
signal: options?.signal,
|
|
351
|
+
reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
|
|
352
|
+
sessionId: options?.sessionId,
|
|
353
|
+
authProfileId: options?.authProfileId
|
|
354
|
+
}),
|
|
355
|
+
resolveServiceTier: resolveCodexServiceTier,
|
|
356
|
+
applyServiceTierPricing: (usage, serviceTier) => applyResponsesServiceTierPricing(usage, serviceTier, model)
|
|
254
357
|
});
|
|
255
|
-
await processStream(response, output, stream, model, options, firstEventAbort.abort);
|
|
256
358
|
if (activeSignal?.aborted) throw transportAbortError(activeSignal);
|
|
359
|
+
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
|
|
257
360
|
stream.push({
|
|
258
361
|
type: "done",
|
|
259
362
|
reason: output.stopReason,
|
|
@@ -263,11 +366,11 @@ const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
263
366
|
} catch (error) {
|
|
264
367
|
const normalizedError = isRequestTimeoutError(error, options?.signal, requestTimeoutSignal, requestTimeoutMs) && requestTimeoutMs !== void 0 ? formatRequestTimeoutError(requestTimeoutMs, error) : error;
|
|
265
368
|
for (const block of output.content) delete block.partialJson;
|
|
266
|
-
|
|
267
|
-
output
|
|
369
|
+
const terminal = projectProviderError(normalizedError, options?.signal);
|
|
370
|
+
Object.assign(output, terminal);
|
|
268
371
|
stream.push({
|
|
269
372
|
type: "error",
|
|
270
|
-
reason:
|
|
373
|
+
reason: terminal.stopReason,
|
|
271
374
|
error: output
|
|
272
375
|
});
|
|
273
376
|
stream.end();
|
|
@@ -280,16 +383,21 @@ const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
280
383
|
const streamSimpleOpenAICodexResponses = (model, context, options) => {
|
|
281
384
|
const apiKey = options?.apiKey || getEnvApiKey(model.provider);
|
|
282
385
|
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
386
|
+
const resolvedOptions = {
|
|
387
|
+
...buildBaseOptions(model, options, apiKey),
|
|
388
|
+
authProfileId: options?.authProfileId,
|
|
286
389
|
reasoningEffort: resolveResponsesReasoningEffort(model, options?.reasoning)
|
|
287
|
-
}
|
|
390
|
+
};
|
|
391
|
+
responsesPromptObserver.copy(options, resolvedOptions);
|
|
392
|
+
return streamOpenAICodexResponses(model, context, resolvedOptions);
|
|
288
393
|
};
|
|
289
|
-
function buildRequestBody(model, context, options) {
|
|
394
|
+
function buildRequestBody(model, context, options, replayMode = "checkpoint") {
|
|
290
395
|
const messages = convertResponsesMessages(model, context, CODEX_TOOL_CALL_PROVIDERS, {
|
|
291
396
|
includeSystemPrompt: false,
|
|
292
|
-
replayResponsesItemIds: false
|
|
397
|
+
replayResponsesItemIds: false,
|
|
398
|
+
sessionId: options?.sessionId,
|
|
399
|
+
authProfileId: options?.authProfileId,
|
|
400
|
+
replayMode
|
|
293
401
|
});
|
|
294
402
|
const body = {
|
|
295
403
|
model: model.id,
|
|
@@ -299,21 +407,16 @@ function buildRequestBody(model, context, options) {
|
|
|
299
407
|
input: messages,
|
|
300
408
|
text: { verbosity: options?.textVerbosity || "low" },
|
|
301
409
|
include: ["reasoning.encrypted_content"],
|
|
302
|
-
prompt_cache_key: options?.cacheRetention === "none" ? void 0 : clampOpenAIPromptCacheKey(options?.promptCacheKey ?? options?.sessionId)
|
|
303
|
-
tool_choice: "auto",
|
|
304
|
-
parallel_tool_calls: true
|
|
410
|
+
prompt_cache_key: options?.cacheRetention === "none" ? void 0 : clampOpenAIPromptCacheKey(options?.promptCacheKey ?? options?.sessionId)
|
|
305
411
|
};
|
|
306
412
|
if (options?.temperature !== void 0 && supportsOpenAITemperature(model)) body.temperature = options.temperature;
|
|
307
413
|
if (options?.serviceTier !== void 0) body.service_tier = options.serviceTier;
|
|
308
414
|
if (context.tools) {
|
|
309
415
|
const converted = convertResponsesToolPayload(context.tools, { strict: null });
|
|
310
|
-
if (converted.
|
|
416
|
+
if (converted.tools.length > 0) {
|
|
311
417
|
body.tools = converted.tools;
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
delete body.tool_choice;
|
|
315
|
-
delete body.parallel_tool_calls;
|
|
316
|
-
}
|
|
418
|
+
body.tool_choice = "auto";
|
|
419
|
+
body.parallel_tool_calls = true;
|
|
317
420
|
}
|
|
318
421
|
}
|
|
319
422
|
if (options?.reasoningEffort !== void 0) {
|
|
@@ -325,22 +428,6 @@ function buildRequestBody(model, context, options) {
|
|
|
325
428
|
}
|
|
326
429
|
return body;
|
|
327
430
|
}
|
|
328
|
-
function getServiceTierCostMultiplier(model, serviceTier) {
|
|
329
|
-
switch (serviceTier) {
|
|
330
|
-
case "flex": return .5;
|
|
331
|
-
case "priority": return model.id === "gpt-5.5" ? 2.5 : 2;
|
|
332
|
-
default: return 1;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
function applyServiceTierPricing(usage, serviceTier, model) {
|
|
336
|
-
const multiplier = getServiceTierCostMultiplier(model, serviceTier);
|
|
337
|
-
if (multiplier === 1) return;
|
|
338
|
-
usage.cost.input *= multiplier;
|
|
339
|
-
usage.cost.output *= multiplier;
|
|
340
|
-
usage.cost.cacheRead *= multiplier;
|
|
341
|
-
usage.cost.cacheWrite *= multiplier;
|
|
342
|
-
usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
|
|
343
|
-
}
|
|
344
431
|
function resolveCodexServiceTier(responseServiceTier, requestServiceTier) {
|
|
345
432
|
if (responseServiceTier === "default" && (requestServiceTier === "flex" || requestServiceTier === "priority")) return requestServiceTier;
|
|
346
433
|
return responseServiceTier ?? requestServiceTier;
|
|
@@ -357,17 +444,6 @@ function resolveCodexWebSocketUrl(baseUrl) {
|
|
|
357
444
|
if (url.protocol === "http:") url.protocol = "ws:";
|
|
358
445
|
return url.toString();
|
|
359
446
|
}
|
|
360
|
-
async function processStream(response, output, stream, model, options, abortFirstEventStream) {
|
|
361
|
-
await processResponsesStream(mapCodexEvents(parseSSE(response)), output, stream, model, {
|
|
362
|
-
serviceTier: options?.serviceTier,
|
|
363
|
-
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
|
|
364
|
-
abortFirstEventStream,
|
|
365
|
-
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
366
|
-
signal: options?.signal,
|
|
367
|
-
resolveServiceTier: resolveCodexServiceTier,
|
|
368
|
-
applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model)
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
447
|
var CodexApiError = class extends Error {
|
|
372
448
|
constructor(message, options) {
|
|
373
449
|
super(message);
|
|
@@ -377,16 +453,8 @@ var CodexApiError = class extends Error {
|
|
|
377
453
|
this.cause = options?.cause;
|
|
378
454
|
}
|
|
379
455
|
};
|
|
380
|
-
var CodexProtocolError = class extends Error {
|
|
381
|
-
constructor(message, options) {
|
|
382
|
-
super(message);
|
|
383
|
-
this.name = "CodexProtocolError";
|
|
384
|
-
this.payload = options?.payload;
|
|
385
|
-
this.cause = options?.cause;
|
|
386
|
-
}
|
|
387
|
-
};
|
|
388
456
|
function isCodexNonTransportError(error) {
|
|
389
|
-
return error instanceof CodexApiError || error instanceof CodexProtocolError;
|
|
457
|
+
return error instanceof CodexApiError || error instanceof CodexProtocolError || error instanceof ResponsesStreamFailure;
|
|
390
458
|
}
|
|
391
459
|
function isWebSocketConnectionLimitReachedError(error) {
|
|
392
460
|
return error instanceof CodexApiError && error.code === WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE;
|
|
@@ -409,15 +477,6 @@ async function* mapCodexEvents(events) {
|
|
|
409
477
|
payload: event
|
|
410
478
|
});
|
|
411
479
|
}
|
|
412
|
-
if (type === "response.failed") {
|
|
413
|
-
const response = event.response;
|
|
414
|
-
const code = response?.error?.code;
|
|
415
|
-
const message = response?.error?.message;
|
|
416
|
-
throw new CodexApiError(message || "Codex response failed", {
|
|
417
|
-
code,
|
|
418
|
-
payload: event
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
480
|
if (type === "response.done" || type === "response.completed" || type === "response.incomplete") {
|
|
422
481
|
const response = event.response;
|
|
423
482
|
const normalizedResponse = response ? {
|
|
@@ -426,7 +485,7 @@ async function* mapCodexEvents(events) {
|
|
|
426
485
|
} : response;
|
|
427
486
|
yield {
|
|
428
487
|
...event,
|
|
429
|
-
type: "response.completed",
|
|
488
|
+
type: type === "response.done" ? "response.completed" : type,
|
|
430
489
|
response: normalizedResponse
|
|
431
490
|
};
|
|
432
491
|
return;
|
|
@@ -438,49 +497,6 @@ function normalizeCodexStatus(status) {
|
|
|
438
497
|
if (typeof status !== "string") return;
|
|
439
498
|
return CODEX_RESPONSE_STATUSES.has(status) ? status : void 0;
|
|
440
499
|
}
|
|
441
|
-
async function* parseSSE(response) {
|
|
442
|
-
if (!response.body) return;
|
|
443
|
-
const reader = response.body.getReader();
|
|
444
|
-
const guard = createSseByteGuard(reader, {
|
|
445
|
-
maxBytes: OPENAI_CHATGPT_RESPONSES_SUCCESS_BODY_MAX_BYTES,
|
|
446
|
-
onOverflow: ({ size, maxBytes }) => /* @__PURE__ */ new Error(`OpenAI ChatGPT Responses success body exceeded ${maxBytes} bytes (received ${size})`)
|
|
447
|
-
});
|
|
448
|
-
const decoder = new TextDecoder();
|
|
449
|
-
let buffer = "";
|
|
450
|
-
try {
|
|
451
|
-
while (true) {
|
|
452
|
-
const { done, value } = await guard.read();
|
|
453
|
-
if (done) break;
|
|
454
|
-
buffer += decoder.decode(value, { stream: true });
|
|
455
|
-
let idx = buffer.indexOf("\n\n");
|
|
456
|
-
while (idx !== -1) {
|
|
457
|
-
const chunk = buffer.slice(0, idx);
|
|
458
|
-
buffer = buffer.slice(idx + 2);
|
|
459
|
-
const dataLines = chunk.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim());
|
|
460
|
-
if (dataLines.length > 0) {
|
|
461
|
-
const data = dataLines.join("\n").trim();
|
|
462
|
-
if (data && data !== "[DONE]") try {
|
|
463
|
-
yield JSON.parse(data);
|
|
464
|
-
} catch (cause) {
|
|
465
|
-
throw new CodexProtocolError(`Invalid Codex SSE JSON: ${formatThrownValue(cause)}`, {
|
|
466
|
-
cause,
|
|
467
|
-
payload: data
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
idx = buffer.indexOf("\n\n");
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
} finally {
|
|
475
|
-
try {
|
|
476
|
-
await guard.cancel();
|
|
477
|
-
} catch {}
|
|
478
|
-
try {
|
|
479
|
-
reader.releaseLock();
|
|
480
|
-
} catch {}
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
const parseSSEForTest = parseSSE;
|
|
484
500
|
const OPENAI_BETA_RESPONSES_WEBSOCKETS = "responses_websockets=2026-02-06";
|
|
485
501
|
const SESSION_WEBSOCKET_CACHE_TTL_MS = 300 * 1e3;
|
|
486
502
|
const SESSION_WEBSOCKET_MAX_AGE_MS = 3300 * 1e3;
|
|
@@ -497,6 +513,7 @@ function closeOpenAICodexWebSocketSessions(sessionId) {
|
|
|
497
513
|
closeWebSocketSilently(entry.socket, 1e3, "debug_close");
|
|
498
514
|
};
|
|
499
515
|
if (sessionId) {
|
|
516
|
+
websocketSseFallbackSessions.delete(sessionId);
|
|
500
517
|
const entry = websocketSessionCache.get(sessionId);
|
|
501
518
|
if (entry) closeEntry(entry);
|
|
502
519
|
websocketSessionCache.delete(sessionId);
|
|
@@ -504,6 +521,7 @@ function closeOpenAICodexWebSocketSessions(sessionId) {
|
|
|
504
521
|
}
|
|
505
522
|
for (const entry of websocketSessionCache.values()) closeEntry(entry);
|
|
506
523
|
websocketSessionCache.clear();
|
|
524
|
+
websocketSseFallbackSessions.clear();
|
|
507
525
|
}
|
|
508
526
|
registerSessionResourceCleanup(closeOpenAICodexWebSocketSessions);
|
|
509
527
|
function isWebSocketSseFallbackActive(sessionId) {
|
|
@@ -559,6 +577,13 @@ function closeWebSocketSilently(socket, code = 1e3, reason = "done") {
|
|
|
559
577
|
function deleteOwnedWebSocketSession(sessionId, entry) {
|
|
560
578
|
if (websocketSessionCache.get(sessionId) === entry) websocketSessionCache.delete(sessionId);
|
|
561
579
|
}
|
|
580
|
+
function setOwnedWebSocketSession(sessionId, entry, expected) {
|
|
581
|
+
if (websocketSessionCache.get(sessionId) === expected) {
|
|
582
|
+
websocketSessionCache.set(sessionId, entry);
|
|
583
|
+
return true;
|
|
584
|
+
}
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
562
587
|
function scheduleSessionWebSocketExpiry(sessionId, entry) {
|
|
563
588
|
if (entry.idleTimer) clearTimeout(entry.idleTimer);
|
|
564
589
|
entry.idleTimer = setTimeout(() => {
|
|
@@ -639,6 +664,7 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
|
|
|
639
664
|
};
|
|
640
665
|
}
|
|
641
666
|
const cached = websocketSessionCache.get(sessionId);
|
|
667
|
+
let expectedCacheValue = cached;
|
|
642
668
|
if (cached) {
|
|
643
669
|
if (cached.idleTimer) {
|
|
644
670
|
clearTimeout(cached.idleTimer);
|
|
@@ -646,7 +672,8 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
|
|
|
646
672
|
}
|
|
647
673
|
if (!cached.busy && isWebSocketSessionExpired(cached)) {
|
|
648
674
|
closeWebSocketSilently(cached.socket, 1e3, "connection_age_limit");
|
|
649
|
-
|
|
675
|
+
deleteOwnedWebSocketSession(sessionId, cached);
|
|
676
|
+
expectedCacheValue = void 0;
|
|
650
677
|
} else if (!cached.busy && isWebSocketReusable(cached.socket)) {
|
|
651
678
|
cached.busy = true;
|
|
652
679
|
return {
|
|
@@ -674,7 +701,8 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
|
|
|
674
701
|
}
|
|
675
702
|
if (!isWebSocketReusable(cached.socket)) {
|
|
676
703
|
closeWebSocketSilently(cached.socket);
|
|
677
|
-
|
|
704
|
+
deleteOwnedWebSocketSession(sessionId, cached);
|
|
705
|
+
expectedCacheValue = void 0;
|
|
678
706
|
}
|
|
679
707
|
}
|
|
680
708
|
const socket = await connectWebSocket(url, headers, signal);
|
|
@@ -683,12 +711,12 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
|
|
|
683
711
|
busy: true,
|
|
684
712
|
createdAt: Date.now()
|
|
685
713
|
};
|
|
686
|
-
|
|
714
|
+
const ownsCache = setOwnedWebSocketSession(sessionId, entry, expectedCacheValue);
|
|
687
715
|
return {
|
|
688
716
|
socket,
|
|
689
|
-
entry,
|
|
717
|
+
entry: ownsCache ? entry : void 0,
|
|
690
718
|
release: ({ keep } = {}) => {
|
|
691
|
-
if (!keep || !isWebSocketReusable(entry.socket)) {
|
|
719
|
+
if (!ownsCache || !keep || !isWebSocketReusable(entry.socket)) {
|
|
692
720
|
closeWebSocketSilently(entry.socket);
|
|
693
721
|
if (entry.idleTimer) clearTimeout(entry.idleTimer);
|
|
694
722
|
deleteOwnedWebSocketSession(sessionId, entry);
|
|
@@ -728,19 +756,6 @@ function extractWebSocketCloseError(event) {
|
|
|
728
756
|
}
|
|
729
757
|
return /* @__PURE__ */ new Error("WebSocket closed");
|
|
730
758
|
}
|
|
731
|
-
async function decodeWebSocketData(data) {
|
|
732
|
-
if (typeof data === "string") return data;
|
|
733
|
-
if (data instanceof ArrayBuffer) return new TextDecoder().decode(new Uint8Array(data));
|
|
734
|
-
if (ArrayBuffer.isView(data)) {
|
|
735
|
-
const view = data;
|
|
736
|
-
return new TextDecoder().decode(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
|
|
737
|
-
}
|
|
738
|
-
if (data && typeof data === "object" && "arrayBuffer" in data) {
|
|
739
|
-
const arrayBuffer = await data.arrayBuffer();
|
|
740
|
-
return new TextDecoder().decode(new Uint8Array(arrayBuffer));
|
|
741
|
-
}
|
|
742
|
-
return null;
|
|
743
|
-
}
|
|
744
759
|
async function* parseWebSocket(socket, signal) {
|
|
745
760
|
const queue = [];
|
|
746
761
|
let pending = null;
|
|
@@ -754,29 +769,30 @@ async function* parseWebSocket(socket, signal) {
|
|
|
754
769
|
resolve();
|
|
755
770
|
};
|
|
756
771
|
const onMessage = (event) => {
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
queue.push(parsed);
|
|
770
|
-
wake();
|
|
771
|
-
} catch (cause) {
|
|
772
|
-
failed = new CodexProtocolError(`Invalid Codex WebSocket JSON: ${formatThrownValue(cause)}`, {
|
|
773
|
-
cause,
|
|
774
|
-
payload: text
|
|
775
|
-
});
|
|
772
|
+
const data = event && typeof event === "object" && "data" in event ? event.data : void 0;
|
|
773
|
+
if (typeof data !== "string") {
|
|
774
|
+
failed = new CodexProtocolError(MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE, { payload: data });
|
|
775
|
+
done = true;
|
|
776
|
+
wake();
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
try {
|
|
780
|
+
const parsed = JSON.parse(data);
|
|
781
|
+
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
782
|
+
if (type === "response.completed" || type === "response.done" || type === "response.incomplete") {
|
|
783
|
+
sawCompletion = true;
|
|
776
784
|
done = true;
|
|
777
|
-
wake();
|
|
778
785
|
}
|
|
779
|
-
|
|
786
|
+
queue.push(parsed);
|
|
787
|
+
wake();
|
|
788
|
+
} catch (cause) {
|
|
789
|
+
failed = new CodexProtocolError(`Invalid Codex WebSocket JSON: ${formatThrownValue(cause)}`, {
|
|
790
|
+
cause,
|
|
791
|
+
payload: data
|
|
792
|
+
});
|
|
793
|
+
done = true;
|
|
794
|
+
wake();
|
|
795
|
+
}
|
|
780
796
|
};
|
|
781
797
|
const onError = (event) => {
|
|
782
798
|
failed = extractWebSocketError(event);
|
|
@@ -870,7 +886,7 @@ async function* startWebSocketOutputOnFirstEvent(events, output, stream, onStart
|
|
|
870
886
|
yield event;
|
|
871
887
|
}
|
|
872
888
|
}
|
|
873
|
-
async function processWebSocketStream(url, body, headers, output, stream, model, onStart, options, abortFirstEventStream) {
|
|
889
|
+
async function processWebSocketStream(url, body, headers, output, stream, model, onStart, options, abortFirstEventStream, observePromptEgress, payloadVariant = "initial", onRequestSent) {
|
|
874
890
|
const { socket, entry, release } = await acquireWebSocket(url, headers, options?.sessionId, options?.signal);
|
|
875
891
|
let keepConnection = true;
|
|
876
892
|
const useCachedContext = options?.transport === "websocket-cached" || options?.transport === "auto";
|
|
@@ -878,24 +894,35 @@ async function processWebSocketStream(url, body, headers, output, stream, model,
|
|
|
878
894
|
const requestBody = useCachedContext && entry ? buildCachedWebSocketRequestBody(entry, fullBody) : fullBody;
|
|
879
895
|
try {
|
|
880
896
|
if (options?.signal?.aborted) throw transportAbortError(options.signal);
|
|
897
|
+
observePromptEgress?.(requestBody, {
|
|
898
|
+
egress: "native-codex-websocket",
|
|
899
|
+
payloadVariant
|
|
900
|
+
});
|
|
881
901
|
socket.send(JSON.stringify({
|
|
882
902
|
type: "response.create",
|
|
883
903
|
...requestBody
|
|
884
904
|
}));
|
|
905
|
+
onRequestSent?.();
|
|
885
906
|
await processResponsesStream(startWebSocketOutputOnFirstEvent(mapCodexEvents(parseWebSocket(socket, options?.signal)), output, stream, onStart), output, stream, model, {
|
|
886
907
|
serviceTier: options?.serviceTier,
|
|
887
908
|
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
|
|
888
909
|
abortFirstEventStream,
|
|
889
910
|
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
890
911
|
signal: options?.signal,
|
|
912
|
+
reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
|
|
913
|
+
sessionId: options?.sessionId,
|
|
914
|
+
authProfileId: options?.authProfileId
|
|
915
|
+
}),
|
|
891
916
|
resolveServiceTier: resolveCodexServiceTier,
|
|
892
|
-
applyServiceTierPricing: (usage, serviceTier) =>
|
|
917
|
+
applyServiceTierPricing: (usage, serviceTier) => applyResponsesServiceTierPricing(usage, serviceTier, model)
|
|
893
918
|
});
|
|
894
919
|
if (options?.signal?.aborted) keepConnection = false;
|
|
895
920
|
else if (useCachedContext && entry && output.responseId) {
|
|
896
921
|
const responseItems = convertResponsesMessages(model, { messages: [output] }, CODEX_TOOL_CALL_PROVIDERS, {
|
|
897
922
|
includeSystemPrompt: false,
|
|
898
|
-
replayResponsesItemIds: false
|
|
923
|
+
replayResponsesItemIds: false,
|
|
924
|
+
sessionId: options?.sessionId,
|
|
925
|
+
authProfileId: options?.authProfileId
|
|
899
926
|
}).filter((item) => item.type !== "function_call_output");
|
|
900
927
|
entry.continuation = {
|
|
901
928
|
lastRequestBody: fullBody,
|
|
@@ -911,17 +938,31 @@ async function processWebSocketStream(url, body, headers, output, stream, model,
|
|
|
911
938
|
release({ keep: keepConnection });
|
|
912
939
|
}
|
|
913
940
|
}
|
|
914
|
-
async function readChatGptResponsesErrorTextLimited(response) {
|
|
941
|
+
async function readChatGptResponsesErrorTextLimited(response, signal) {
|
|
915
942
|
const reader = response.body?.getReader();
|
|
916
943
|
if (!reader) return "";
|
|
917
944
|
const decoder = new TextDecoder();
|
|
918
945
|
let total = 0;
|
|
919
946
|
let text = "";
|
|
920
947
|
let reachedLimit = false;
|
|
948
|
+
let completed = false;
|
|
949
|
+
let cancelPromise;
|
|
950
|
+
const cancel = () => {
|
|
951
|
+
cancelPromise ??= reader.cancel(signal?.reason).catch(() => {});
|
|
952
|
+
return cancelPromise;
|
|
953
|
+
};
|
|
954
|
+
const onAbort = () => {
|
|
955
|
+
cancel();
|
|
956
|
+
};
|
|
957
|
+
if (signal?.aborted) onAbort();
|
|
958
|
+
else signal?.addEventListener("abort", onAbort, { once: true });
|
|
921
959
|
try {
|
|
922
960
|
while (true) {
|
|
923
961
|
const { value, done } = await reader.read();
|
|
924
|
-
if (done)
|
|
962
|
+
if (done) {
|
|
963
|
+
completed = true;
|
|
964
|
+
break;
|
|
965
|
+
}
|
|
925
966
|
if (!value || value.byteLength === 0) continue;
|
|
926
967
|
const remaining = OPENAI_CHATGPT_RESPONSES_ERROR_BODY_MAX_BYTES - total;
|
|
927
968
|
if (remaining <= 0) {
|
|
@@ -938,7 +979,8 @@ async function readChatGptResponsesErrorTextLimited(response) {
|
|
|
938
979
|
}
|
|
939
980
|
if (!reachedLimit) text += decoder.decode();
|
|
940
981
|
} finally {
|
|
941
|
-
|
|
982
|
+
signal?.removeEventListener("abort", onAbort);
|
|
983
|
+
if (!completed) await cancel();
|
|
942
984
|
try {
|
|
943
985
|
reader.releaseLock();
|
|
944
986
|
} catch {}
|
|
@@ -948,11 +990,12 @@ async function readChatGptResponsesErrorTextLimited(response) {
|
|
|
948
990
|
function parseErrorResponseText(raw, status, statusText) {
|
|
949
991
|
let message = raw || statusText || "Request failed";
|
|
950
992
|
let friendlyMessage;
|
|
993
|
+
let code;
|
|
951
994
|
try {
|
|
952
995
|
const err = JSON.parse(raw)?.error;
|
|
953
996
|
if (err) {
|
|
954
|
-
|
|
955
|
-
if (/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(code) || status === 429) {
|
|
997
|
+
code = err.code || err.type || void 0;
|
|
998
|
+
if (/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(code ?? "") || status === 429) {
|
|
956
999
|
const plan = err.plan_type ? ` (${err.plan_type.toLowerCase()} plan)` : "";
|
|
957
1000
|
const mins = err.resets_at ? Math.max(0, Math.round((err.resets_at * 1e3 - Date.now()) / 6e4)) : void 0;
|
|
958
1001
|
friendlyMessage = `You have hit your ChatGPT usage limit${plan}.${mins !== void 0 ? ` Try again in ~${mins} min.` : ""}`.trim();
|
|
@@ -961,6 +1004,7 @@ function parseErrorResponseText(raw, status, statusText) {
|
|
|
961
1004
|
}
|
|
962
1005
|
} catch {}
|
|
963
1006
|
return {
|
|
1007
|
+
...code ? { code } : {},
|
|
964
1008
|
message,
|
|
965
1009
|
friendlyMessage
|
|
966
1010
|
};
|
|
@@ -1012,4 +1056,4 @@ function buildWebSocketHeaders(initHeaders, additionalHeaders, accountId, token,
|
|
|
1012
1056
|
return headers;
|
|
1013
1057
|
}
|
|
1014
1058
|
//#endregion
|
|
1015
|
-
export { closeOpenAICodexWebSocketSessions, extractOpenAICodexAccountId,
|
|
1059
|
+
export { closeOpenAICodexWebSocketSessions, extractOpenAICodexAccountId, resetOpenAICodexWebSocketStateForTest, streamOpenAICodexResponses, streamSimpleOpenAICodexResponses };
|