@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.
Files changed (91) hide show
  1. package/dist/{anthropic-CH4UUnZr.mjs → anthropic-B6dLpq5L.mjs} +55 -203
  2. package/dist/{src-QkygScBs.mjs → anthropic-JsNA5KCu.mjs} +0 -1
  3. package/dist/anthropic-compaction-replay-8lJNKXOE.mjs +840 -0
  4. package/dist/anthropic-payload-policy-CiEuQS72.d.mts +49 -0
  5. package/dist/{api-registry-DlMgPR39.d.mts → api-registry-k3zTz0cV.d.mts} +1 -1
  6. package/dist/{azure-openai-responses-CImcwB83.mjs → azure-openai-responses-mxIOtUnn.mjs} +23 -29
  7. package/dist/diagnostics.d.mts +24 -1
  8. package/dist/diagnostics.mjs +2 -1
  9. package/dist/{event-stream-YjaPW20U.d.mts → event-stream-BP6AWT8j.d.mts} +4 -1
  10. package/dist/{event-stream-D8n2uFee.mjs → event-stream-uSMZJ3FA.mjs} +26 -5
  11. package/dist/event-stream.d.mts +1 -1
  12. package/dist/event-stream.mjs +1 -1
  13. package/dist/{google-CtSg0iTS.mjs → google-C7h2QzDX.mjs} +5 -5
  14. package/dist/{google-shared-DNBz5rcD.mjs → google-shared-B0Qr9OR7.mjs} +146 -89
  15. package/dist/google-thinking-level-C-V3tecN.mjs +9 -0
  16. package/dist/{google-vertex-31f1uS9L.mjs → google-vertex-Bhc3TjDl.mjs} +11 -7
  17. package/dist/{llm-request-activity-BjtkplhG.mjs → headers-DdOQtGuU.mjs} +9 -1
  18. package/dist/host-DTqNc7ad.mjs +466 -0
  19. package/dist/{host-B9GUmcra.d.mts → host-vWgMMhiJ.d.mts} +9 -4
  20. package/dist/index.d.mts +6 -6
  21. package/dist/index.mjs +7 -5
  22. package/dist/internal/anthropic.d.mts +29 -5
  23. package/dist/internal/anthropic.mjs +5 -5
  24. package/dist/internal/openai-responses-payload-policy.d.mts +3 -0
  25. package/dist/internal/openai-responses-payload-policy.mjs +3 -0
  26. package/dist/internal/openai.d.mts +6 -6
  27. package/dist/internal/openai.mjs +8 -7
  28. package/dist/internal/runtime.d.mts +17 -5
  29. package/dist/internal/runtime.mjs +85 -73
  30. package/dist/internal/shared.d.mts +1 -6
  31. package/dist/internal/shared.mjs +3 -5
  32. package/dist/{json-parse-BvXNt1-7.mjs → json-parse-CDnesDM_.mjs} +4 -6
  33. package/dist/{mistral-CWmpvWYh.mjs → mistral-CEWoQI_g.mjs} +45 -48
  34. package/dist/number-coercion-H9qHik3g.mjs +71 -0
  35. package/dist/openai-chatgpt-jwt-KWcgd0d_.mjs +19 -0
  36. package/dist/{openai-chatgpt-responses-B84Ibtrd.mjs → openai-chatgpt-responses-CrPmqERt.mjs} +284 -240
  37. package/dist/{openai-completions-DsOxhOD1.mjs → openai-completions-BPnt4Sml.mjs} +65 -99
  38. package/dist/{openai-completions-compat-DBWjXoMZ.d.mts → openai-completions-compat-Dt3dcawL.d.mts} +2 -2
  39. package/dist/{openai-responses-BT7A3sLu.mjs → openai-responses-DhIKtOup.mjs} +18 -41
  40. package/dist/openai-responses-contracts-CyfIkQi5.mjs +253 -0
  41. package/dist/openai-responses-contracts-XpZJxrRG.d.mts +68 -0
  42. package/dist/openai-responses-payload-policy-BDxV-W0c.mjs +206 -0
  43. package/dist/openai-responses-payload-policy-BSs371VM.d.mts +40 -0
  44. package/dist/openai-responses-prompt-observer-internal-DgNTYnRY.mjs +46 -0
  45. package/dist/{openai-responses-stream-internal-Cw5txaGW.mjs → openai-responses-shared-DXIt3iY5.mjs} +1406 -940
  46. package/dist/{openai-reasoning-compat-YgeLncHw.mjs → openai-stop-reason-BkFkqqK0.mjs} +204 -37
  47. package/dist/openai-tool-projection-CY04OcvQ.mjs +338 -0
  48. package/dist/provider-error-BUwEnjXq.mjs +429 -0
  49. package/dist/provider-error-CzNw4BWX.d.mts +12 -0
  50. package/dist/{provider-options-D8bB3z9b.d.mts → provider-options-B96RdNpH.d.mts} +9 -3
  51. package/dist/provider-transcript-transform-ePx-Bbfr.mjs +155 -0
  52. package/dist/provider-types.d.mts +31 -0
  53. package/dist/provider-types.mjs +8 -0
  54. package/dist/providers.d.mts +1 -1
  55. package/dist/providers.mjs +17 -19
  56. package/dist/{reasoning-tag-text-partitioner-CGDyLWUR.mjs → reasoning-tag-text-partitioner-rnPwX2pg.mjs} +14 -8
  57. package/dist/record-coerce-DdXsgUd_.mjs +23 -0
  58. package/dist/sanitize-unicode-BYqrYtC_.mjs +90 -0
  59. package/dist/session-resources-CkR4WWy1.mjs +21 -0
  60. package/dist/simple-options-D58D5Kvw.mjs +117 -0
  61. package/dist/src-D2H6yKkH.mjs +2 -0
  62. package/dist/{stream-first-event-timeout-BBys9hSb.mjs → stream-first-event-timeout-MK28puvq.mjs} +2 -2
  63. package/dist/string-coerce-fsri9iCu.mjs +34 -0
  64. package/dist/{tool-schema-json-projection-BwNu3nDi.mjs → tool-schema-json-projection-q5d7QX5c.mjs} +32 -7
  65. package/dist/transport-utils-DJqkxbhC.mjs +138 -0
  66. package/dist/transports.d.mts +166 -241
  67. package/dist/transports.mjs +1979 -1825
  68. package/dist/types-BDdaOVi2.mjs +6 -0
  69. package/dist/{types-bzp5k29J.d.mts → types-BHNrPS1l.d.mts} +23 -1
  70. package/dist/types.d.mts +4 -4
  71. package/dist/types.mjs +6 -4
  72. package/dist/utf16-slice-CvGodqok.mjs +29 -0
  73. package/dist/{validation-DAa_yFOM.mjs → validation-B61OhAio.mjs} +6 -6
  74. package/dist/{validation-B-j7cOYp.d.mts → validation-DT9SrFn3.d.mts} +1 -1
  75. package/dist/validation.d.mts +1 -1
  76. package/dist/validation.mjs +1 -1
  77. package/package.json +15 -1
  78. package/dist/anthropic-usage-DWU-x8MI.mjs +0 -459
  79. package/dist/error-coercion-DgxlWC0n.mjs +0 -15
  80. package/dist/headers-B_e4-1J0.mjs +0 -9
  81. package/dist/host-Dog2WQiR.mjs +0 -369
  82. package/dist/number-coercion-DvG7SNMg.mjs +0 -129
  83. package/dist/openai-chatgpt-jwt-DhAAzLkj.mjs +0 -39
  84. package/dist/openai-responses-shared-pXl6Wd8S.mjs +0 -392
  85. package/dist/openai-tool-projection-OhX64DoP.mjs +0 -215
  86. package/dist/provider-error-CAEvRjry.mjs +0 -47
  87. package/dist/sanitize-unicode-DT5o51ur.mjs +0 -26
  88. package/dist/simple-options-9lhRrN73.mjs +0 -50
  89. package/dist/tool-result-text-CTpIRbYd.mjs +0 -225
  90. package/dist/transform-messages-C8mBqZxF.mjs +0 -2
  91. package/dist/transport-stream-shared-D81p90xq.mjs +0 -297
@@ -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-D8n2uFee.mjs";
4
- import { n as getAiTransportHost, r as resolveAiTransportHeaderSentinels } from "./host-Dog2WQiR.mjs";
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 { 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";
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 { 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";
13
- 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";
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
- let body = buildRequestBody(model, context, options);
150
- const nextBody = await options?.onPayload?.(body, model);
151
- if (nextBody !== void 0) body = nextBody;
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), body, websocketHeaders, output, stream, model, () => {
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(body)).byteLength
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
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
211
- if (activeSignal?.aborted) throw transportAbortError(activeSignal);
212
- let attemptResponse;
213
- let errorText;
214
- try {
215
- attemptResponse = await fetch(resolveCodexUrl(model.baseUrl), {
216
- method: "POST",
217
- headers: sseHeaders,
218
- body: sseBody,
219
- signal: activeSignal
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
- errorText = await readChatGptResponsesErrorTextLimited(attemptResponse);
228
- } catch (error) {
229
- if (error instanceof Error) {
230
- if (isRequestTimeoutError(error, options?.signal, requestTimeoutSignal, requestTimeoutMs) && requestTimeoutMs !== void 0) throw formatRequestTimeoutError(requestTimeoutMs, error);
231
- if (error.name === "AbortError" || error.message === "Request was aborted") throw new Error("Request was aborted", { cause: error });
232
- if (error.name === "TimeoutError" && requestTimeoutMs !== void 0) throw new Error(`Request timed out after ${requestTimeoutMs}ms`, { cause: error });
233
- }
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);
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
- throw lastError;
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 (attempt < maxRetries && isRetryableError(attemptResponse.status, errorText)) {
243
- await sleepWithAbort(resolveHttpRetryDelayMs(attemptResponse, attempt), activeSignal);
244
- continue;
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
- const info = parseErrorResponseText(errorText, attemptResponse.status, attemptResponse.statusText);
247
- throw new Error(info.friendlyMessage || info.message);
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
- if (!response?.ok) throw lastError ?? /* @__PURE__ */ new Error("Failed after retries");
250
- if (!response.body) throw new Error("No response body");
251
- stream.push({
252
- type: "start",
253
- partial: output
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
- output.stopReason = options?.signal?.aborted ? "aborted" : "error";
267
- output.errorMessage = normalizedError instanceof Error ? normalizedError.message : String(normalizedError);
369
+ const terminal = projectProviderError(normalizedError, options?.signal);
370
+ Object.assign(output, terminal);
268
371
  stream.push({
269
372
  type: "error",
270
- reason: output.stopReason,
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 base = buildBaseOptions(model, options, apiKey);
284
- return streamOpenAICodexResponses(model, context, {
285
- ...base,
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.projection.inputToolCount > 0 || converted.projection.diagnostics.length > 0) {
416
+ if (converted.tools.length > 0) {
311
417
  body.tools = converted.tools;
312
- if (body.tools.length === 0) {
313
- delete body.tools;
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
- websocketSessionCache.delete(sessionId);
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
- websocketSessionCache.delete(sessionId);
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
- websocketSessionCache.set(sessionId, entry);
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
- (async () => {
758
- let text = null;
759
- try {
760
- if (!event || typeof event !== "object" || !("data" in event)) return;
761
- text = await decodeWebSocketData(event.data);
762
- if (!text) return;
763
- const parsed = JSON.parse(text);
764
- const type = typeof parsed.type === "string" ? parsed.type : "";
765
- if (type === "response.completed" || type === "response.done" || type === "response.incomplete") {
766
- sawCompletion = true;
767
- done = true;
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) => applyServiceTierPricing(usage, serviceTier, model)
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) break;
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
- if (reachedLimit) await reader.cancel().catch(() => {});
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
- const code = err.code || err.type || "";
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, parseSSEForTest, resetOpenAICodexWebSocketStateForTest, streamOpenAICodexResponses, streamSimpleOpenAICodexResponses };
1059
+ export { closeOpenAICodexWebSocketSessions, extractOpenAICodexAccountId, resetOpenAICodexWebSocketStateForTest, streamOpenAICodexResponses, streamSimpleOpenAICodexResponses };