@openclaw/ai 0.0.0 → 2026.7.1-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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +27 -3
  3. package/dist/anthropic-B5gZQM5X.mjs +1383 -0
  4. package/dist/api-registry-BXYnCOIR.d.mts +33 -0
  5. package/dist/azure-openai-responses-DNoSk8Uy.mjs +141 -0
  6. package/dist/azure-openai-responses-client-compat-a_O_GVQV.mjs +41 -0
  7. package/dist/diagnostics-BaTA9eVl.d.mts +25 -0
  8. package/dist/diagnostics-COpOtRwq.mjs +36 -0
  9. package/dist/diagnostics.d.mts +2 -0
  10. package/dist/diagnostics.mjs +2 -0
  11. package/dist/env-api-keys-CtMlqaQ4.mjs +171 -0
  12. package/dist/event-stream-0nZeBKl2.d.mts +26 -0
  13. package/dist/event-stream-ReMmOTzX.mjs +65 -0
  14. package/dist/event-stream.d.mts +2 -0
  15. package/dist/event-stream.mjs +2 -0
  16. package/dist/github-copilot-headers-BsH5cqGj.mjs +48 -0
  17. package/dist/google-D6sIQ1bL.mjs +55 -0
  18. package/dist/google-shared-ZPSl2qTi.mjs +548 -0
  19. package/dist/google-vertex-rDGwkoZK.mjs +111 -0
  20. package/dist/hash-CHgqbJmD.mjs +16 -0
  21. package/dist/headers-B_e4-1J0.mjs +9 -0
  22. package/dist/host-4t713IeR.mjs +37 -0
  23. package/dist/index-BoTnz8cv.d.mts +74 -0
  24. package/dist/index.d.mts +69 -0
  25. package/dist/index.mjs +7 -0
  26. package/dist/internal/anthropic.d.mts +234 -0
  27. package/dist/internal/anthropic.mjs +4 -0
  28. package/dist/internal/openai.d.mts +244 -0
  29. package/dist/internal/openai.mjs +7 -0
  30. package/dist/internal/runtime.d.mts +245 -0
  31. package/dist/internal/runtime.mjs +176 -0
  32. package/dist/internal/shared.d.mts +48 -0
  33. package/dist/internal/shared.mjs +3 -0
  34. package/dist/json-parse-DzNSIQBq.mjs +134 -0
  35. package/dist/llm-request-activity-CehVkZP-.mjs +35 -0
  36. package/dist/mistral-CePVNdws.mjs +563 -0
  37. package/dist/model-utils-DgmOla96.mjs +69 -0
  38. package/dist/openai-chatgpt-jwt-DhAAzLkj.mjs +39 -0
  39. package/dist/openai-chatgpt-responses-DVC4Bk_A.mjs +1068 -0
  40. package/dist/openai-completions-B9QLIq2U.mjs +844 -0
  41. package/dist/openai-responses-B6LylGxM.mjs +136 -0
  42. package/dist/openai-responses-shared-sj2YUPYc.mjs +1944 -0
  43. package/dist/openai-tool-projection-BknoV11q.mjs +195 -0
  44. package/dist/providers.d.mts +11 -0
  45. package/dist/providers.mjs +109 -0
  46. package/dist/reasoning-tag-text-partitioner-axhAdUwg.mjs +394 -0
  47. package/dist/sanitize-unicode-BZiVbGwK.d.mts +24 -0
  48. package/dist/sanitize-unicode-DT5o51ur.mjs +26 -0
  49. package/dist/src-CZ503MYJ.mjs +99 -0
  50. package/dist/stream-CREqxHgU.mjs +74 -0
  51. package/dist/stream-first-event-timeout-RjWszj8c.mjs +106 -0
  52. package/dist/streaming-byte-guard-BrbkbwUu.mjs +46 -0
  53. package/dist/tool-schema-json-projection-BXtBc_mD.mjs +74 -0
  54. package/dist/transform-messages-BhGF_fF4.mjs +507 -0
  55. package/dist/types-BVVgDSdq.d.mts +1 -0
  56. package/dist/types-DRgdPqaZ.d.mts +587 -0
  57. package/dist/types.d.mts +6 -0
  58. package/dist/types.mjs +5 -0
  59. package/dist/validation-BDMWOr8d.d.mts +9 -0
  60. package/dist/validation-FrchoOlv.mjs +199 -0
  61. package/dist/validation.d.mts +2 -0
  62. package/dist/validation.mjs +2 -0
  63. package/npm-shrinkwrap.json +645 -0
  64. package/package.json +74 -2
@@ -0,0 +1,1068 @@
1
+ import { n as getEnvApiKey } from "./env-api-keys-CtMlqaQ4.mjs";
2
+ import { i as formatThrownValue, n as createAssistantMessageDiagnostic, t as appendAssistantMessageDiagnostic } from "./diagnostics-COpOtRwq.mjs";
3
+ import { t as AssistantMessageEventStream } from "./event-stream-ReMmOTzX.mjs";
4
+ import { n as getAiTransportHost, r as resolveAiTransportHeaderSentinels } from "./host-4t713IeR.mjs";
5
+ import { t as headersToRecord } from "./headers-B_e4-1J0.mjs";
6
+ import { b as stripSystemPromptCacheBoundary, o as buildBaseOptions } from "./transform-messages-BhGF_fF4.mjs";
7
+ import { a as resolveResponsesReasoningEffort, i as processResponsesStream, n as convertResponsesMessages, s as convertResponsesToolPayload } from "./openai-responses-shared-sj2YUPYc.mjs";
8
+ import { i as getFirstStreamEventTimeoutMs, o as clampTimerTimeoutMs, r as getFirstStreamEventTimeoutHandler, s as resolveTimerTimeoutMs, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-RjWszj8c.mjs";
9
+ import { a as clampOpenAIPromptCacheKey } from "./openai-tool-projection-BknoV11q.mjs";
10
+ import { i as registerSessionResourceCleanup, n as resolveOpenAICodexAccountId } from "./openai-chatgpt-jwt-DhAAzLkj.mjs";
11
+ import { t as createSseByteGuard } from "./streaming-byte-guard-BrbkbwUu.mjs";
12
+ //#region packages/ai/src/providers/openai-chatgpt-responses.ts
13
+ const dynamicImport = (specifier) => import(specifier);
14
+ function loadNodeOs() {
15
+ if (typeof process === "undefined" || !(process.versions?.node || process.versions?.bun)) return null;
16
+ return process.getBuiltinModule?.("node:os") ?? null;
17
+ }
18
+ const os = loadNodeOs();
19
+ const DEFAULT_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
20
+ const MAX_RETRIES = 3;
21
+ const BASE_DELAY_MS = 1e3;
22
+ const REQUEST_COMPRESSION_ZSTD_LEVEL = 3;
23
+ const RETRY_AFTER_HTTP_DATE_RE = /^(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT|(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), \d{2}-(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-\d{2} \d{2}:\d{2}:\d{2} GMT|(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [ \d]\d \d{2}:\d{2}:\d{2} \d{4})$/;
24
+ const CODEX_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set(["openai", "opencode"]);
25
+ const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
26
+ const WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE = "websocket_connection_limit_reached";
27
+ const OPENAI_CHATGPT_RESPONSES_ERROR_BODY_MAX_BYTES = 16 * 1024;
28
+ const OPENAI_CHATGPT_RESPONSES_SUCCESS_BODY_MAX_BYTES = 16 * 1024 * 1024;
29
+ const CODEX_RESPONSE_STATUSES = /* @__PURE__ */ new Set([
30
+ "completed",
31
+ "incomplete",
32
+ "failed",
33
+ "cancelled",
34
+ "queued",
35
+ "in_progress"
36
+ ]);
37
+ function isRetryableError(status, errorText) {
38
+ if (status === 429 || status === 500 || status === 502 || status === 503 || status === 504) return true;
39
+ return /rate.?limit|overloaded|service.?unavailable|upstream.?connect|connection.?refused/i.test(errorText);
40
+ }
41
+ function sleep(ms, signal) {
42
+ return new Promise((resolve, reject) => {
43
+ if (signal?.aborted) {
44
+ reject(/* @__PURE__ */ new Error("Request was aborted"));
45
+ return;
46
+ }
47
+ const timeout = setTimeout(resolve, ms);
48
+ signal?.addEventListener("abort", () => {
49
+ clearTimeout(timeout);
50
+ reject(/* @__PURE__ */ new Error("Request was aborted"));
51
+ });
52
+ });
53
+ }
54
+ function resolveRequestTimeoutMs(options) {
55
+ const timeoutMs = options?.timeoutMs;
56
+ return typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0 ? resolveTimerTimeoutMs(timeoutMs, 1) : void 0;
57
+ }
58
+ function buildRequestSignal(baseSignal, timeoutMs) {
59
+ if (timeoutMs === void 0) return baseSignal;
60
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
61
+ if (!baseSignal) return timeoutSignal;
62
+ return AbortSignal.any([baseSignal, timeoutSignal]);
63
+ }
64
+ function isRequestTimeoutError(error, callerSignal, requestSignal, timeoutMs) {
65
+ if (timeoutMs === void 0 || callerSignal?.aborted || !requestSignal?.aborted) return false;
66
+ if (!(error instanceof Error)) return false;
67
+ return error.name === "AbortError" || error.name === "TimeoutError" || error.message === "Request was aborted";
68
+ }
69
+ function formatRequestTimeoutError(timeoutMs, cause) {
70
+ return new Error(`Request timed out after ${timeoutMs}ms`, { cause: cause instanceof Error ? cause : void 0 });
71
+ }
72
+ function compressRequestBodyZstd(bodyJson) {
73
+ if (typeof process === "undefined" || !(process.versions?.node || process.versions?.bun)) return null;
74
+ const zlib = process.getBuiltinModule?.("node:zlib");
75
+ if (!zlib || typeof zlib.zstdCompressSync !== "function") return null;
76
+ try {
77
+ const compressed = zlib.zstdCompressSync(bodyJson, { params: { [zlib.constants.ZSTD_c_compressionLevel]: REQUEST_COMPRESSION_ZSTD_LEVEL } });
78
+ return Uint8Array.from(compressed);
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+ const streamOpenAICodexResponses = (model, context, options) => {
84
+ const stream = new AssistantMessageEventStream();
85
+ (async () => {
86
+ let requestTimeoutMs;
87
+ let requestTimeoutSignal;
88
+ let activeSignal;
89
+ let firstEventAbort;
90
+ const output = {
91
+ role: "assistant",
92
+ content: [],
93
+ api: "openai-chatgpt-responses",
94
+ provider: model.provider,
95
+ model: model.id,
96
+ usage: {
97
+ input: 0,
98
+ output: 0,
99
+ cacheRead: 0,
100
+ cacheWrite: 0,
101
+ totalTokens: 0,
102
+ cost: {
103
+ input: 0,
104
+ output: 0,
105
+ cacheRead: 0,
106
+ cacheWrite: 0,
107
+ total: 0
108
+ }
109
+ },
110
+ stopReason: "stop",
111
+ timestamp: Date.now()
112
+ };
113
+ try {
114
+ const unresolvedApiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
115
+ if (!unresolvedApiKey) throw new Error(`No API key for provider: ${model.provider}`);
116
+ const apiKey = getAiTransportHost().resolveSecretSentinel(unresolvedApiKey);
117
+ const modelHeaders = resolveAiTransportHeaderSentinels(model.headers);
118
+ const optionHeaders = resolveAiTransportHeaderSentinels(options?.headers);
119
+ const accountId = extractOpenAICodexAccountId(apiKey);
120
+ let body = buildRequestBody(model, context, options);
121
+ const nextBody = await options?.onPayload?.(body, model);
122
+ if (nextBody !== void 0) body = nextBody;
123
+ const websocketRequestId = options?.sessionId || createCodexRequestId();
124
+ const sseHeaders = buildSSEHeaders(modelHeaders, optionHeaders, accountId, apiKey, options?.sessionId);
125
+ const websocketHeaders = buildWebSocketHeaders(modelHeaders, optionHeaders, accountId, apiKey, websocketRequestId);
126
+ const bodyJson = JSON.stringify(body);
127
+ requestTimeoutMs = resolveRequestTimeoutMs(options);
128
+ requestTimeoutSignal = buildRequestSignal(options?.signal, requestTimeoutMs);
129
+ firstEventAbort = createFirstStreamEventAbortController(requestTimeoutSignal);
130
+ activeSignal = firstEventAbort.signal;
131
+ const requestOptions = activeSignal === options?.signal ? options : {
132
+ ...options,
133
+ signal: activeSignal
134
+ };
135
+ const transport = options?.transport || "auto";
136
+ const websocketDisabledForSession = transport === "auto" && isWebSocketSseFallbackActive(options?.sessionId);
137
+ if (websocketDisabledForSession) recordWebSocketSseFallback(options?.sessionId);
138
+ if (transport !== "sse" && !websocketDisabledForSession) {
139
+ let websocketStarted = false;
140
+ let retriedWebSocketConnectionLimit = false;
141
+ while (true) {
142
+ websocketStarted = false;
143
+ try {
144
+ await processWebSocketStream(resolveCodexWebSocketUrl(model.baseUrl), body, websocketHeaders, output, stream, model, () => {
145
+ websocketStarted = true;
146
+ }, requestOptions, firstEventAbort.abort);
147
+ if (activeSignal?.aborted) throw new Error("Request was aborted");
148
+ stream.push({
149
+ type: "done",
150
+ reason: output.stopReason,
151
+ message: output
152
+ });
153
+ stream.end();
154
+ return;
155
+ } catch (error) {
156
+ const aborted = activeSignal?.aborted;
157
+ const connectionLimitBeforeStart = !websocketStarted && isWebSocketConnectionLimitReachedError(error);
158
+ if (!aborted && connectionLimitBeforeStart && !retriedWebSocketConnectionLimit) {
159
+ retriedWebSocketConnectionLimit = true;
160
+ continue;
161
+ }
162
+ if (aborted || isCodexNonTransportError(error) && !connectionLimitBeforeStart) throw error;
163
+ appendAssistantMessageDiagnostic(output, createAssistantMessageDiagnostic("provider_transport_failure", error, {
164
+ configuredTransport: transport,
165
+ fallbackTransport: transport === "auto" && !websocketStarted ? "sse" : void 0,
166
+ eventsEmitted: websocketStarted,
167
+ phase: websocketStarted ? "after_message_stream_start" : "before_message_stream_start",
168
+ requestBytes: new TextEncoder().encode(bodyJson).byteLength
169
+ }));
170
+ recordWebSocketFailure(options?.sessionId, error, { activateSseFallback: transport === "auto" });
171
+ if (websocketStarted || transport !== "auto") throw error;
172
+ recordWebSocketSseFallback(options?.sessionId);
173
+ break;
174
+ }
175
+ }
176
+ }
177
+ const compressedBody = model.provider === "openai" && !sseHeaders.has("content-encoding") ? compressRequestBodyZstd(bodyJson) : null;
178
+ if (compressedBody) sseHeaders.set("content-encoding", "zstd");
179
+ const sseBody = compressedBody ?? bodyJson;
180
+ let response;
181
+ let lastError;
182
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
183
+ if (activeSignal?.aborted) throw new Error("Request was aborted");
184
+ try {
185
+ response = await fetch(resolveCodexUrl(model.baseUrl), {
186
+ method: "POST",
187
+ headers: sseHeaders,
188
+ body: sseBody,
189
+ signal: activeSignal
190
+ });
191
+ await options?.onResponse?.({
192
+ status: response.status,
193
+ headers: headersToRecord(response.headers)
194
+ }, model);
195
+ if (response.ok) break;
196
+ const errorText = await readChatGptResponsesErrorTextLimited(response);
197
+ if (attempt < MAX_RETRIES && isRetryableError(response.status, errorText)) {
198
+ let delayMs = BASE_DELAY_MS * 2 ** attempt;
199
+ const retryAfterMs = response.headers.get("retry-after-ms");
200
+ if (retryAfterMs !== null) {
201
+ const trimmedRetryAfterMs = retryAfterMs.trim();
202
+ const millis = Number(trimmedRetryAfterMs);
203
+ if (/^\d+(?:\.\d+)?$/.test(trimmedRetryAfterMs) && Number.isFinite(millis)) delayMs = clampTimerTimeoutMs(millis, 0) ?? delayMs;
204
+ } else {
205
+ const retryAfter = response.headers.get("retry-after");
206
+ if (retryAfter) {
207
+ const trimmedRetryAfter = retryAfter.trim();
208
+ const seconds = Number(trimmedRetryAfter);
209
+ if (/^\d+$/.test(trimmedRetryAfter) && Number.isFinite(seconds)) delayMs = clampTimerTimeoutMs(seconds * 1e3, 0) ?? delayMs;
210
+ else if (RETRY_AFTER_HTTP_DATE_RE.test(trimmedRetryAfter)) {
211
+ const date = Date.parse(trimmedRetryAfter);
212
+ if (!Number.isNaN(date)) delayMs = clampTimerTimeoutMs(date - Date.now(), 0) ?? delayMs;
213
+ }
214
+ }
215
+ }
216
+ await sleep(delayMs, activeSignal);
217
+ continue;
218
+ }
219
+ const info = await parseErrorResponse(new Response(errorText, {
220
+ status: response.status,
221
+ statusText: response.statusText
222
+ }));
223
+ throw new Error(info.friendlyMessage || info.message);
224
+ } catch (error) {
225
+ if (error instanceof Error) {
226
+ if (isRequestTimeoutError(error, options?.signal, requestTimeoutSignal, requestTimeoutMs) && requestTimeoutMs !== void 0) throw formatRequestTimeoutError(requestTimeoutMs, error);
227
+ if (error.name === "AbortError" || error.message === "Request was aborted") throw new Error("Request was aborted", { cause: error });
228
+ if (error.name === "TimeoutError" && requestTimeoutMs !== void 0) throw new Error(`Request timed out after ${requestTimeoutMs}ms`, { cause: error });
229
+ }
230
+ lastError = error instanceof Error ? error : new Error(String(error));
231
+ if (attempt < MAX_RETRIES && !lastError.message.includes("usage limit")) {
232
+ await sleep(BASE_DELAY_MS * 2 ** attempt, activeSignal);
233
+ continue;
234
+ }
235
+ throw lastError;
236
+ }
237
+ }
238
+ if (!response?.ok) throw lastError ?? /* @__PURE__ */ new Error("Failed after retries");
239
+ if (!response.body) throw new Error("No response body");
240
+ stream.push({
241
+ type: "start",
242
+ partial: output
243
+ });
244
+ await processStream(response, output, stream, model, options, firstEventAbort.abort);
245
+ if (activeSignal?.aborted) throw new Error("Request was aborted");
246
+ stream.push({
247
+ type: "done",
248
+ reason: output.stopReason,
249
+ message: output
250
+ });
251
+ stream.end();
252
+ } catch (error) {
253
+ const normalizedError = isRequestTimeoutError(error, options?.signal, requestTimeoutSignal, requestTimeoutMs) && requestTimeoutMs !== void 0 ? formatRequestTimeoutError(requestTimeoutMs, error) : error;
254
+ for (const block of output.content) delete block.partialJson;
255
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
256
+ output.errorMessage = normalizedError instanceof Error ? normalizedError.message : String(normalizedError);
257
+ stream.push({
258
+ type: "error",
259
+ reason: output.stopReason,
260
+ error: output
261
+ });
262
+ stream.end();
263
+ } finally {
264
+ firstEventAbort?.dispose();
265
+ }
266
+ })();
267
+ return stream;
268
+ };
269
+ const streamSimpleOpenAICodexResponses = (model, context, options) => {
270
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider);
271
+ if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
272
+ const base = buildBaseOptions(model, options, apiKey);
273
+ return streamOpenAICodexResponses(model, context, {
274
+ ...base,
275
+ reasoningEffort: resolveResponsesReasoningEffort(model, options?.reasoning)
276
+ });
277
+ };
278
+ function buildRequestBody(model, context, options) {
279
+ const messages = convertResponsesMessages(model, context, CODEX_TOOL_CALL_PROVIDERS, {
280
+ includeSystemPrompt: false,
281
+ replayResponsesItemIds: false
282
+ });
283
+ const body = {
284
+ model: model.id,
285
+ store: false,
286
+ stream: true,
287
+ instructions: stripSystemPromptCacheBoundary(context.systemPrompt ?? "") || "You are a helpful assistant.",
288
+ input: messages,
289
+ text: { verbosity: options?.textVerbosity || "low" },
290
+ include: ["reasoning.encrypted_content"],
291
+ prompt_cache_key: options?.cacheRetention === "none" ? void 0 : clampOpenAIPromptCacheKey(options?.promptCacheKey ?? options?.sessionId),
292
+ tool_choice: "auto",
293
+ parallel_tool_calls: true
294
+ };
295
+ if (options?.temperature !== void 0) body.temperature = options.temperature;
296
+ if (options?.serviceTier !== void 0) body.service_tier = options.serviceTier;
297
+ if (context.tools) {
298
+ const converted = convertResponsesToolPayload(context.tools, { strict: null });
299
+ if (converted.projection.inputToolCount > 0 || converted.projection.diagnostics.length > 0) {
300
+ body.tools = converted.tools;
301
+ if (body.tools.length === 0) {
302
+ delete body.tools;
303
+ delete body.tool_choice;
304
+ delete body.parallel_tool_calls;
305
+ }
306
+ }
307
+ }
308
+ if (options?.reasoningEffort !== void 0) {
309
+ const effort = options.reasoningEffort === "none" ? model.thinkingLevelMap?.off ?? "none" : model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort;
310
+ if (effort !== null) body.reasoning = {
311
+ effort,
312
+ summary: options.reasoningSummary ?? "auto"
313
+ };
314
+ }
315
+ return body;
316
+ }
317
+ function getServiceTierCostMultiplier(model, serviceTier) {
318
+ switch (serviceTier) {
319
+ case "flex": return .5;
320
+ case "priority": return model.id === "gpt-5.5" ? 2.5 : 2;
321
+ default: return 1;
322
+ }
323
+ }
324
+ function applyServiceTierPricing(usage, serviceTier, model) {
325
+ const multiplier = getServiceTierCostMultiplier(model, serviceTier);
326
+ if (multiplier === 1) return;
327
+ usage.cost.input *= multiplier;
328
+ usage.cost.output *= multiplier;
329
+ usage.cost.cacheRead *= multiplier;
330
+ usage.cost.cacheWrite *= multiplier;
331
+ usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
332
+ }
333
+ function resolveCodexServiceTier(responseServiceTier, requestServiceTier) {
334
+ if (responseServiceTier === "default" && (requestServiceTier === "flex" || requestServiceTier === "priority")) return requestServiceTier;
335
+ return responseServiceTier ?? requestServiceTier;
336
+ }
337
+ function resolveCodexUrl(baseUrl) {
338
+ const normalized = (baseUrl && baseUrl.trim().length > 0 ? baseUrl : DEFAULT_CODEX_BASE_URL).replace(/\/+$/, "");
339
+ if (normalized.endsWith("/codex/responses")) return normalized;
340
+ if (normalized.endsWith("/codex")) return `${normalized}/responses`;
341
+ return `${normalized}/codex/responses`;
342
+ }
343
+ function resolveCodexWebSocketUrl(baseUrl) {
344
+ const url = new URL(resolveCodexUrl(baseUrl));
345
+ if (url.protocol === "https:") url.protocol = "wss:";
346
+ if (url.protocol === "http:") url.protocol = "ws:";
347
+ return url.toString();
348
+ }
349
+ async function processStream(response, output, stream, model, options, abortFirstEventStream) {
350
+ await processResponsesStream(mapCodexEvents(parseSSE(response)), output, stream, model, {
351
+ serviceTier: options?.serviceTier,
352
+ firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
353
+ abortFirstEventStream,
354
+ onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
355
+ resolveServiceTier: resolveCodexServiceTier,
356
+ applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model)
357
+ });
358
+ }
359
+ var CodexApiError = class extends Error {
360
+ constructor(message, options) {
361
+ super(message);
362
+ this.name = "CodexApiError";
363
+ this.code = options?.code;
364
+ this.payload = options?.payload;
365
+ this.cause = options?.cause;
366
+ }
367
+ };
368
+ var CodexProtocolError = class extends Error {
369
+ constructor(message, options) {
370
+ super(message);
371
+ this.name = "CodexProtocolError";
372
+ this.payload = options?.payload;
373
+ this.cause = options?.cause;
374
+ }
375
+ };
376
+ function isCodexNonTransportError(error) {
377
+ return error instanceof CodexApiError || error instanceof CodexProtocolError;
378
+ }
379
+ function isWebSocketConnectionLimitReachedError(error) {
380
+ return error instanceof CodexApiError && error.code === WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE;
381
+ }
382
+ function extractCodexEventError(event) {
383
+ const nested = event.error && typeof event.error === "object" ? event.error : void 0;
384
+ return {
385
+ code: typeof event.code === "string" ? event.code : typeof nested?.code === "string" ? nested.code : void 0,
386
+ message: typeof event.message === "string" ? event.message : typeof nested?.message === "string" ? nested.message : void 0
387
+ };
388
+ }
389
+ async function* mapCodexEvents(events) {
390
+ for await (const event of events) {
391
+ const type = typeof event.type === "string" ? event.type : void 0;
392
+ if (!type) continue;
393
+ if (type === "error") {
394
+ const { code, message } = extractCodexEventError(event);
395
+ throw new CodexApiError(`Codex error: ${message || code || JSON.stringify(event)}`, {
396
+ code,
397
+ payload: event
398
+ });
399
+ }
400
+ if (type === "response.failed") {
401
+ const response = event.response;
402
+ const code = response?.error?.code;
403
+ const message = response?.error?.message;
404
+ throw new CodexApiError(message || "Codex response failed", {
405
+ code,
406
+ payload: event
407
+ });
408
+ }
409
+ if (type === "response.done" || type === "response.completed" || type === "response.incomplete") {
410
+ const response = event.response;
411
+ const normalizedResponse = response ? {
412
+ ...response,
413
+ status: normalizeCodexStatus(response.status)
414
+ } : response;
415
+ yield {
416
+ ...event,
417
+ type: "response.completed",
418
+ response: normalizedResponse
419
+ };
420
+ return;
421
+ }
422
+ yield event;
423
+ }
424
+ }
425
+ function normalizeCodexStatus(status) {
426
+ if (typeof status !== "string") return;
427
+ return CODEX_RESPONSE_STATUSES.has(status) ? status : void 0;
428
+ }
429
+ async function* parseSSE(response) {
430
+ if (!response.body) return;
431
+ const reader = response.body.getReader();
432
+ const guard = createSseByteGuard(reader, {
433
+ maxBytes: OPENAI_CHATGPT_RESPONSES_SUCCESS_BODY_MAX_BYTES,
434
+ onOverflow: ({ size, maxBytes }) => /* @__PURE__ */ new Error(`OpenAI ChatGPT Responses success body exceeded ${maxBytes} bytes (received ${size})`)
435
+ });
436
+ const decoder = new TextDecoder();
437
+ let buffer = "";
438
+ try {
439
+ while (true) {
440
+ const { done, value } = await guard.read();
441
+ if (done) break;
442
+ buffer += decoder.decode(value, { stream: true });
443
+ let idx = buffer.indexOf("\n\n");
444
+ while (idx !== -1) {
445
+ const chunk = buffer.slice(0, idx);
446
+ buffer = buffer.slice(idx + 2);
447
+ const dataLines = chunk.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim());
448
+ if (dataLines.length > 0) {
449
+ const data = dataLines.join("\n").trim();
450
+ if (data && data !== "[DONE]") try {
451
+ yield JSON.parse(data);
452
+ } catch (cause) {
453
+ throw new CodexProtocolError(`Invalid Codex SSE JSON: ${formatThrownValue(cause)}`, {
454
+ cause,
455
+ payload: data
456
+ });
457
+ }
458
+ }
459
+ idx = buffer.indexOf("\n\n");
460
+ }
461
+ }
462
+ } finally {
463
+ try {
464
+ await guard.cancel();
465
+ } catch {}
466
+ try {
467
+ reader.releaseLock();
468
+ } catch {}
469
+ }
470
+ }
471
+ const parseSSEForTest = parseSSE;
472
+ const OPENAI_BETA_RESPONSES_WEBSOCKETS = "responses_websockets=2026-02-06";
473
+ const SESSION_WEBSOCKET_CACHE_TTL_MS = 300 * 1e3;
474
+ const SESSION_WEBSOCKET_MAX_AGE_MS = 3300 * 1e3;
475
+ const websocketSessionCache = /* @__PURE__ */ new Map();
476
+ const websocketDebugStats = /* @__PURE__ */ new Map();
477
+ const websocketSseFallbackSessions = /* @__PURE__ */ new Set();
478
+ let cachedWebsocket = null;
479
+ function getOrCreateWebSocketDebugStats(sessionId) {
480
+ let stats = websocketDebugStats.get(sessionId);
481
+ if (!stats) {
482
+ stats = {
483
+ requests: 0,
484
+ connectionsCreated: 0,
485
+ connectionsReused: 0,
486
+ cachedContextRequests: 0,
487
+ storeTrueRequests: 0,
488
+ fullContextRequests: 0,
489
+ deltaRequests: 0,
490
+ lastInputItems: 0,
491
+ websocketFailures: 0,
492
+ sseFallbacks: 0
493
+ };
494
+ websocketDebugStats.set(sessionId, stats);
495
+ }
496
+ return stats;
497
+ }
498
+ function resetOpenAICodexWebSocketDebugStats(sessionId) {
499
+ cachedWebsocket = null;
500
+ if (sessionId) {
501
+ websocketDebugStats.delete(sessionId);
502
+ websocketSseFallbackSessions.delete(sessionId);
503
+ return;
504
+ }
505
+ websocketDebugStats.clear();
506
+ websocketSseFallbackSessions.clear();
507
+ }
508
+ function closeOpenAICodexWebSocketSessions(sessionId) {
509
+ const closeEntry = (entry) => {
510
+ if (entry.idleTimer) clearTimeout(entry.idleTimer);
511
+ closeWebSocketSilently(entry.socket, 1e3, "debug_close");
512
+ };
513
+ if (sessionId) {
514
+ const entry = websocketSessionCache.get(sessionId);
515
+ if (entry) closeEntry(entry);
516
+ websocketSessionCache.delete(sessionId);
517
+ return;
518
+ }
519
+ for (const entry of websocketSessionCache.values()) closeEntry(entry);
520
+ websocketSessionCache.clear();
521
+ }
522
+ registerSessionResourceCleanup(closeOpenAICodexWebSocketSessions);
523
+ function isWebSocketSseFallbackActive(sessionId) {
524
+ return sessionId ? websocketSseFallbackSessions.has(sessionId) : false;
525
+ }
526
+ function recordWebSocketSseFallback(sessionId) {
527
+ if (!sessionId) return;
528
+ const stats = getOrCreateWebSocketDebugStats(sessionId);
529
+ stats.sseFallbacks++;
530
+ stats.websocketFallbackActive = isWebSocketSseFallbackActive(sessionId);
531
+ }
532
+ function recordWebSocketFailure(sessionId, error, options) {
533
+ if (!sessionId) return;
534
+ if (options.activateSseFallback) websocketSseFallbackSessions.add(sessionId);
535
+ const stats = getOrCreateWebSocketDebugStats(sessionId);
536
+ stats.websocketFailures++;
537
+ stats.lastWebSocketError = formatThrownValue(error);
538
+ stats.websocketFallbackActive = isWebSocketSseFallbackActive(sessionId);
539
+ }
540
+ async function getWebSocketConstructor() {
541
+ if (cachedWebsocket) return cachedWebsocket;
542
+ if (process?.versions?.bun && (process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy)) {
543
+ const getProxyForUrl = (await dynamicImport("proxy-from-env")).getProxyForUrl;
544
+ cachedWebsocket = class extends WebSocket {
545
+ constructor(url, options) {
546
+ let opts;
547
+ if (Array.isArray(options) || typeof options === "string") opts = { protocols: options };
548
+ else opts = { ...options };
549
+ const proxy = getProxyForUrl(url.toString().replace(/^wss:/, "https:").replace(/^ws:/, "http:"));
550
+ super(url, {
551
+ ...opts,
552
+ ...proxy ? { proxy } : {}
553
+ });
554
+ }
555
+ };
556
+ return cachedWebsocket;
557
+ }
558
+ const ctor = globalThis.WebSocket;
559
+ if (typeof ctor !== "function") return null;
560
+ return ctor;
561
+ }
562
+ var WebSocketCloseError = class extends Error {
563
+ constructor(message, options) {
564
+ super(message);
565
+ this.name = "WebSocketCloseError";
566
+ this.code = options?.code;
567
+ this.reason = options?.reason;
568
+ this.wasClean = options?.wasClean;
569
+ }
570
+ };
571
+ function getWebSocketReadyState(socket) {
572
+ const readyState = socket.readyState;
573
+ return typeof readyState === "number" ? readyState : void 0;
574
+ }
575
+ function isWebSocketReusable(socket) {
576
+ const readyState = getWebSocketReadyState(socket);
577
+ return readyState === void 0 || readyState === 1;
578
+ }
579
+ function isWebSocketSessionExpired(entry) {
580
+ return Date.now() - entry.createdAt >= SESSION_WEBSOCKET_MAX_AGE_MS;
581
+ }
582
+ function closeWebSocketSilently(socket, code = 1e3, reason = "done") {
583
+ try {
584
+ socket.close(code, reason);
585
+ } catch {}
586
+ }
587
+ function scheduleSessionWebSocketExpiry(sessionId, entry) {
588
+ if (entry.idleTimer) clearTimeout(entry.idleTimer);
589
+ entry.idleTimer = setTimeout(() => {
590
+ if (entry.busy) return;
591
+ closeWebSocketSilently(entry.socket, 1e3, "idle_timeout");
592
+ websocketSessionCache.delete(sessionId);
593
+ }, SESSION_WEBSOCKET_CACHE_TTL_MS);
594
+ }
595
+ async function connectWebSocket(url, headers, signal) {
596
+ const WebSocketCtor = await getWebSocketConstructor();
597
+ if (!WebSocketCtor) throw new Error("WebSocket transport is not available in this runtime");
598
+ const wsHeaders = headersToRecord(headers);
599
+ delete wsHeaders["OpenAI-Beta"];
600
+ return new Promise((resolve, reject) => {
601
+ let settled = false;
602
+ let socket;
603
+ try {
604
+ socket = new WebSocketCtor(url, { headers: wsHeaders });
605
+ } catch (error) {
606
+ reject(error instanceof Error ? error : new Error(String(error)));
607
+ return;
608
+ }
609
+ const onOpen = () => {
610
+ if (settled) return;
611
+ settled = true;
612
+ cleanup();
613
+ resolve(socket);
614
+ };
615
+ const onError = (event) => {
616
+ const error = extractWebSocketError(event);
617
+ if (settled) return;
618
+ settled = true;
619
+ cleanup();
620
+ reject(error);
621
+ };
622
+ const onClose = (event) => {
623
+ const error = extractWebSocketCloseError(event);
624
+ if (settled) return;
625
+ settled = true;
626
+ cleanup();
627
+ reject(error);
628
+ };
629
+ const onAbort = () => {
630
+ if (settled) return;
631
+ settled = true;
632
+ cleanup();
633
+ socket.close(1e3, "aborted");
634
+ reject(/* @__PURE__ */ new Error("Request was aborted"));
635
+ };
636
+ const cleanup = () => {
637
+ socket.removeEventListener("open", onOpen);
638
+ socket.removeEventListener("error", onError);
639
+ socket.removeEventListener("close", onClose);
640
+ signal?.removeEventListener("abort", onAbort);
641
+ };
642
+ if (signal?.aborted) {
643
+ onAbort();
644
+ return;
645
+ }
646
+ socket.addEventListener("open", onOpen);
647
+ socket.addEventListener("error", onError);
648
+ socket.addEventListener("close", onClose);
649
+ signal?.addEventListener("abort", onAbort);
650
+ });
651
+ }
652
+ async function acquireWebSocket(url, headers, sessionId, signal) {
653
+ if (!sessionId) {
654
+ const socket = await connectWebSocket(url, headers, signal);
655
+ return {
656
+ socket,
657
+ reused: false,
658
+ release: ({ keep } = {}) => {
659
+ if (keep === false) {
660
+ closeWebSocketSilently(socket);
661
+ return;
662
+ }
663
+ closeWebSocketSilently(socket);
664
+ }
665
+ };
666
+ }
667
+ const cached = websocketSessionCache.get(sessionId);
668
+ if (cached) {
669
+ if (cached.idleTimer) {
670
+ clearTimeout(cached.idleTimer);
671
+ cached.idleTimer = void 0;
672
+ }
673
+ if (!cached.busy && isWebSocketSessionExpired(cached)) {
674
+ closeWebSocketSilently(cached.socket, 1e3, "connection_age_limit");
675
+ websocketSessionCache.delete(sessionId);
676
+ } else if (!cached.busy && isWebSocketReusable(cached.socket)) {
677
+ cached.busy = true;
678
+ return {
679
+ socket: cached.socket,
680
+ entry: cached,
681
+ reused: true,
682
+ release: ({ keep } = {}) => {
683
+ if (!keep || !isWebSocketReusable(cached.socket)) {
684
+ closeWebSocketSilently(cached.socket);
685
+ websocketSessionCache.delete(sessionId);
686
+ return;
687
+ }
688
+ cached.busy = false;
689
+ scheduleSessionWebSocketExpiry(sessionId, cached);
690
+ }
691
+ };
692
+ }
693
+ if (cached.busy) {
694
+ const socket = await connectWebSocket(url, headers, signal);
695
+ return {
696
+ socket,
697
+ reused: false,
698
+ release: () => {
699
+ closeWebSocketSilently(socket);
700
+ }
701
+ };
702
+ }
703
+ if (!isWebSocketReusable(cached.socket)) {
704
+ closeWebSocketSilently(cached.socket);
705
+ websocketSessionCache.delete(sessionId);
706
+ }
707
+ }
708
+ const socket = await connectWebSocket(url, headers, signal);
709
+ const entry = {
710
+ socket,
711
+ busy: true,
712
+ createdAt: Date.now()
713
+ };
714
+ websocketSessionCache.set(sessionId, entry);
715
+ return {
716
+ socket,
717
+ entry,
718
+ reused: false,
719
+ release: ({ keep } = {}) => {
720
+ if (!keep || !isWebSocketReusable(entry.socket)) {
721
+ closeWebSocketSilently(entry.socket);
722
+ if (entry.idleTimer) clearTimeout(entry.idleTimer);
723
+ if (websocketSessionCache.get(sessionId) === entry) websocketSessionCache.delete(sessionId);
724
+ return;
725
+ }
726
+ entry.busy = false;
727
+ scheduleSessionWebSocketExpiry(sessionId, entry);
728
+ }
729
+ };
730
+ }
731
+ function extractWebSocketError(event) {
732
+ if (event && typeof event === "object") {
733
+ const message = "message" in event ? event.message : void 0;
734
+ if (typeof message === "string" && message.length > 0) return new Error(message);
735
+ const nestedError = "error" in event ? event.error : void 0;
736
+ if (nestedError instanceof Error && nestedError.message.length > 0) return nestedError;
737
+ if (nestedError && typeof nestedError === "object" && "message" in nestedError) {
738
+ const nestedMessage = nestedError.message;
739
+ if (typeof nestedMessage === "string" && nestedMessage.length > 0) return new Error(nestedMessage);
740
+ }
741
+ }
742
+ return /* @__PURE__ */ new Error("WebSocket error");
743
+ }
744
+ function extractWebSocketCloseError(event) {
745
+ if (event && typeof event === "object") {
746
+ const code = "code" in event ? event.code : void 0;
747
+ const reason = "reason" in event ? event.reason : void 0;
748
+ const wasClean = "wasClean" in event ? event.wasClean : void 0;
749
+ const codeText = typeof code === "number" ? ` ${code}` : "";
750
+ let reasonText = typeof reason === "string" && reason.length > 0 ? ` ${reason}` : "";
751
+ if (!reasonText && code === WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE) reasonText = " message too big";
752
+ return new WebSocketCloseError(`WebSocket closed${codeText}${reasonText}`.trim(), {
753
+ code: typeof code === "number" ? code : void 0,
754
+ reason: typeof reason === "string" && reason.length > 0 ? reason : void 0,
755
+ wasClean: typeof wasClean === "boolean" ? wasClean : void 0
756
+ });
757
+ }
758
+ return /* @__PURE__ */ new Error("WebSocket closed");
759
+ }
760
+ async function decodeWebSocketData(data) {
761
+ if (typeof data === "string") return data;
762
+ if (data instanceof ArrayBuffer) return new TextDecoder().decode(new Uint8Array(data));
763
+ if (ArrayBuffer.isView(data)) {
764
+ const view = data;
765
+ return new TextDecoder().decode(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
766
+ }
767
+ if (data && typeof data === "object" && "arrayBuffer" in data) {
768
+ const arrayBuffer = await data.arrayBuffer();
769
+ return new TextDecoder().decode(new Uint8Array(arrayBuffer));
770
+ }
771
+ return null;
772
+ }
773
+ async function* parseWebSocket(socket, signal) {
774
+ const queue = [];
775
+ let pending = null;
776
+ let done = false;
777
+ let failed = null;
778
+ let sawCompletion = false;
779
+ const wake = () => {
780
+ if (!pending) return;
781
+ const resolve = pending;
782
+ pending = null;
783
+ resolve();
784
+ };
785
+ const onMessage = (event) => {
786
+ (async () => {
787
+ let text = null;
788
+ try {
789
+ if (!event || typeof event !== "object" || !("data" in event)) return;
790
+ text = await decodeWebSocketData(event.data);
791
+ if (!text) return;
792
+ const parsed = JSON.parse(text);
793
+ const type = typeof parsed.type === "string" ? parsed.type : "";
794
+ if (type === "response.completed" || type === "response.done" || type === "response.incomplete") {
795
+ sawCompletion = true;
796
+ done = true;
797
+ }
798
+ queue.push(parsed);
799
+ wake();
800
+ } catch (cause) {
801
+ failed = new CodexProtocolError(`Invalid Codex WebSocket JSON: ${formatThrownValue(cause)}`, {
802
+ cause,
803
+ payload: text
804
+ });
805
+ done = true;
806
+ wake();
807
+ }
808
+ })();
809
+ };
810
+ const onError = (event) => {
811
+ failed = extractWebSocketError(event);
812
+ done = true;
813
+ wake();
814
+ };
815
+ const onClose = (event) => {
816
+ if (sawCompletion) {
817
+ done = true;
818
+ wake();
819
+ return;
820
+ }
821
+ if (!failed) failed = extractWebSocketCloseError(event);
822
+ done = true;
823
+ wake();
824
+ };
825
+ const onAbort = () => {
826
+ failed = /* @__PURE__ */ new Error("Request was aborted");
827
+ done = true;
828
+ wake();
829
+ };
830
+ socket.addEventListener("message", onMessage);
831
+ socket.addEventListener("error", onError);
832
+ socket.addEventListener("close", onClose);
833
+ signal?.addEventListener("abort", onAbort);
834
+ try {
835
+ while (true) {
836
+ if (signal?.aborted) throw new Error("Request was aborted");
837
+ if (queue.length > 0) {
838
+ yield queue.shift();
839
+ continue;
840
+ }
841
+ if (done) break;
842
+ await new Promise((resolve) => {
843
+ pending = resolve;
844
+ });
845
+ }
846
+ if (failed) throw toLintErrorObject(failed, "Non-Error thrown");
847
+ if (!sawCompletion) throw new Error("WebSocket stream closed before response.completed");
848
+ } finally {
849
+ socket.removeEventListener("message", onMessage);
850
+ socket.removeEventListener("error", onError);
851
+ socket.removeEventListener("close", onClose);
852
+ signal?.removeEventListener("abort", onAbort);
853
+ }
854
+ }
855
+ function requestBodyWithoutInput(body) {
856
+ const { input: _input, previous_response_id: _previousResponseId, ...rest } = body;
857
+ return rest;
858
+ }
859
+ function responseInputsEqual(a, b) {
860
+ return JSON.stringify(a ?? []) === JSON.stringify(b ?? []);
861
+ }
862
+ function requestBodiesMatchExceptInput(a, b) {
863
+ return JSON.stringify(requestBodyWithoutInput(a)) === JSON.stringify(requestBodyWithoutInput(b));
864
+ }
865
+ function getCachedWebSocketInputDelta(body, continuation) {
866
+ if (!requestBodiesMatchExceptInput(body, continuation.lastRequestBody)) return;
867
+ const currentInput = body.input ?? [];
868
+ const baseline = [...continuation.lastRequestBody.input ?? [], ...continuation.lastResponseItems];
869
+ if (currentInput.length < baseline.length) return;
870
+ if (!responseInputsEqual(currentInput.slice(0, baseline.length), baseline)) return;
871
+ return currentInput.slice(baseline.length);
872
+ }
873
+ function buildCachedWebSocketRequestBody(entry, body) {
874
+ const continuation = entry.continuation;
875
+ if (!continuation) return body;
876
+ const delta = getCachedWebSocketInputDelta(body, continuation);
877
+ if (!delta || !continuation.lastResponseId) {
878
+ entry.continuation = void 0;
879
+ return body;
880
+ }
881
+ return {
882
+ ...body,
883
+ previous_response_id: continuation.lastResponseId,
884
+ input: delta
885
+ };
886
+ }
887
+ async function* startWebSocketOutputOnFirstEvent(events, output, stream, onStart) {
888
+ let started = false;
889
+ for await (const event of events) {
890
+ if (!started) {
891
+ started = true;
892
+ onStart();
893
+ stream.push({
894
+ type: "start",
895
+ partial: output
896
+ });
897
+ }
898
+ yield event;
899
+ }
900
+ }
901
+ async function processWebSocketStream(url, body, headers, output, stream, model, onStart, options, abortFirstEventStream) {
902
+ const { socket, entry, reused, release } = await acquireWebSocket(url, headers, options?.sessionId, options?.signal);
903
+ let keepConnection = true;
904
+ const useCachedContext = options?.transport === "websocket-cached" || options?.transport === "auto";
905
+ const fullBody = body;
906
+ const requestBody = useCachedContext && entry ? buildCachedWebSocketRequestBody(entry, fullBody) : fullBody;
907
+ const stats = options?.sessionId ? getOrCreateWebSocketDebugStats(options.sessionId) : void 0;
908
+ if (stats) {
909
+ stats.requests++;
910
+ if (reused) stats.connectionsReused++;
911
+ else stats.connectionsCreated++;
912
+ if (useCachedContext) stats.cachedContextRequests++;
913
+ if (requestBody.store === true) stats.storeTrueRequests++;
914
+ stats.lastInputItems = requestBody.input?.length ?? 0;
915
+ if (requestBody.previous_response_id) {
916
+ stats.deltaRequests++;
917
+ stats.lastDeltaInputItems = requestBody.input?.length ?? 0;
918
+ stats.lastPreviousResponseId = requestBody.previous_response_id;
919
+ } else {
920
+ stats.fullContextRequests++;
921
+ stats.lastDeltaInputItems = void 0;
922
+ stats.lastPreviousResponseId = void 0;
923
+ }
924
+ }
925
+ try {
926
+ if (options?.signal?.aborted) throw new Error("Request was aborted");
927
+ socket.send(JSON.stringify({
928
+ type: "response.create",
929
+ ...requestBody
930
+ }));
931
+ await processResponsesStream(startWebSocketOutputOnFirstEvent(mapCodexEvents(parseWebSocket(socket, options?.signal)), output, stream, onStart), output, stream, model, {
932
+ serviceTier: options?.serviceTier,
933
+ firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
934
+ abortFirstEventStream,
935
+ onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
936
+ resolveServiceTier: resolveCodexServiceTier,
937
+ applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model)
938
+ });
939
+ if (options?.signal?.aborted) keepConnection = false;
940
+ else if (useCachedContext && entry && output.responseId) {
941
+ const responseItems = convertResponsesMessages(model, { messages: [output] }, CODEX_TOOL_CALL_PROVIDERS, {
942
+ includeSystemPrompt: false,
943
+ replayResponsesItemIds: false
944
+ }).filter((item) => item.type !== "function_call_output");
945
+ entry.continuation = {
946
+ lastRequestBody: fullBody,
947
+ lastResponseId: output.responseId,
948
+ lastResponseItems: responseItems
949
+ };
950
+ }
951
+ } catch (error) {
952
+ if (entry) entry.continuation = void 0;
953
+ keepConnection = false;
954
+ throw error;
955
+ } finally {
956
+ release({ keep: keepConnection });
957
+ }
958
+ }
959
+ async function readChatGptResponsesErrorTextLimited(response) {
960
+ const reader = response.body?.getReader();
961
+ if (!reader) return "";
962
+ const decoder = new TextDecoder();
963
+ let total = 0;
964
+ let text = "";
965
+ let reachedLimit = false;
966
+ try {
967
+ while (true) {
968
+ const { value, done } = await reader.read();
969
+ if (done) break;
970
+ if (!value || value.byteLength === 0) continue;
971
+ const remaining = OPENAI_CHATGPT_RESPONSES_ERROR_BODY_MAX_BYTES - total;
972
+ if (remaining <= 0) {
973
+ reachedLimit = true;
974
+ break;
975
+ }
976
+ const chunk = value.byteLength > remaining ? value.subarray(0, remaining) : value;
977
+ total += chunk.byteLength;
978
+ text += decoder.decode(chunk, { stream: true });
979
+ if (total >= OPENAI_CHATGPT_RESPONSES_ERROR_BODY_MAX_BYTES) {
980
+ reachedLimit = true;
981
+ break;
982
+ }
983
+ }
984
+ text += decoder.decode();
985
+ } finally {
986
+ if (reachedLimit) await reader.cancel().catch(() => {});
987
+ try {
988
+ reader.releaseLock();
989
+ } catch {}
990
+ }
991
+ return text;
992
+ }
993
+ async function parseErrorResponse(response) {
994
+ const raw = await readChatGptResponsesErrorTextLimited(response);
995
+ let message = raw || response.statusText || "Request failed";
996
+ let friendlyMessage;
997
+ try {
998
+ const err = JSON.parse(raw)?.error;
999
+ if (err) {
1000
+ const code = err.code || err.type || "";
1001
+ if (/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(code) || response.status === 429) {
1002
+ const plan = err.plan_type ? ` (${err.plan_type.toLowerCase()} plan)` : "";
1003
+ const mins = err.resets_at ? Math.max(0, Math.round((err.resets_at * 1e3 - Date.now()) / 6e4)) : void 0;
1004
+ friendlyMessage = `You have hit your ChatGPT usage limit${plan}.${mins !== void 0 ? ` Try again in ~${mins} min.` : ""}`.trim();
1005
+ }
1006
+ message = err.message || friendlyMessage || message;
1007
+ }
1008
+ } catch {}
1009
+ return {
1010
+ message,
1011
+ friendlyMessage
1012
+ };
1013
+ }
1014
+ function extractOpenAICodexAccountId(token) {
1015
+ const accountId = resolveOpenAICodexAccountId(token);
1016
+ if (accountId) return accountId;
1017
+ throw new Error("Failed to extract accountId from token");
1018
+ }
1019
+ function createCodexRequestId() {
1020
+ const crypto = globalThis.crypto;
1021
+ if (typeof crypto?.randomUUID === "function") return crypto.randomUUID();
1022
+ if (typeof crypto?.getRandomValues === "function") {
1023
+ const bytes = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
1024
+ return `codex_${Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
1025
+ }
1026
+ throw new Error("Secure random request id generation is unavailable");
1027
+ }
1028
+ function buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token) {
1029
+ const headers = new Headers(initHeaders);
1030
+ for (const [key, value] of Object.entries(additionalHeaders || {})) headers.set(key, value);
1031
+ headers.set("Authorization", `Bearer ${token}`);
1032
+ headers.set("chatgpt-account-id", accountId);
1033
+ headers.set("originator", "openclaw");
1034
+ const userAgent = os ? `openclaw (${os.platform()} ${os.release()}; ${os.arch()})` : "openclaw (browser)";
1035
+ headers.set("User-Agent", userAgent);
1036
+ return headers;
1037
+ }
1038
+ function buildSSEHeaders(initHeaders, additionalHeaders, accountId, token, sessionId) {
1039
+ const headers = buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token);
1040
+ headers.set("OpenAI-Beta", "responses=experimental");
1041
+ headers.set("accept", "text/event-stream");
1042
+ headers.set("content-type", "application/json");
1043
+ if (sessionId) {
1044
+ headers.set("session_id", sessionId);
1045
+ headers.set("x-client-request-id", sessionId);
1046
+ }
1047
+ return headers;
1048
+ }
1049
+ function buildWebSocketHeaders(initHeaders, additionalHeaders, accountId, token, requestId) {
1050
+ const headers = buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token);
1051
+ headers.delete("accept");
1052
+ headers.delete("content-type");
1053
+ headers.delete("OpenAI-Beta");
1054
+ headers.delete("openai-beta");
1055
+ headers.set("OpenAI-Beta", OPENAI_BETA_RESPONSES_WEBSOCKETS);
1056
+ headers.set("x-client-request-id", requestId);
1057
+ headers.set("session_id", requestId);
1058
+ return headers;
1059
+ }
1060
+ function toLintErrorObject(value, fallbackMessage) {
1061
+ if (value instanceof Error) return value;
1062
+ if (typeof value === "string") return new Error(value);
1063
+ const error = new Error(fallbackMessage, { cause: value });
1064
+ if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
1065
+ return error;
1066
+ }
1067
+ //#endregion
1068
+ export { closeOpenAICodexWebSocketSessions, extractOpenAICodexAccountId, parseSSEForTest, resetOpenAICodexWebSocketDebugStats, streamOpenAICodexResponses, streamSimpleOpenAICodexResponses };