@openclaw/ai 2026.7.2-beta.7 → 2026.7.33

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 (78) hide show
  1. package/README.md +5 -6
  2. package/dist/{anthropic-CH4UUnZr.mjs → anthropic-BML72tLz.mjs} +468 -101
  3. package/dist/{api-registry-DlMgPR39.d.mts → api-registry-BXYnCOIR.d.mts} +2 -1
  4. package/dist/{azure-openai-responses-client-compat-C7K7QfUE.mjs → azure-openai-responses-client-compat-a_O_GVQV.mjs} +2 -23
  5. package/dist/{azure-openai-responses-CImcwB83.mjs → azure-openai-responses-kEN8II8I.mjs} +13 -11
  6. package/dist/{env-api-keys-DrgeBuva.mjs → env-api-keys-CtMlqaQ4.mjs} +4 -5
  7. package/dist/{event-stream-YjaPW20U.d.mts → event-stream-0nZeBKl2.d.mts} +1 -1
  8. package/dist/{event-stream-D8n2uFee.mjs → event-stream-ReMmOTzX.mjs} +6 -14
  9. package/dist/event-stream.d.mts +1 -1
  10. package/dist/event-stream.mjs +1 -1
  11. package/dist/{github-copilot-headers-NCJtz9i0.mjs → github-copilot-headers-BsH5cqGj.mjs} +12 -1
  12. package/dist/{google-CtSg0iTS.mjs → google-DTM4O3e9.mjs} +5 -5
  13. package/dist/{google-shared-DNBz5rcD.mjs → google-shared-BAP0BQVi.mjs} +22 -34
  14. package/dist/{google-vertex-31f1uS9L.mjs → google-vertex-D58MZHCQ.mjs} +19 -6
  15. package/dist/host-4t713IeR.mjs +37 -0
  16. package/dist/{anthropic-SrGtwsJu.d.mts → index-BoTnz8cv.d.mts} +1 -27
  17. package/dist/index.d.mts +50 -7
  18. package/dist/index.mjs +5 -5
  19. package/dist/internal/anthropic.d.mts +62 -22
  20. package/dist/internal/anthropic.mjs +4 -5
  21. package/dist/internal/openai.d.mts +82 -98
  22. package/dist/internal/openai.mjs +6 -8
  23. package/dist/internal/runtime.d.mts +34 -13
  24. package/dist/internal/runtime.mjs +14 -26
  25. package/dist/internal/shared.d.mts +9 -26
  26. package/dist/internal/shared.mjs +2 -6
  27. package/dist/{json-parse-BvXNt1-7.mjs → json-parse-DzNSIQBq.mjs} +6 -4
  28. package/dist/{llm-request-activity-BjtkplhG.mjs → llm-request-activity-CehVkZP-.mjs} +19 -1
  29. package/dist/{mistral-CWmpvWYh.mjs → mistral-CePVNdws.mjs} +39 -141
  30. package/dist/model-utils-DgmOla96.mjs +69 -0
  31. package/dist/{openai-chatgpt-responses-B84Ibtrd.mjs → openai-chatgpt-responses-BHqbcipc.mjs} +154 -101
  32. package/dist/{openai-completions-DsOxhOD1.mjs → openai-completions-9qYFVS7H.mjs} +295 -81
  33. package/dist/{openai-responses-BT7A3sLu.mjs → openai-responses-D6l9URyV.mjs} +8 -10
  34. package/dist/openai-responses-shared-L3PWZvkm.mjs +1949 -0
  35. package/dist/{openai-tool-projection-OhX64DoP.mjs → openai-tool-projection-BPlyXt8H.mjs} +12 -32
  36. package/dist/providers.d.mts +2 -3
  37. package/dist/providers.mjs +10 -11
  38. package/dist/reasoning-tag-text-partitioner-axhAdUwg.mjs +394 -0
  39. package/dist/{src-QkygScBs.mjs → src-CZ503MYJ.mjs} +5 -24
  40. package/dist/{stream-first-event-timeout-BBys9hSb.mjs → stream-first-event-timeout-RjWszj8c.mjs} +22 -2
  41. package/dist/transform-messages-BhGF_fF4.mjs +507 -0
  42. package/dist/{types-bzp5k29J.d.mts → types-DRgdPqaZ.d.mts} +0 -19
  43. package/dist/types.d.mts +5 -5
  44. package/dist/types.mjs +4 -4
  45. package/dist/{validation-B-j7cOYp.d.mts → validation-BDMWOr8d.d.mts} +1 -1
  46. package/dist/{validation-DAa_yFOM.mjs → validation-FrchoOlv.mjs} +7 -2
  47. package/dist/validation.d.mts +1 -1
  48. package/dist/validation.mjs +1 -1
  49. package/npm-shrinkwrap.json +645 -0
  50. package/package.json +11 -36
  51. package/dist/anthropic-usage-DWU-x8MI.mjs +0 -459
  52. package/dist/cache-retention-0x979a5V.mjs +0 -12
  53. package/dist/deferred-event-buffer-DAvyP7qA.mjs +0 -19
  54. package/dist/error-coercion-DgxlWC0n.mjs +0 -15
  55. package/dist/host-B9GUmcra.d.mts +0 -173
  56. package/dist/host-Dog2WQiR.mjs +0 -369
  57. package/dist/index-BVVgDSdq.d.mts +0 -1
  58. package/dist/internal/retry-after.d.mts +0 -5
  59. package/dist/internal/retry-after.mjs +0 -101
  60. package/dist/number-coercion-DvG7SNMg.mjs +0 -129
  61. package/dist/openai-completions-compat-DBWjXoMZ.d.mts +0 -43
  62. package/dist/openai-prompt-cache-2uo_1OR1.d.mts +0 -7
  63. package/dist/openai-prompt-cache-mZTCdRPo.mjs +0 -12
  64. package/dist/openai-reasoning-compat-YgeLncHw.mjs +0 -396
  65. package/dist/openai-responses-shared-pXl6Wd8S.mjs +0 -392
  66. package/dist/openai-responses-stream-internal-Cw5txaGW.mjs +0 -2964
  67. package/dist/prompt-cache-stability-Cwcjv_fx.d.mts +0 -13
  68. package/dist/provider-error-CAEvRjry.mjs +0 -47
  69. package/dist/provider-options-D8bB3z9b.d.mts +0 -144
  70. package/dist/reasoning-tag-text-partitioner-CGDyLWUR.mjs +0 -12209
  71. package/dist/simple-options-9lhRrN73.mjs +0 -50
  72. package/dist/stream-first-event-timeout-DvDeSucC.d.mts +0 -29
  73. package/dist/tls-certificate-errors-DXSpluKI.mjs +0 -93
  74. package/dist/tool-result-text-CTpIRbYd.mjs +0 -225
  75. package/dist/transform-messages-C8mBqZxF.mjs +0 -2
  76. package/dist/transport-stream-shared-D81p90xq.mjs +0 -297
  77. package/dist/transports.d.mts +0 -573
  78. package/dist/transports.mjs +0 -4110
@@ -1,41 +1,14 @@
1
- import { n as getEnvApiKey } from "./env-api-keys-DrgeBuva.mjs";
1
+ import { n as getEnvApiKey } from "./env-api-keys-CtMlqaQ4.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-D8n2uFee.mjs";
4
- import { n as getAiTransportHost, r as resolveAiTransportHeaderSentinels } from "./host-Dog2WQiR.mjs";
5
- import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
6
- import { d as stripSystemPromptCacheBoundary } from "./tool-result-text-CTpIRbYd.mjs";
7
- import { t as toErrorObject } from "./error-coercion-DgxlWC0n.mjs";
8
- import { n as clampTimerTimeoutMs, o as resolveTimerTimeoutMs } from "./number-coercion-DvG7SNMg.mjs";
9
- import { parseRetryAfterHttpDateMs } from "./internal/retry-after.mjs";
10
- import { d as transportAbortError } from "./transport-stream-shared-D81p90xq.mjs";
11
- import { _t as supportsOpenAITemperature, n as processResponsesStream } from "./openai-responses-stream-internal-Cw5txaGW.mjs";
12
- import { i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-BBys9hSb.mjs";
3
+ import { t as AssistantMessageEventStream } from "./event-stream-ReMmOTzX.mjs";
4
+ import { n as getAiTransportHost, r as resolveAiTransportHeaderSentinels } from "./host-4t713IeR.mjs";
13
5
  import { t as headersToRecord } from "./headers-B_e4-1J0.mjs";
14
- import { n as buildBaseOptions } from "./simple-options-9lhRrN73.mjs";
15
- import { i as resolveResponsesReasoningEffort, n as convertResponsesMessages, o as convertResponsesToolPayload } from "./openai-responses-shared-pXl6Wd8S.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-L3PWZvkm.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-BPlyXt8H.mjs";
16
10
  import { i as registerSessionResourceCleanup, n as resolveOpenAICodexAccountId } from "./openai-chatgpt-jwt-DhAAzLkj.mjs";
17
11
  import { t as createSseByteGuard } from "./streaming-byte-guard-BrbkbwUu.mjs";
18
- import { t as inspectTlsCertificateError } from "./tls-certificate-errors-DXSpluKI.mjs";
19
- //#region packages/ai/src/internal/retry-sleep.ts
20
- function sleepWithAbort(ms, signal) {
21
- return new Promise((resolve, reject) => {
22
- if (signal?.aborted) {
23
- reject(/* @__PURE__ */ new Error("Request was aborted"));
24
- return;
25
- }
26
- const onAbort = () => {
27
- clearTimeout(timeout);
28
- signal?.removeEventListener("abort", onAbort);
29
- reject(/* @__PURE__ */ new Error("Request was aborted"));
30
- };
31
- const timeout = setTimeout(() => {
32
- signal?.removeEventListener("abort", onAbort);
33
- resolve();
34
- }, ms);
35
- signal?.addEventListener("abort", onAbort, { once: true });
36
- });
37
- }
38
- //#endregion
39
12
  //#region packages/ai/src/providers/openai-chatgpt-responses.ts
40
13
  const dynamicImport = (specifier) => import(specifier);
41
14
  function loadNodeOs() {
@@ -44,9 +17,10 @@ function loadNodeOs() {
44
17
  }
45
18
  const os = loadNodeOs();
46
19
  const DEFAULT_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
47
- const DEFAULT_MAX_RETRIES = 3;
20
+ const MAX_RETRIES = 3;
48
21
  const BASE_DELAY_MS = 1e3;
49
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})$/;
50
24
  const CODEX_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set(["openai", "opencode"]);
51
25
  const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
52
26
  const WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE = "websocket_connection_limit_reached";
@@ -64,21 +38,18 @@ function isRetryableError(status, errorText) {
64
38
  if (status === 429 || status === 500 || status === 502 || status === 503 || status === 504) return true;
65
39
  return /rate.?limit|overloaded|service.?unavailable|upstream.?connect|connection.?refused/i.test(errorText);
66
40
  }
67
- function resolveHttpRetryDelayMs(response, attempt) {
68
- const fallbackMs = BASE_DELAY_MS * 2 ** attempt;
69
- const retryAfterMs = response.headers.get("retry-after-ms");
70
- if (retryAfterMs) {
71
- const trimmed = retryAfterMs.trim();
72
- const millis = Number(trimmed);
73
- if (/^\d+(?:\.\d+)?$/.test(trimmed) && Number.isFinite(millis)) return clampTimerTimeoutMs(millis, 0) ?? fallbackMs;
74
- }
75
- const retryAfter = response.headers.get("retry-after");
76
- if (!retryAfter) return fallbackMs;
77
- const trimmed = retryAfter.trim();
78
- const seconds = Number(trimmed);
79
- if (/^\d+$/.test(trimmed) && Number.isFinite(seconds)) return clampTimerTimeoutMs(seconds * 1e3, 0) ?? fallbackMs;
80
- const retryAt = parseRetryAfterHttpDateMs(trimmed);
81
- return retryAt === void 0 ? fallbackMs : clampTimerTimeoutMs(retryAt - Date.now(), 0) ?? fallbackMs;
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
+ });
82
53
  }
83
54
  function resolveRequestTimeoutMs(options) {
84
55
  const timeoutMs = options?.timeoutMs;
@@ -149,7 +120,10 @@ const streamOpenAICodexResponses = (model, context, options) => {
149
120
  let body = buildRequestBody(model, context, options);
150
121
  const nextBody = await options?.onPayload?.(body, model);
151
122
  if (nextBody !== void 0) body = nextBody;
152
- const sessionId = clampOpenAIPromptCacheKey(options?.sessionId);
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);
153
127
  requestTimeoutMs = resolveRequestTimeoutMs(options);
154
128
  requestTimeoutSignal = buildRequestSignal(options?.signal, requestTimeoutMs);
155
129
  firstEventAbort = createFirstStreamEventAbortController(requestTimeoutSignal);
@@ -160,8 +134,8 @@ const streamOpenAICodexResponses = (model, context, options) => {
160
134
  };
161
135
  const transport = options?.transport || "auto";
162
136
  const websocketDisabledForSession = transport === "auto" && isWebSocketSseFallbackActive(options?.sessionId);
137
+ if (websocketDisabledForSession) recordWebSocketSseFallback(options?.sessionId);
163
138
  if (transport !== "sse" && !websocketDisabledForSession) {
164
- const websocketHeaders = buildWebSocketHeaders(modelHeaders, optionHeaders, accountId, apiKey, sessionId || createCodexRequestId());
165
139
  let websocketStarted = false;
166
140
  let retriedWebSocketConnectionLimit = false;
167
141
  while (true) {
@@ -170,7 +144,7 @@ const streamOpenAICodexResponses = (model, context, options) => {
170
144
  await processWebSocketStream(resolveCodexWebSocketUrl(model.baseUrl), body, websocketHeaders, output, stream, model, () => {
171
145
  websocketStarted = true;
172
146
  }, requestOptions, firstEventAbort.abort);
173
- if (activeSignal?.aborted) throw transportAbortError(activeSignal);
147
+ if (activeSignal?.aborted) throw new Error("Request was aborted");
174
148
  stream.push({
175
149
  type: "done",
176
150
  reason: output.stopReason,
@@ -191,60 +165,75 @@ const streamOpenAICodexResponses = (model, context, options) => {
191
165
  fallbackTransport: transport === "auto" && !websocketStarted ? "sse" : void 0,
192
166
  eventsEmitted: websocketStarted,
193
167
  phase: websocketStarted ? "after_message_stream_start" : "before_message_stream_start",
194
- requestBytes: new TextEncoder().encode(JSON.stringify(body)).byteLength
168
+ requestBytes: new TextEncoder().encode(bodyJson).byteLength
195
169
  }));
196
- if (transport === "auto" && options?.sessionId) websocketSseFallbackSessions.add(options.sessionId);
170
+ recordWebSocketFailure(options?.sessionId, error, { activateSseFallback: transport === "auto" });
197
171
  if (websocketStarted || transport !== "auto") throw error;
172
+ recordWebSocketSseFallback(options?.sessionId);
198
173
  break;
199
174
  }
200
175
  }
201
176
  }
202
- const sseHeaders = buildSSEHeaders(modelHeaders, optionHeaders, accountId, apiKey, sessionId);
203
- const bodyJson = JSON.stringify(body);
204
177
  const compressedBody = model.provider === "openai" && !sseHeaders.has("content-encoding") ? compressRequestBodyZstd(bodyJson) : null;
205
178
  if (compressedBody) sseHeaders.set("content-encoding", "zstd");
206
179
  const sseBody = compressedBody ?? bodyJson;
207
180
  let response;
208
181
  let lastError;
209
- const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;
210
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
211
- if (activeSignal?.aborted) throw transportAbortError(activeSignal);
212
- let attemptResponse;
213
- let errorText;
182
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
183
+ if (activeSignal?.aborted) throw new Error("Request was aborted");
214
184
  try {
215
- attemptResponse = await fetch(resolveCodexUrl(model.baseUrl), {
185
+ response = await fetch(resolveCodexUrl(model.baseUrl), {
216
186
  method: "POST",
217
187
  headers: sseHeaders,
218
188
  body: sseBody,
219
189
  signal: activeSignal
220
190
  });
221
- response = attemptResponse;
222
191
  await options?.onResponse?.({
223
- status: attemptResponse.status,
224
- headers: headersToRecord(attemptResponse.headers)
192
+ status: response.status,
193
+ headers: headersToRecord(response.headers)
225
194
  }, model);
226
- if (attemptResponse.ok) break;
227
- errorText = await readChatGptResponsesErrorTextLimited(attemptResponse);
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);
228
224
  } catch (error) {
229
225
  if (error instanceof Error) {
230
226
  if (isRequestTimeoutError(error, options?.signal, requestTimeoutSignal, requestTimeoutMs) && requestTimeoutMs !== void 0) throw formatRequestTimeoutError(requestTimeoutMs, error);
231
227
  if (error.name === "AbortError" || error.message === "Request was aborted") throw new Error("Request was aborted", { cause: error });
232
228
  if (error.name === "TimeoutError" && requestTimeoutMs !== void 0) throw new Error(`Request timed out after ${requestTimeoutMs}ms`, { cause: error });
233
229
  }
234
- const tlsCertificateError = inspectTlsCertificateError(error);
235
- lastError = toErrorObject(error, String(error));
236
- if (attempt < maxRetries && !lastError.message.includes("usage limit") && !tlsCertificateError) {
237
- await sleepWithAbort(BASE_DELAY_MS * 2 ** attempt, activeSignal);
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);
238
233
  continue;
239
234
  }
240
235
  throw lastError;
241
236
  }
242
- if (attempt < maxRetries && isRetryableError(attemptResponse.status, errorText)) {
243
- await sleepWithAbort(resolveHttpRetryDelayMs(attemptResponse, attempt), activeSignal);
244
- continue;
245
- }
246
- const info = parseErrorResponseText(errorText, attemptResponse.status, attemptResponse.statusText);
247
- throw new Error(info.friendlyMessage || info.message);
248
237
  }
249
238
  if (!response?.ok) throw lastError ?? /* @__PURE__ */ new Error("Failed after retries");
250
239
  if (!response.body) throw new Error("No response body");
@@ -253,7 +242,7 @@ const streamOpenAICodexResponses = (model, context, options) => {
253
242
  partial: output
254
243
  });
255
244
  await processStream(response, output, stream, model, options, firstEventAbort.abort);
256
- if (activeSignal?.aborted) throw transportAbortError(activeSignal);
245
+ if (activeSignal?.aborted) throw new Error("Request was aborted");
257
246
  stream.push({
258
247
  type: "done",
259
248
  reason: output.stopReason,
@@ -303,7 +292,7 @@ function buildRequestBody(model, context, options) {
303
292
  tool_choice: "auto",
304
293
  parallel_tool_calls: true
305
294
  };
306
- if (options?.temperature !== void 0 && supportsOpenAITemperature(model)) body.temperature = options.temperature;
295
+ if (options?.temperature !== void 0) body.temperature = options.temperature;
307
296
  if (options?.serviceTier !== void 0) body.service_tier = options.serviceTier;
308
297
  if (context.tools) {
309
298
  const converted = convertResponsesToolPayload(context.tools, { strict: null });
@@ -363,7 +352,6 @@ async function processStream(response, output, stream, model, options, abortFirs
363
352
  firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
364
353
  abortFirstEventStream,
365
354
  onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
366
- signal: options?.signal,
367
355
  resolveServiceTier: resolveCodexServiceTier,
368
356
  applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model)
369
357
  });
@@ -485,10 +473,36 @@ const OPENAI_BETA_RESPONSES_WEBSOCKETS = "responses_websockets=2026-02-06";
485
473
  const SESSION_WEBSOCKET_CACHE_TTL_MS = 300 * 1e3;
486
474
  const SESSION_WEBSOCKET_MAX_AGE_MS = 3300 * 1e3;
487
475
  const websocketSessionCache = /* @__PURE__ */ new Map();
476
+ const websocketDebugStats = /* @__PURE__ */ new Map();
488
477
  const websocketSseFallbackSessions = /* @__PURE__ */ new Set();
489
478
  let cachedWebsocket = null;
490
- function resetOpenAICodexWebSocketStateForTest() {
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) {
491
499
  cachedWebsocket = null;
500
+ if (sessionId) {
501
+ websocketDebugStats.delete(sessionId);
502
+ websocketSseFallbackSessions.delete(sessionId);
503
+ return;
504
+ }
505
+ websocketDebugStats.clear();
492
506
  websocketSseFallbackSessions.clear();
493
507
  }
494
508
  function closeOpenAICodexWebSocketSessions(sessionId) {
@@ -509,6 +523,20 @@ registerSessionResourceCleanup(closeOpenAICodexWebSocketSessions);
509
523
  function isWebSocketSseFallbackActive(sessionId) {
510
524
  return sessionId ? websocketSseFallbackSessions.has(sessionId) : false;
511
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
+ }
512
540
  async function getWebSocketConstructor() {
513
541
  if (cachedWebsocket) return cachedWebsocket;
514
542
  if (process?.versions?.bun && (process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy)) {
@@ -556,15 +584,12 @@ function closeWebSocketSilently(socket, code = 1e3, reason = "done") {
556
584
  socket.close(code, reason);
557
585
  } catch {}
558
586
  }
559
- function deleteOwnedWebSocketSession(sessionId, entry) {
560
- if (websocketSessionCache.get(sessionId) === entry) websocketSessionCache.delete(sessionId);
561
- }
562
587
  function scheduleSessionWebSocketExpiry(sessionId, entry) {
563
588
  if (entry.idleTimer) clearTimeout(entry.idleTimer);
564
589
  entry.idleTimer = setTimeout(() => {
565
590
  if (entry.busy) return;
566
591
  closeWebSocketSilently(entry.socket, 1e3, "idle_timeout");
567
- deleteOwnedWebSocketSession(sessionId, entry);
592
+ websocketSessionCache.delete(sessionId);
568
593
  }, SESSION_WEBSOCKET_CACHE_TTL_MS);
569
594
  }
570
595
  async function connectWebSocket(url, headers, signal) {
@@ -629,6 +654,7 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
629
654
  const socket = await connectWebSocket(url, headers, signal);
630
655
  return {
631
656
  socket,
657
+ reused: false,
632
658
  release: ({ keep } = {}) => {
633
659
  if (keep === false) {
634
660
  closeWebSocketSilently(socket);
@@ -652,10 +678,11 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
652
678
  return {
653
679
  socket: cached.socket,
654
680
  entry: cached,
681
+ reused: true,
655
682
  release: ({ keep } = {}) => {
656
683
  if (!keep || !isWebSocketReusable(cached.socket)) {
657
684
  closeWebSocketSilently(cached.socket);
658
- deleteOwnedWebSocketSession(sessionId, cached);
685
+ websocketSessionCache.delete(sessionId);
659
686
  return;
660
687
  }
661
688
  cached.busy = false;
@@ -667,6 +694,7 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
667
694
  const socket = await connectWebSocket(url, headers, signal);
668
695
  return {
669
696
  socket,
697
+ reused: false,
670
698
  release: () => {
671
699
  closeWebSocketSilently(socket);
672
700
  }
@@ -687,11 +715,12 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
687
715
  return {
688
716
  socket,
689
717
  entry,
718
+ reused: false,
690
719
  release: ({ keep } = {}) => {
691
720
  if (!keep || !isWebSocketReusable(entry.socket)) {
692
721
  closeWebSocketSilently(entry.socket);
693
722
  if (entry.idleTimer) clearTimeout(entry.idleTimer);
694
- deleteOwnedWebSocketSession(sessionId, entry);
723
+ if (websocketSessionCache.get(sessionId) === entry) websocketSessionCache.delete(sessionId);
695
724
  return;
696
725
  }
697
726
  entry.busy = false;
@@ -804,10 +833,9 @@ async function* parseWebSocket(socket, signal) {
804
833
  signal?.addEventListener("abort", onAbort);
805
834
  try {
806
835
  while (true) {
807
- if (signal?.aborted) throw transportAbortError(signal);
808
- const next = queue.shift();
809
- if (next !== void 0) {
810
- yield next;
836
+ if (signal?.aborted) throw new Error("Request was aborted");
837
+ if (queue.length > 0) {
838
+ yield queue.shift();
811
839
  continue;
812
840
  }
813
841
  if (done) break;
@@ -815,7 +843,7 @@ async function* parseWebSocket(socket, signal) {
815
843
  pending = resolve;
816
844
  });
817
845
  }
818
- if (failed) throw toErrorObject(failed, "Non-Error thrown");
846
+ if (failed) throw toLintErrorObject(failed, "Non-Error thrown");
819
847
  if (!sawCompletion) throw new Error("WebSocket stream closed before response.completed");
820
848
  } finally {
821
849
  socket.removeEventListener("message", onMessage);
@@ -871,13 +899,31 @@ async function* startWebSocketOutputOnFirstEvent(events, output, stream, onStart
871
899
  }
872
900
  }
873
901
  async function processWebSocketStream(url, body, headers, output, stream, model, onStart, options, abortFirstEventStream) {
874
- const { socket, entry, release } = await acquireWebSocket(url, headers, options?.sessionId, options?.signal);
902
+ const { socket, entry, reused, release } = await acquireWebSocket(url, headers, options?.sessionId, options?.signal);
875
903
  let keepConnection = true;
876
904
  const useCachedContext = options?.transport === "websocket-cached" || options?.transport === "auto";
877
905
  const fullBody = body;
878
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
+ }
879
925
  try {
880
- if (options?.signal?.aborted) throw transportAbortError(options.signal);
926
+ if (options?.signal?.aborted) throw new Error("Request was aborted");
881
927
  socket.send(JSON.stringify({
882
928
  type: "response.create",
883
929
  ...requestBody
@@ -887,7 +933,6 @@ async function processWebSocketStream(url, body, headers, output, stream, model,
887
933
  firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
888
934
  abortFirstEventStream,
889
935
  onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
890
- signal: options?.signal,
891
936
  resolveServiceTier: resolveCodexServiceTier,
892
937
  applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model)
893
938
  });
@@ -936,7 +981,7 @@ async function readChatGptResponsesErrorTextLimited(response) {
936
981
  break;
937
982
  }
938
983
  }
939
- if (!reachedLimit) text += decoder.decode();
984
+ text += decoder.decode();
940
985
  } finally {
941
986
  if (reachedLimit) await reader.cancel().catch(() => {});
942
987
  try {
@@ -945,14 +990,15 @@ async function readChatGptResponsesErrorTextLimited(response) {
945
990
  }
946
991
  return text;
947
992
  }
948
- function parseErrorResponseText(raw, status, statusText) {
949
- let message = raw || statusText || "Request failed";
993
+ async function parseErrorResponse(response) {
994
+ const raw = await readChatGptResponsesErrorTextLimited(response);
995
+ let message = raw || response.statusText || "Request failed";
950
996
  let friendlyMessage;
951
997
  try {
952
998
  const err = JSON.parse(raw)?.error;
953
999
  if (err) {
954
1000
  const code = err.code || err.type || "";
955
- if (/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(code) || status === 429) {
1001
+ if (/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(code) || response.status === 429) {
956
1002
  const plan = err.plan_type ? ` (${err.plan_type.toLowerCase()} plan)` : "";
957
1003
  const mins = err.resets_at ? Math.max(0, Math.round((err.resets_at * 1e3 - Date.now()) / 6e4)) : void 0;
958
1004
  friendlyMessage = `You have hit your ChatGPT usage limit${plan}.${mins !== void 0 ? ` Try again in ~${mins} min.` : ""}`.trim();
@@ -1011,5 +1057,12 @@ function buildWebSocketHeaders(initHeaders, additionalHeaders, accountId, token,
1011
1057
  headers.set("session_id", requestId);
1012
1058
  return headers;
1013
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
+ }
1014
1067
  //#endregion
1015
- export { closeOpenAICodexWebSocketSessions, extractOpenAICodexAccountId, parseSSEForTest, resetOpenAICodexWebSocketStateForTest, streamOpenAICodexResponses, streamSimpleOpenAICodexResponses };
1068
+ export { closeOpenAICodexWebSocketSessions, extractOpenAICodexAccountId, parseSSEForTest, resetOpenAICodexWebSocketDebugStats, streamOpenAICodexResponses, streamSimpleOpenAICodexResponses };