codex-subscription-oauth-plugin 0.1.1

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.
@@ -0,0 +1,2827 @@
1
+ import { n as __commonJSMin, t as getProviderEnvValue } from "./provider-env-DRzE1dvc.js";
2
+ import { a as appendAssistantMessageDiagnostic, l as AssistantMessageEventStream, n as clampThinkingLevel, o as createAssistantMessageDiagnostic, s as formatThrownValue, t as calculateCost } from "./models-DaqoXO5b.js";
3
+ //#region node_modules/@earendil-works/pi-ai/dist/session-resources.js
4
+ const sessionResourceCleanups = /* @__PURE__ */ new Set();
5
+ function registerSessionResourceCleanup(cleanup) {
6
+ sessionResourceCleanups.add(cleanup);
7
+ return () => {
8
+ sessionResourceCleanups.delete(cleanup);
9
+ };
10
+ }
11
+ //#endregion
12
+ //#region node_modules/partial-json/dist/options.js
13
+ var require_options = /* @__PURE__ */ __commonJSMin(((exports) => {
14
+ /**
15
+ * Sometimes you don't allow every type to be partially parsed.
16
+ * For example, you may not want a partial number because it may increase its size gradually before it's complete.
17
+ * In this case, you can use the `Allow` object to control what types you allow to be partially parsed.
18
+ * @module
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.Allow = exports.ALL = exports.COLLECTION = exports.ATOM = exports.SPECIAL = exports.INF = exports._INFINITY = exports.INFINITY = exports.NAN = exports.BOOL = exports.NULL = exports.OBJ = exports.ARR = exports.NUM = exports.STR = void 0;
22
+ /**
23
+ * allow partial strings like `"hello \u12` to be parsed as `"hello "`
24
+ */
25
+ exports.STR = 1;
26
+ /**
27
+ * allow partial numbers like `123.` to be parsed as `123`
28
+ */
29
+ exports.NUM = 2;
30
+ /**
31
+ * allow partial arrays like `[1, 2,` to be parsed as `[1, 2]`
32
+ */
33
+ exports.ARR = 4;
34
+ /**
35
+ * allow partial objects like `{"a": 1, "b":` to be parsed as `{"a": 1}`
36
+ */
37
+ exports.OBJ = 8;
38
+ /**
39
+ * allow `nu` to be parsed as `null`
40
+ */
41
+ exports.NULL = 16;
42
+ /**
43
+ * allow `tr` to be parsed as `true`, and `fa` to be parsed as `false`
44
+ */
45
+ exports.BOOL = 32;
46
+ /**
47
+ * allow `Na` to be parsed as `NaN`
48
+ */
49
+ exports.NAN = 64;
50
+ /**
51
+ * allow `Inf` to be parsed as `Infinity`
52
+ */
53
+ exports.INFINITY = 128;
54
+ /**
55
+ * allow `-Inf` to be parsed as `-Infinity`
56
+ */
57
+ exports._INFINITY = 256;
58
+ exports.INF = exports.INFINITY | exports._INFINITY;
59
+ exports.SPECIAL = exports.NULL | exports.BOOL | exports.INF | exports.NAN;
60
+ exports.ATOM = exports.STR | exports.NUM | exports.SPECIAL;
61
+ exports.COLLECTION = exports.ARR | exports.OBJ;
62
+ exports.ALL = exports.ATOM | exports.COLLECTION;
63
+ /**
64
+ * Control what types you allow to be partially parsed.
65
+ * The default is to allow all types to be partially parsed, which in most casees is the best option.
66
+ * @example
67
+ * If you don't want to allow partial objects, you can use the following code:
68
+ * ```ts
69
+ * import { Allow, parse } from "partial-json";
70
+ * parse(`[{"a": 1, "b": 2}, {"a": 3,`, Allow.ARR); // [ { a: 1, b: 2 } ]
71
+ * ```
72
+ * Or you can use `~` to disallow a type:
73
+ * ```ts
74
+ * parse(`[{"a": 1, "b": 2}, {"a": 3,`, ~Allow.OBJ); // [ { a: 1, b: 2 } ]
75
+ * ```
76
+ * @example
77
+ * If you don't want to allow partial strings, you can use the following code:
78
+ * ```ts
79
+ * import { Allow, parse } from "partial-json";
80
+ * parse(`["complete string", "incompl`, ~Allow.STR); // [ 'complete string' ]
81
+ * ```
82
+ */
83
+ exports.Allow = {
84
+ STR: exports.STR,
85
+ NUM: exports.NUM,
86
+ ARR: exports.ARR,
87
+ OBJ: exports.OBJ,
88
+ NULL: exports.NULL,
89
+ BOOL: exports.BOOL,
90
+ NAN: exports.NAN,
91
+ INFINITY: exports.INFINITY,
92
+ _INFINITY: exports._INFINITY,
93
+ INF: exports.INF,
94
+ SPECIAL: exports.SPECIAL,
95
+ ATOM: exports.ATOM,
96
+ COLLECTION: exports.COLLECTION,
97
+ ALL: exports.ALL
98
+ };
99
+ exports.default = exports.Allow;
100
+ }));
101
+ //#endregion
102
+ //#region node_modules/@earendil-works/pi-ai/dist/utils/json-parse.js
103
+ var import_dist = (/* @__PURE__ */ __commonJSMin(((exports) => {
104
+ var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
105
+ if (k2 === void 0) k2 = k;
106
+ var desc = Object.getOwnPropertyDescriptor(m, k);
107
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) desc = {
108
+ enumerable: true,
109
+ get: function() {
110
+ return m[k];
111
+ }
112
+ };
113
+ Object.defineProperty(o, k2, desc);
114
+ }) : (function(o, m, k, k2) {
115
+ if (k2 === void 0) k2 = k;
116
+ o[k2] = m[k];
117
+ }));
118
+ var __exportStar = exports && exports.__exportStar || function(m, exports$1) {
119
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports$1, p)) __createBinding(exports$1, m, p);
120
+ };
121
+ Object.defineProperty(exports, "__esModule", { value: true });
122
+ exports.Allow = exports.MalformedJSON = exports.PartialJSON = exports.parseJSON = exports.parse = void 0;
123
+ const options_1 = require_options();
124
+ Object.defineProperty(exports, "Allow", {
125
+ enumerable: true,
126
+ get: function() {
127
+ return options_1.Allow;
128
+ }
129
+ });
130
+ __exportStar(require_options(), exports);
131
+ var PartialJSON = class extends Error {};
132
+ exports.PartialJSON = PartialJSON;
133
+ var MalformedJSON = class extends Error {};
134
+ exports.MalformedJSON = MalformedJSON;
135
+ /**
136
+ * Parse incomplete JSON
137
+ * @param {string} jsonString Partial JSON to be parsed
138
+ * @param {number} allowPartial Specify what types are allowed to be partial, see {@link Allow} for details
139
+ * @returns The parsed JSON
140
+ * @throws {PartialJSON} If the JSON is incomplete (related to the `allow` parameter)
141
+ * @throws {MalformedJSON} If the JSON is malformed
142
+ */
143
+ function parseJSON(jsonString, allowPartial = options_1.Allow.ALL) {
144
+ if (typeof jsonString !== "string") throw new TypeError(`expecting str, got ${typeof jsonString}`);
145
+ if (!jsonString.trim()) throw new Error(`${jsonString} is empty`);
146
+ return _parseJSON(jsonString.trim(), allowPartial);
147
+ }
148
+ exports.parseJSON = parseJSON;
149
+ const _parseJSON = (jsonString, allow) => {
150
+ const length = jsonString.length;
151
+ let index = 0;
152
+ const markPartialJSON = (msg) => {
153
+ throw new PartialJSON(`${msg} at position ${index}`);
154
+ };
155
+ const throwMalformedError = (msg) => {
156
+ throw new MalformedJSON(`${msg} at position ${index}`);
157
+ };
158
+ const parseAny = () => {
159
+ skipBlank();
160
+ if (index >= length) markPartialJSON("Unexpected end of input");
161
+ if (jsonString[index] === "\"") return parseStr();
162
+ if (jsonString[index] === "{") return parseObj();
163
+ if (jsonString[index] === "[") return parseArr();
164
+ if (jsonString.substring(index, index + 4) === "null" || options_1.Allow.NULL & allow && length - index < 4 && "null".startsWith(jsonString.substring(index))) {
165
+ index += 4;
166
+ return null;
167
+ }
168
+ if (jsonString.substring(index, index + 4) === "true" || options_1.Allow.BOOL & allow && length - index < 4 && "true".startsWith(jsonString.substring(index))) {
169
+ index += 4;
170
+ return true;
171
+ }
172
+ if (jsonString.substring(index, index + 5) === "false" || options_1.Allow.BOOL & allow && length - index < 5 && "false".startsWith(jsonString.substring(index))) {
173
+ index += 5;
174
+ return false;
175
+ }
176
+ if (jsonString.substring(index, index + 8) === "Infinity" || options_1.Allow.INFINITY & allow && length - index < 8 && "Infinity".startsWith(jsonString.substring(index))) {
177
+ index += 8;
178
+ return Infinity;
179
+ }
180
+ if (jsonString.substring(index, index + 9) === "-Infinity" || options_1.Allow._INFINITY & allow && 1 < length - index && length - index < 9 && "-Infinity".startsWith(jsonString.substring(index))) {
181
+ index += 9;
182
+ return -Infinity;
183
+ }
184
+ if (jsonString.substring(index, index + 3) === "NaN" || options_1.Allow.NAN & allow && length - index < 3 && "NaN".startsWith(jsonString.substring(index))) {
185
+ index += 3;
186
+ return NaN;
187
+ }
188
+ return parseNum();
189
+ };
190
+ const parseStr = () => {
191
+ const start = index;
192
+ let escape = false;
193
+ index++;
194
+ while (index < length && (jsonString[index] !== "\"" || escape && jsonString[index - 1] === "\\")) {
195
+ escape = jsonString[index] === "\\" ? !escape : false;
196
+ index++;
197
+ }
198
+ if (jsonString.charAt(index) == "\"") try {
199
+ return JSON.parse(jsonString.substring(start, ++index - Number(escape)));
200
+ } catch (e) {
201
+ throwMalformedError(String(e));
202
+ }
203
+ else if (options_1.Allow.STR & allow) try {
204
+ return JSON.parse(jsonString.substring(start, index - Number(escape)) + "\"");
205
+ } catch (e) {
206
+ return JSON.parse(jsonString.substring(start, jsonString.lastIndexOf("\\")) + "\"");
207
+ }
208
+ markPartialJSON("Unterminated string literal");
209
+ };
210
+ const parseObj = () => {
211
+ index++;
212
+ skipBlank();
213
+ const obj = {};
214
+ try {
215
+ while (jsonString[index] !== "}") {
216
+ skipBlank();
217
+ if (index >= length && options_1.Allow.OBJ & allow) return obj;
218
+ const key = parseStr();
219
+ skipBlank();
220
+ index++;
221
+ try {
222
+ obj[key] = parseAny();
223
+ } catch (e) {
224
+ if (options_1.Allow.OBJ & allow) return obj;
225
+ else throw e;
226
+ }
227
+ skipBlank();
228
+ if (jsonString[index] === ",") index++;
229
+ }
230
+ } catch (e) {
231
+ if (options_1.Allow.OBJ & allow) return obj;
232
+ else markPartialJSON("Expected '}' at end of object");
233
+ }
234
+ index++;
235
+ return obj;
236
+ };
237
+ const parseArr = () => {
238
+ index++;
239
+ const arr = [];
240
+ try {
241
+ while (jsonString[index] !== "]") {
242
+ arr.push(parseAny());
243
+ skipBlank();
244
+ if (jsonString[index] === ",") index++;
245
+ }
246
+ } catch (e) {
247
+ if (options_1.Allow.ARR & allow) return arr;
248
+ markPartialJSON("Expected ']' at end of array");
249
+ }
250
+ index++;
251
+ return arr;
252
+ };
253
+ const parseNum = () => {
254
+ if (index === 0) {
255
+ if (jsonString === "-") throwMalformedError("Not sure what '-' is");
256
+ try {
257
+ return JSON.parse(jsonString);
258
+ } catch (e) {
259
+ if (options_1.Allow.NUM & allow) try {
260
+ return JSON.parse(jsonString.substring(0, jsonString.lastIndexOf("e")));
261
+ } catch (e) {}
262
+ throwMalformedError(String(e));
263
+ }
264
+ }
265
+ const start = index;
266
+ if (jsonString[index] === "-") index++;
267
+ while (jsonString[index] && ",]}".indexOf(jsonString[index]) === -1) index++;
268
+ if (index == length && !(options_1.Allow.NUM & allow)) markPartialJSON("Unterminated number literal");
269
+ try {
270
+ return JSON.parse(jsonString.substring(start, index));
271
+ } catch (e) {
272
+ if (jsonString.substring(start, index) === "-") markPartialJSON("Not sure what '-' is");
273
+ try {
274
+ return JSON.parse(jsonString.substring(start, jsonString.lastIndexOf("e")));
275
+ } catch (e) {
276
+ throwMalformedError(String(e));
277
+ }
278
+ }
279
+ };
280
+ const skipBlank = () => {
281
+ while (index < length && " \n\r ".includes(jsonString[index])) index++;
282
+ };
283
+ return parseAny();
284
+ };
285
+ exports.parse = parseJSON;
286
+ })))();
287
+ const VALID_JSON_ESCAPES = /* @__PURE__ */ new Set([
288
+ "\"",
289
+ "\\",
290
+ "/",
291
+ "b",
292
+ "f",
293
+ "n",
294
+ "r",
295
+ "t",
296
+ "u"
297
+ ]);
298
+ function isControlCharacter(char) {
299
+ const codePoint = char.codePointAt(0);
300
+ return codePoint !== void 0 && codePoint >= 0 && codePoint <= 31;
301
+ }
302
+ function escapeControlCharacter(char) {
303
+ switch (char) {
304
+ case "\b": return "\\b";
305
+ case "\f": return "\\f";
306
+ case "\n": return "\\n";
307
+ case "\r": return "\\r";
308
+ case " ": return "\\t";
309
+ default: return `\\u${char.codePointAt(0)?.toString(16).padStart(4, "0") ?? "0000"}`;
310
+ }
311
+ }
312
+ /**
313
+ * Repairs malformed JSON string literals by:
314
+ * - escaping raw control characters inside strings
315
+ * - doubling backslashes before invalid escape characters
316
+ */
317
+ function repairJson(json) {
318
+ let repaired = "";
319
+ let inString = false;
320
+ for (let index = 0; index < json.length; index++) {
321
+ const char = json[index];
322
+ if (!inString) {
323
+ repaired += char;
324
+ if (char === "\"") inString = true;
325
+ continue;
326
+ }
327
+ if (char === "\"") {
328
+ repaired += char;
329
+ inString = false;
330
+ continue;
331
+ }
332
+ if (char === "\\") {
333
+ const nextChar = json[index + 1];
334
+ if (nextChar === void 0) {
335
+ repaired += "\\\\";
336
+ continue;
337
+ }
338
+ if (nextChar === "u") {
339
+ const unicodeDigits = json.slice(index + 2, index + 6);
340
+ if (/^[0-9a-fA-F]{4}$/.test(unicodeDigits)) {
341
+ repaired += `\\u${unicodeDigits}`;
342
+ index += 5;
343
+ continue;
344
+ }
345
+ }
346
+ if (VALID_JSON_ESCAPES.has(nextChar)) {
347
+ repaired += `\\${nextChar}`;
348
+ index += 1;
349
+ continue;
350
+ }
351
+ repaired += "\\\\";
352
+ continue;
353
+ }
354
+ repaired += isControlCharacter(char) ? escapeControlCharacter(char) : char;
355
+ }
356
+ return repaired;
357
+ }
358
+ function parseJsonWithRepair(json) {
359
+ try {
360
+ return JSON.parse(json);
361
+ } catch (error) {
362
+ const repairedJson = repairJson(json);
363
+ if (repairedJson !== json) return JSON.parse(repairedJson);
364
+ throw error;
365
+ }
366
+ }
367
+ /**
368
+ * Attempts to parse potentially incomplete JSON during streaming.
369
+ * Always returns a valid object, even if the JSON is incomplete.
370
+ *
371
+ * @param partialJson The partial JSON string from streaming
372
+ * @returns Parsed object or empty object if parsing fails
373
+ */
374
+ function parseStreamingJson(partialJson) {
375
+ if (!partialJson || partialJson.trim() === "") return {};
376
+ try {
377
+ return parseJsonWithRepair(partialJson);
378
+ } catch {
379
+ try {
380
+ return (0, import_dist.parse)(partialJson) ?? {};
381
+ } catch {
382
+ try {
383
+ return (0, import_dist.parse)(repairJson(partialJson)) ?? {};
384
+ } catch {
385
+ return {};
386
+ }
387
+ }
388
+ }
389
+ }
390
+ //#endregion
391
+ //#region node_modules/@earendil-works/pi-ai/dist/utils/uuid.js
392
+ const MAX_UUID_V7_TIMESTAMP = 0xffffffffffff;
393
+ const MAX_SEQUENCE = (1n << 41n) - 1n;
394
+ let lastOrdinaryTimestamp = -1;
395
+ let sequence;
396
+ /** Generate a time-ordered UUIDv7. A supplied timestamp is preserved for follower ids. */
397
+ function uuidv7(timestampMs) {
398
+ const requestedTimestamp = timestampMs ?? Date.now();
399
+ if (!Number.isInteger(requestedTimestamp) || requestedTimestamp < 0 || requestedTimestamp > MAX_UUID_V7_TIMESTAMP) throw new RangeError(`UUIDv7 timestamp must be an integer between 0 and ${MAX_UUID_V7_TIMESTAMP}`);
400
+ const effectiveTimestamp = timestampMs === void 0 ? Math.max(requestedTimestamp, lastOrdinaryTimestamp) : timestampMs;
401
+ if (timestampMs === void 0) lastOrdinaryTimestamp = effectiveTimestamp;
402
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
403
+ globalThis.crypto.getRandomValues(bytes);
404
+ if (sequence === void 0) sequence = BigInt(bytes[1]) << 32n | BigInt(bytes[2]) << 24n | BigInt(bytes[3]) << 16n | BigInt(bytes[4]) << 8n | BigInt(bytes[5]);
405
+ else {
406
+ if (sequence === MAX_SEQUENCE) throw new RangeError("UUIDv7 generator sequence exhausted");
407
+ sequence++;
408
+ }
409
+ const timestamp = BigInt(effectiveTimestamp);
410
+ for (let index = 5; index >= 0; index--) bytes[index] = Number(timestamp >> BigInt((5 - index) * 8)) & 255;
411
+ bytes[6] = 112 | Number(sequence >> 37n & 15n);
412
+ bytes[7] = Number(sequence >> 29n & 255n);
413
+ bytes[8] = 128 | Number(sequence >> 23n & 63n);
414
+ bytes[9] = Number(sequence >> 15n & 255n);
415
+ bytes[10] = Number(sequence >> 7n & 255n);
416
+ bytes[11] = Number((sequence & 127n) << 1n) | bytes[11] & 1;
417
+ const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0"));
418
+ return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
419
+ }
420
+ //#endregion
421
+ //#region node_modules/@earendil-works/pi-ai/dist/utils/abort-signals.js
422
+ function combineAbortSignals(signals) {
423
+ const activeSignals = signals.filter((signal) => signal !== void 0);
424
+ if (activeSignals.length === 0) return { cleanup: () => {} };
425
+ if (activeSignals.length === 1) return {
426
+ signal: activeSignals[0],
427
+ cleanup: () => {}
428
+ };
429
+ const controller = new AbortController();
430
+ const listeners = [];
431
+ const abort = (signal) => {
432
+ if (!controller.signal.aborted) controller.abort(signal.reason);
433
+ };
434
+ for (const signal of activeSignals) {
435
+ if (signal.aborted) {
436
+ abort(signal);
437
+ break;
438
+ }
439
+ const listener = () => abort(signal);
440
+ signal.addEventListener("abort", listener, { once: true });
441
+ listeners.push({
442
+ signal,
443
+ listener
444
+ });
445
+ }
446
+ return {
447
+ signal: controller.signal,
448
+ cleanup: () => {
449
+ for (const { signal, listener } of listeners) signal.removeEventListener("abort", listener);
450
+ }
451
+ };
452
+ }
453
+ //#endregion
454
+ //#region node_modules/@earendil-works/pi-ai/dist/utils/deferred-tools.js
455
+ const identityToolName = (name) => name;
456
+ /** Split current tools into prefix and transcript-loaded definitions. */
457
+ function splitDeferredTools(context, enabled, normalizeName = identityToolName) {
458
+ const uniqueTools = /* @__PURE__ */ new Map();
459
+ for (const tool of context.tools ?? []) uniqueTools.set(normalizeName(tool.name), tool);
460
+ if (!enabled) return {
461
+ immediate: [...uniqueTools.values()],
462
+ deferred: /* @__PURE__ */ new Map()
463
+ };
464
+ const deferredNames = /* @__PURE__ */ new Set();
465
+ const usedNames = /* @__PURE__ */ new Set();
466
+ for (const message of context.messages) if (message.role === "assistant") {
467
+ for (const block of message.content) if (block.type === "toolCall") usedNames.add(normalizeName(block.name));
468
+ } else if (message.role === "toolResult") for (const name of message.addedToolNames ?? []) {
469
+ const normalizedName = normalizeName(name);
470
+ if (!usedNames.has(normalizedName)) deferredNames.add(normalizedName);
471
+ }
472
+ const immediate = [];
473
+ const deferred = /* @__PURE__ */ new Map();
474
+ for (const [name, tool] of uniqueTools) if (deferredNames.has(name)) deferred.set(name, tool);
475
+ else immediate.push(tool);
476
+ return {
477
+ immediate,
478
+ deferred
479
+ };
480
+ }
481
+ //#endregion
482
+ //#region node_modules/@earendil-works/pi-ai/dist/utils/error-body.js
483
+ const MAX_PROVIDER_ERROR_BODY_CHARS = 4e3;
484
+ function normalizeProviderError(error) {
485
+ if (!(error instanceof Error)) return {
486
+ message: safeJsonStringify$1(error),
487
+ messageCarriesBody: false
488
+ };
489
+ const sdkError = error;
490
+ const status = extractStatus(sdkError);
491
+ const body = extractBody(sdkError);
492
+ const messageCarriesBody = body === void 0 || error.message.includes(body);
493
+ return {
494
+ status,
495
+ body,
496
+ message: error.message,
497
+ messageCarriesBody
498
+ };
499
+ }
500
+ /**
501
+ * Probe the HTTP status, first numeric hit wins, in SDK-field order:
502
+ * `statusCode` (Mistral) → `status` (`openai`, `@google/genai`) →
503
+ * `$metadata.httpStatusCode` (Bedrock) → `$response.statusCode` (Bedrock).
504
+ */
505
+ function extractStatus(error) {
506
+ if (typeof error.statusCode === "number") return error.statusCode;
507
+ if (typeof error.status === "number") return error.status;
508
+ if (typeof error.$metadata?.httpStatusCode === "number") return error.$metadata.httpStatusCode;
509
+ if (typeof error.$response?.statusCode === "number") return error.$response.statusCode;
510
+ }
511
+ /**
512
+ * Probe the raw body reason, first usable hit wins, in SDK-field order:
513
+ * `body` string (Mistral) → `error` parsed JSON body object (`openai` SDK's
514
+ * `this.error`) → `$response.body` (Bedrock). Empty objects and unread response
515
+ * streams are treated as no body so they do not surface as `"{}"` or serialized
516
+ * stream internals. The chosen body is truncated to the cap.
517
+ */
518
+ function extractBody(error) {
519
+ const bodyText = pickBodyText(error);
520
+ if (bodyText === void 0) return void 0;
521
+ const trimmed = bodyText.trim();
522
+ if (trimmed.length === 0) return void 0;
523
+ return truncateErrorText(trimmed, MAX_PROVIDER_ERROR_BODY_CHARS);
524
+ }
525
+ function pickBodyText(error) {
526
+ if (typeof error.body === "string") return error.body;
527
+ if (isPlainNonEmptyObject(error.error)) return safeJsonStringify$1(error.error);
528
+ const responseBody = error.$response?.body;
529
+ if (typeof responseBody === "string") return responseBody;
530
+ if (isReadableStreamLike(responseBody)) return void 0;
531
+ if (isPlainNonEmptyObject(responseBody)) return safeJsonStringify$1(responseBody);
532
+ }
533
+ function isReadableStreamLike(value) {
534
+ return typeof value === "object" && value !== null && "pipe" in value && typeof value.pipe === "function";
535
+ }
536
+ /**
537
+ * Only a PLAIN object counts as an HTTP body. SDK error fields can hold class
538
+ * instances instead of parsed bodies — AWS SDK v3's `$response.body` is an
539
+ * HTTP stream/response wrapper object, and stringifying one produced garbage
540
+ * like `{"_events":...}` as the "body", which then REPLACED `error.message`
541
+ * in the composed display string. `error.message` is where the SDK puts the
542
+ * real deserialized exception text ("Input is too long...", schema validation
543
+ * details, ...), so the one useful string was discarded for noise. A class
544
+ * instance yields no body, `messageCarriesBody` stays true, and the real
545
+ * message survives. Complements the `pipe` sniffing above: web
546
+ * ReadableStreams (pipeTo/pipeThrough, no `pipe`) and non-stream SDK wrapper
547
+ * classes fail the prototype check, while parsed JSON bodies (plain objects
548
+ * by construction) still pass.
549
+ */
550
+ function isPlainNonEmptyObject(value) {
551
+ if (typeof value !== "object" || value === null) return false;
552
+ const proto = Object.getPrototypeOf(value);
553
+ if (proto !== Object.prototype && proto !== null) return false;
554
+ return Object.keys(value).length > 0;
555
+ }
556
+ /**
557
+ * Compose a display string from a normalized error. When the message already
558
+ * carries the body (Anthropic / `@google/genai` happy path) or no body/status
559
+ * was extracted, the message is returned unchanged. Otherwise the status and
560
+ * body are surfaced, with an optional provider prefix.
561
+ *
562
+ * - no prefix: `"<status>: <body>"`
563
+ * - prefix: `"<prefix> (<status>): <body>"`
564
+ */
565
+ function formatProviderError(norm, prefix) {
566
+ if (norm.messageCarriesBody || norm.status === void 0 || norm.body === void 0) return prefix !== void 0 && norm.status !== void 0 ? `${prefix} (${norm.status}): ${norm.message}` : norm.message;
567
+ return prefix !== void 0 ? `${prefix} (${norm.status}): ${norm.body}` : `${norm.status}: ${norm.body}`;
568
+ }
569
+ function truncateErrorText(text, maxChars) {
570
+ if (text.length <= maxChars) return text;
571
+ return `${text.slice(0, maxChars)}... [truncated ${text.length - maxChars} chars]`;
572
+ }
573
+ function safeJsonStringify$1(value) {
574
+ try {
575
+ const serialized = JSON.stringify(value);
576
+ return serialized === void 0 ? String(value) : serialized;
577
+ } catch {
578
+ return String(value);
579
+ }
580
+ }
581
+ //#endregion
582
+ //#region node_modules/@earendil-works/pi-ai/dist/utils/headers.js
583
+ function headersToRecord(headers) {
584
+ const result = {};
585
+ for (const [key, value] of headers.entries()) result[key] = value;
586
+ return result;
587
+ }
588
+ //#endregion
589
+ //#region node_modules/@earendil-works/pi-ai/dist/utils/node-http-proxy.js
590
+ const DEFAULT_PROXY_PORTS = {
591
+ ftp: 21,
592
+ gopher: 70,
593
+ http: 80,
594
+ https: 443,
595
+ ws: 80,
596
+ wss: 443
597
+ };
598
+ function getProxyEnv(key, env) {
599
+ const lowercaseKey = key.toLowerCase();
600
+ const uppercaseKey = key.toUpperCase();
601
+ return env?.[lowercaseKey] || env?.[uppercaseKey] || getProviderEnvValue(lowercaseKey) || getProviderEnvValue(uppercaseKey) || "";
602
+ }
603
+ function parseProxyTargetUrl(targetUrl) {
604
+ if (targetUrl instanceof URL) return targetUrl;
605
+ try {
606
+ return new URL(targetUrl);
607
+ } catch {
608
+ return;
609
+ }
610
+ }
611
+ function stripBrackets(host) {
612
+ return host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
613
+ }
614
+ function parseNoProxyEntry(entry) {
615
+ const trimmed = entry.trim().toLowerCase();
616
+ if (!trimmed) return void 0;
617
+ if (trimmed.startsWith("[")) {
618
+ const closingBracket = trimmed.indexOf("]");
619
+ if (closingBracket !== -1) {
620
+ const host = trimmed.slice(1, closingBracket);
621
+ const rest = trimmed.slice(closingBracket + 1);
622
+ if (rest.startsWith(":")) {
623
+ const port = Number.parseInt(rest.slice(1), 10);
624
+ return {
625
+ host,
626
+ port: Number.isNaN(port) ? 0 : port
627
+ };
628
+ }
629
+ return {
630
+ host,
631
+ port: 0
632
+ };
633
+ }
634
+ }
635
+ if (trimmed.includes(":") && trimmed.split(":").length > 2) return {
636
+ host: trimmed,
637
+ port: 0
638
+ };
639
+ const colonIndex = trimmed.lastIndexOf(":");
640
+ if (colonIndex !== -1 && colonIndex === trimmed.indexOf(":")) {
641
+ const host = trimmed.slice(0, colonIndex);
642
+ const port = Number.parseInt(trimmed.slice(colonIndex + 1), 10);
643
+ if (!Number.isNaN(port)) return {
644
+ host,
645
+ port
646
+ };
647
+ }
648
+ return {
649
+ host: trimmed,
650
+ port: 0
651
+ };
652
+ }
653
+ function shouldProxyHostname(hostname, port, env) {
654
+ const noProxy = getProxyEnv("no_proxy", env).toLowerCase();
655
+ if (!noProxy) return true;
656
+ if (noProxy === "*") return false;
657
+ const normalizedTargetHost = stripBrackets(hostname.toLowerCase());
658
+ return noProxy.split(/[,\s]/).every((entry) => {
659
+ const parsed = parseNoProxyEntry(entry);
660
+ if (!parsed) return true;
661
+ if (parsed.port && parsed.port !== port) return true;
662
+ let domain = stripBrackets(parsed.host);
663
+ if (domain.startsWith("*.")) domain = domain.slice(2);
664
+ else if (domain.startsWith(".") || domain.startsWith("*")) domain = domain.slice(1);
665
+ if (!domain) return true;
666
+ if (normalizedTargetHost === domain) return false;
667
+ if (normalizedTargetHost.endsWith(`.${domain}`)) return false;
668
+ return true;
669
+ });
670
+ }
671
+ function getProxyForUrl(targetUrl, env) {
672
+ const parsedUrl = parseProxyTargetUrl(targetUrl);
673
+ if (!parsedUrl?.protocol || !parsedUrl.host) return "";
674
+ const protocol = parsedUrl.protocol.split(":", 1)[0];
675
+ if (!shouldProxyHostname(stripBrackets(parsedUrl.hostname || parsedUrl.host.replace(/:\d*$/, "")), Number.parseInt(parsedUrl.port, 10) || DEFAULT_PROXY_PORTS[protocol] || 0, env)) return "";
676
+ let proxy = getProxyEnv(`${protocol}_proxy`, env) || getProxyEnv("all_proxy", env);
677
+ if (proxy && !proxy.includes("://")) proxy = `${protocol}://${proxy}`;
678
+ return proxy;
679
+ }
680
+ const UNSUPPORTED_PROXY_PROTOCOL_MESSAGE = "Unsupported proxy protocol. SOCKS and PAC proxy URLs are not supported; use an HTTP or HTTPS proxy URL.";
681
+ function resolveHttpProxyUrlForTarget(targetUrl, env) {
682
+ const proxy = getProxyForUrl(targetUrl, env);
683
+ if (!proxy) return;
684
+ let proxyUrl;
685
+ try {
686
+ proxyUrl = new URL(proxy);
687
+ } catch (error) {
688
+ throw new Error(`Invalid proxy URL ${JSON.stringify(proxy)}: ${error instanceof Error ? error.message : String(error)}`);
689
+ }
690
+ if (proxyUrl.protocol !== "http:" && proxyUrl.protocol !== "https:") throw new Error(`${UNSUPPORTED_PROXY_PROTOCOL_MESSAGE} Got ${proxyUrl.protocol}`);
691
+ return proxyUrl;
692
+ }
693
+ //#endregion
694
+ //#region node_modules/@earendil-works/pi-ai/dist/utils/pi-user-agent.js
695
+ function loadNodeOs() {
696
+ if (typeof process === "undefined" || !(process.versions?.node || process.versions?.bun)) return null;
697
+ return process.getBuiltinModule?.("node:os") ?? null;
698
+ }
699
+ const nodeOs = loadNodeOs();
700
+ function getPiUserAgent() {
701
+ return nodeOs ? `pi (${nodeOs.platform()} ${nodeOs.release()}; ${nodeOs.arch()})` : "pi (browser)";
702
+ }
703
+ //#endregion
704
+ //#region node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.js
705
+ var UnsupportedStrictJsonSchemaError = class extends Error {};
706
+ const UNSUPPORTED_STRICT_SCHEMA_KEYS = [
707
+ "$ref",
708
+ "$defs",
709
+ "definitions",
710
+ "allOf",
711
+ "oneOf",
712
+ "patternProperties",
713
+ "dependentSchemas",
714
+ "dependencies",
715
+ "unevaluatedProperties",
716
+ "propertyNames",
717
+ "contains",
718
+ "prefixItems",
719
+ "not",
720
+ "if",
721
+ "then",
722
+ "else"
723
+ ];
724
+ function isJsonSchemaObject(value) {
725
+ return typeof value === "object" && value !== null && !Array.isArray(value);
726
+ }
727
+ function isStructuredSchema(schema) {
728
+ if (!isJsonSchemaObject(schema)) return false;
729
+ const types = typeof schema.type === "string" ? [schema.type] : Array.isArray(schema.type) ? schema.type : [];
730
+ return types.includes("object") || types.includes("array") || schema.properties !== void 0 || schema.items !== void 0;
731
+ }
732
+ function schemaAllowsNull(schema) {
733
+ if (!isJsonSchemaObject(schema)) return false;
734
+ if (schema.type === "null" || Array.isArray(schema.type) && schema.type.includes("null")) return true;
735
+ if (schema.const === null || Array.isArray(schema.enum) && schema.enum.includes(null)) return true;
736
+ return Array.isArray(schema.anyOf) && schema.anyOf.some((variant) => schemaAllowsNull(variant));
737
+ }
738
+ function makeJsonSchemaNodeStrict(schema) {
739
+ if (!isJsonSchemaObject(schema)) throw new UnsupportedStrictJsonSchemaError("boolean schemas are unsupported");
740
+ for (const key of UNSUPPORTED_STRICT_SCHEMA_KEYS) if (schema[key] !== void 0) throw new UnsupportedStrictJsonSchemaError(`${key} schemas are unsupported`);
741
+ if (schema.anyOf !== void 0) {
742
+ if (!Array.isArray(schema.anyOf) || schema.anyOf.length === 0) throw new UnsupportedStrictJsonSchemaError("anyOf must contain at least one schema");
743
+ for (const variant of schema.anyOf) {
744
+ if (isStructuredSchema(variant)) throw new UnsupportedStrictJsonSchemaError("object and array unions are unsupported");
745
+ makeJsonSchemaNodeStrict(variant);
746
+ }
747
+ }
748
+ if (schema.items !== void 0) {
749
+ if (Array.isArray(schema.items)) throw new UnsupportedStrictJsonSchemaError("tuple schemas are unsupported");
750
+ makeJsonSchemaNodeStrict(schema.items);
751
+ }
752
+ const isObjectSchema = schema.type === "object";
753
+ if (schema.properties !== void 0 && !isObjectSchema) throw new UnsupportedStrictJsonSchemaError("properties require type object");
754
+ if (!isObjectSchema) return;
755
+ if (schema.additionalProperties !== void 0 && schema.additionalProperties !== false) throw new UnsupportedStrictJsonSchemaError("schema-valued or true additionalProperties is unsupported");
756
+ if (schema.properties !== void 0 && !isJsonSchemaObject(schema.properties)) throw new UnsupportedStrictJsonSchemaError("object properties must be a schema map");
757
+ if (schema.required !== void 0 && (!Array.isArray(schema.required) || schema.required.some((key) => typeof key !== "string"))) throw new UnsupportedStrictJsonSchemaError("object required must be a string array");
758
+ const properties = schema.properties ?? {};
759
+ const propertyNames = Object.keys(properties);
760
+ const required = new Set(Array.isArray(schema.required) ? schema.required : []);
761
+ if ([...required].some((key) => !propertyNames.includes(key))) throw new UnsupportedStrictJsonSchemaError("required contains an unknown property");
762
+ for (const [key, property] of Object.entries(properties)) {
763
+ makeJsonSchemaNodeStrict(property);
764
+ if (!required.has(key) && !schemaAllowsNull(property)) properties[key] = { anyOf: [property, { type: "null" }] };
765
+ }
766
+ schema.required = propertyNames;
767
+ schema.additionalProperties = false;
768
+ }
769
+ /** Convert a tool schema to the strict subset expected by provider constrained sampling. */
770
+ function makeStrictJsonSchema(schema) {
771
+ const cloned = structuredClone(schema);
772
+ if (!isJsonSchemaObject(cloned)) throw new UnsupportedStrictJsonSchemaError("root schema must have type object");
773
+ makeJsonSchemaNodeStrict(cloned);
774
+ if (cloned.type !== "object") throw new UnsupportedStrictJsonSchemaError("root schema must have type object");
775
+ return cloned;
776
+ }
777
+ function getJsonSchemaToolParameters(tool, strict) {
778
+ return strict === true ? makeStrictJsonSchema(tool.parameters) : tool.parameters;
779
+ }
780
+ function getGrammarToolInput(toolName, arguments_, inputProperty) {
781
+ const input = arguments_[inputProperty];
782
+ if (typeof input !== "string") throw new Error(`Grammar tool call "${toolName}" requires argument "${inputProperty}" to be a string.`);
783
+ return input;
784
+ }
785
+ function appendGrammarToolInputJsonDelta(buffer, inputProperty, nextInput, close) {
786
+ if (buffer.closed) {
787
+ if (close && nextInput === buffer.input) return void 0;
788
+ throw new Error(`grammar tool input for property "${inputProperty}" changed after it was closed`);
789
+ }
790
+ if (!nextInput.startsWith(buffer.input)) throw new Error(`grammar tool input for property "${inputProperty}" changed non-monotonically`);
791
+ const inputDelta = nextInput.slice(buffer.input.length);
792
+ if (!close && inputDelta.length === 0) return void 0;
793
+ let delta = "";
794
+ if (!buffer.started) {
795
+ delta += `{${JSON.stringify(inputProperty)}:"`;
796
+ buffer.started = true;
797
+ }
798
+ delta += JSON.stringify(inputDelta).slice(1, -1);
799
+ buffer.input = nextInput;
800
+ if (close) {
801
+ delta += "\"}";
802
+ buffer.closed = true;
803
+ }
804
+ return delta;
805
+ }
806
+ function inferGrammarInputProperty(tool) {
807
+ const schema = tool.parameters;
808
+ if (schema.type !== "object") throw new Error("grammar constrained sampling requires an object parameter schema");
809
+ if (!Array.isArray(schema.required) || schema.required.length !== 1 || typeof schema.required[0] !== "string") throw new Error("grammar constrained sampling requires exactly one required string property");
810
+ const inputProperty = schema.required[0];
811
+ if (!schema.properties?.[inputProperty]) throw new Error(`grammar constrained sampling requires a properties entry for ${inputProperty}`);
812
+ if (schema.properties[inputProperty]?.type !== "string") throw new Error(`grammar constrained sampling property ${inputProperty} must have type string`);
813
+ return inputProperty;
814
+ }
815
+ function resolveJsonSchemaStrictSampling(tool, supportsStrictMode) {
816
+ const config = tool.constrainedSampling;
817
+ if (!config || config.type !== "json_schema") return void 0;
818
+ if (supportsStrictMode) try {
819
+ makeStrictJsonSchema(tool.parameters);
820
+ return true;
821
+ } catch (error) {
822
+ if (!(error instanceof UnsupportedStrictJsonSchemaError)) throw error;
823
+ if (config.strict !== "require") return void 0;
824
+ throw new Error(`Tool "${tool.name}" requires JSON-schema constrained sampling, but ${error.message}.`);
825
+ }
826
+ if (config.strict === "require") throw new Error(`Tool "${tool.name}" requires JSON-schema constrained sampling, but strict tools are unsupported.`);
827
+ }
828
+ function resolveGrammarConstrainedSampling(tool, supportsOpenAIGrammarTools) {
829
+ const config = tool.constrainedSampling;
830
+ if (!config || config.type !== "grammar") return;
831
+ if (!supportsOpenAIGrammarTools) return;
832
+ const larkDefinition = config.variants.openai_lark;
833
+ const regexDefinition = config.variants.openai_regex;
834
+ const hasLarkDefinition = typeof larkDefinition === "string" && larkDefinition.trim().length > 0;
835
+ const hasRegexDefinition = typeof regexDefinition === "string" && regexDefinition.trim().length > 0;
836
+ if (!hasLarkDefinition && !hasRegexDefinition) throw new Error(`Tool "${tool.name}" cannot use grammar constrained sampling: no supported grammar variant was provided.`);
837
+ try {
838
+ return {
839
+ format: hasLarkDefinition ? "lark" : "regex",
840
+ definition: hasLarkDefinition ? larkDefinition : regexDefinition,
841
+ inputProperty: inferGrammarInputProperty(tool)
842
+ };
843
+ } catch (error) {
844
+ const message = error instanceof Error ? error.message : String(error);
845
+ throw new Error(`Tool "${tool.name}" cannot use grammar constrained sampling: ${message}.`);
846
+ }
847
+ }
848
+ function createGrammarToolInputProperties(tools, supportsOpenAIGrammarTools) {
849
+ const properties = /* @__PURE__ */ new Map();
850
+ for (const tool of tools ?? []) {
851
+ const grammar = resolveGrammarConstrainedSampling(tool, supportsOpenAIGrammarTools);
852
+ if (grammar) properties.set(tool.name, grammar.inputProperty);
853
+ }
854
+ return properties;
855
+ }
856
+ function clampOpenAIPromptCacheKey(key) {
857
+ if (key === void 0) return void 0;
858
+ const chars = Array.from(key);
859
+ if (chars.length <= 64) return key;
860
+ return chars.slice(0, 64).join("");
861
+ }
862
+ //#endregion
863
+ //#region node_modules/@earendil-works/pi-ai/dist/utils/hash.js
864
+ /** Fast deterministic hash to shorten long strings */
865
+ function shortHash(str) {
866
+ let h1 = 3735928559;
867
+ let h2 = 1103547991;
868
+ for (let i = 0; i < str.length; i++) {
869
+ const ch = str.charCodeAt(i);
870
+ h1 = Math.imul(h1 ^ ch, 2654435761);
871
+ h2 = Math.imul(h2 ^ ch, 1597334677);
872
+ }
873
+ h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
874
+ h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
875
+ return (h2 >>> 0).toString(36) + (h1 >>> 0).toString(36);
876
+ }
877
+ //#endregion
878
+ //#region node_modules/@earendil-works/pi-ai/dist/utils/sanitize-unicode.js
879
+ /**
880
+ * Removes unpaired Unicode surrogate characters from a string.
881
+ *
882
+ * Unpaired surrogates (high surrogates 0xD800-0xDBFF without matching low surrogates 0xDC00-0xDFFF,
883
+ * or vice versa) cause JSON serialization errors in many API providers.
884
+ *
885
+ * Valid emoji and other characters outside the Basic Multilingual Plane use properly paired
886
+ * surrogates and will NOT be affected by this function.
887
+ *
888
+ * @param text - The text to sanitize
889
+ * @returns The sanitized text with unpaired surrogates removed
890
+ *
891
+ * @example
892
+ * // Valid emoji (properly paired surrogates) are preserved
893
+ * sanitizeSurrogates("Hello 🙈 World") // => "Hello 🙈 World"
894
+ *
895
+ * // Unpaired high surrogate is removed
896
+ * const unpaired = String.fromCharCode(0xD83D); // high surrogate without low
897
+ * sanitizeSurrogates(`Text ${unpaired} here`) // => "Text here"
898
+ */
899
+ function sanitizeSurrogates(text) {
900
+ return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
901
+ }
902
+ //#endregion
903
+ //#region node_modules/@earendil-works/pi-ai/dist/api/transform-messages.js
904
+ const NON_VISION_USER_IMAGE_PLACEHOLDER = "(image omitted: model does not support images)";
905
+ const NON_VISION_TOOL_IMAGE_PLACEHOLDER = "(tool image omitted: model does not support images)";
906
+ function replaceImagesWithPlaceholder(content, placeholder) {
907
+ const result = [];
908
+ let previousWasPlaceholder = false;
909
+ for (const block of content) {
910
+ if (block.type === "image") {
911
+ if (!previousWasPlaceholder) result.push({
912
+ type: "text",
913
+ text: placeholder
914
+ });
915
+ previousWasPlaceholder = true;
916
+ continue;
917
+ }
918
+ result.push(block);
919
+ previousWasPlaceholder = block.text === placeholder;
920
+ }
921
+ return result;
922
+ }
923
+ function downgradeUnsupportedImages(messages, model) {
924
+ if (model.input.includes("image")) return messages;
925
+ return messages.map((msg) => {
926
+ if (msg.role === "user" && Array.isArray(msg.content)) return {
927
+ ...msg,
928
+ content: replaceImagesWithPlaceholder(msg.content, NON_VISION_USER_IMAGE_PLACEHOLDER)
929
+ };
930
+ if (msg.role === "toolResult") return {
931
+ ...msg,
932
+ content: replaceImagesWithPlaceholder(msg.content, NON_VISION_TOOL_IMAGE_PLACEHOLDER)
933
+ };
934
+ return msg;
935
+ });
936
+ }
937
+ /**
938
+ * Normalize tool call ID for cross-provider compatibility.
939
+ * OpenAI Responses API generates IDs that are 450+ chars with special characters like `|`.
940
+ * Anthropic APIs require IDs matching ^[a-zA-Z0-9_-]+$ (max 64 chars).
941
+ */
942
+ function transformMessages(messages, model, normalizeToolCallId) {
943
+ const toolCallIdMap = /* @__PURE__ */ new Map();
944
+ const transformed = downgradeUnsupportedImages(messages.map((msg) => msg.content == null ? {
945
+ ...msg,
946
+ content: []
947
+ } : msg), model).map((msg) => {
948
+ if (msg.role === "user") return msg;
949
+ if (msg.role === "toolResult") {
950
+ const normalizedId = toolCallIdMap.get(msg.toolCallId);
951
+ if (normalizedId && normalizedId !== msg.toolCallId) return {
952
+ ...msg,
953
+ toolCallId: normalizedId
954
+ };
955
+ return msg;
956
+ }
957
+ if (msg.role === "assistant") {
958
+ const assistantMsg = msg;
959
+ const isSameModel = assistantMsg.provider === model.provider && assistantMsg.api === model.api && assistantMsg.model === model.id;
960
+ const transformedContent = assistantMsg.content.flatMap((block) => {
961
+ if (block.type === "thinking") {
962
+ if (block.redacted) return isSameModel ? block : [];
963
+ if (isSameModel && block.thinkingSignature) return block;
964
+ if (!block.thinking || block.thinking.trim() === "") return [];
965
+ if (isSameModel) return block;
966
+ return {
967
+ type: "text",
968
+ text: block.thinking
969
+ };
970
+ }
971
+ if (block.type === "text") {
972
+ if (isSameModel) return block;
973
+ return {
974
+ type: "text",
975
+ text: block.text
976
+ };
977
+ }
978
+ if (block.type === "toolCall") {
979
+ const toolCall = block;
980
+ let normalizedToolCall = toolCall;
981
+ if (!isSameModel && toolCall.thoughtSignature) {
982
+ normalizedToolCall = { ...toolCall };
983
+ delete normalizedToolCall.thoughtSignature;
984
+ }
985
+ if (!isSameModel && normalizeToolCallId) {
986
+ const normalizedId = normalizeToolCallId(toolCall.id, model, assistantMsg);
987
+ if (normalizedId !== toolCall.id) {
988
+ toolCallIdMap.set(toolCall.id, normalizedId);
989
+ normalizedToolCall = {
990
+ ...normalizedToolCall,
991
+ id: normalizedId
992
+ };
993
+ }
994
+ }
995
+ return normalizedToolCall;
996
+ }
997
+ return block;
998
+ });
999
+ return {
1000
+ ...assistantMsg,
1001
+ content: transformedContent
1002
+ };
1003
+ }
1004
+ return msg;
1005
+ });
1006
+ const result = [];
1007
+ let pendingToolCalls = [];
1008
+ let existingToolResultIds = /* @__PURE__ */ new Set();
1009
+ const insertSyntheticToolResults = () => {
1010
+ if (pendingToolCalls.length > 0) {
1011
+ for (const tc of pendingToolCalls) if (!existingToolResultIds.has(tc.id)) result.push({
1012
+ role: "toolResult",
1013
+ toolCallId: tc.id,
1014
+ toolName: tc.name,
1015
+ content: [{
1016
+ type: "text",
1017
+ text: "No result provided"
1018
+ }],
1019
+ isError: true,
1020
+ timestamp: Date.now()
1021
+ });
1022
+ pendingToolCalls = [];
1023
+ existingToolResultIds = /* @__PURE__ */ new Set();
1024
+ }
1025
+ };
1026
+ for (let i = 0; i < transformed.length; i++) {
1027
+ const msg = transformed[i];
1028
+ if (msg.role === "assistant") {
1029
+ insertSyntheticToolResults();
1030
+ const assistantMsg = msg;
1031
+ if (assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") continue;
1032
+ const toolCalls = assistantMsg.content.filter((b) => b.type === "toolCall");
1033
+ if (toolCalls.length > 0) {
1034
+ pendingToolCalls = toolCalls;
1035
+ existingToolResultIds = /* @__PURE__ */ new Set();
1036
+ }
1037
+ result.push(msg);
1038
+ } else if (msg.role === "toolResult") {
1039
+ existingToolResultIds.add(msg.toolCallId);
1040
+ result.push(msg);
1041
+ } else if (msg.role === "user") {
1042
+ insertSyntheticToolResults();
1043
+ result.push(msg);
1044
+ } else result.push(msg);
1045
+ }
1046
+ insertSyntheticToolResults();
1047
+ return result;
1048
+ }
1049
+ //#endregion
1050
+ //#region node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js
1051
+ function encodeTextSignatureV1(id, phase) {
1052
+ const payload = {
1053
+ v: 1,
1054
+ id
1055
+ };
1056
+ if (phase) payload.phase = phase;
1057
+ return JSON.stringify(payload);
1058
+ }
1059
+ function parseTextSignature(signature) {
1060
+ if (!signature) return void 0;
1061
+ if (signature.startsWith("{")) try {
1062
+ const parsed = JSON.parse(signature);
1063
+ if (parsed.v === 1 && typeof parsed.id === "string") {
1064
+ if (parsed.phase === "commentary" || parsed.phase === "final_answer") return {
1065
+ id: parsed.id,
1066
+ phase: parsed.phase
1067
+ };
1068
+ return { id: parsed.id };
1069
+ }
1070
+ } catch {}
1071
+ return { id: signature };
1072
+ }
1073
+ function convertToolResultOutput(model, content) {
1074
+ const textResult = content.filter((c) => c.type === "text").map((c) => c.text).join("\n");
1075
+ const images = content.filter((c) => c.type === "image");
1076
+ const hasText = textResult.length > 0;
1077
+ if (images.length === 0 || !model.input.includes("image")) return sanitizeSurrogates(hasText ? textResult : images.length > 0 ? "(see attached image)" : "(no tool output)");
1078
+ const output = [];
1079
+ if (hasText) output.push({
1080
+ type: "input_text",
1081
+ text: sanitizeSurrogates(textResult)
1082
+ });
1083
+ for (const image of images) output.push({
1084
+ type: "input_image",
1085
+ detail: "auto",
1086
+ image_url: `data:${image.mimeType};base64,${image.data}`
1087
+ });
1088
+ return output;
1089
+ }
1090
+ function convertResponsesMessages(model, context, allowedToolCallProviders, options) {
1091
+ const messages = [];
1092
+ const loadedToolNames = /* @__PURE__ */ new Set();
1093
+ const normalizeIdPart = (part) => {
1094
+ const sanitized = part.replace(/[^a-zA-Z0-9_-]/g, "_");
1095
+ return (sanitized.length > 64 ? sanitized.slice(0, 64) : sanitized).replace(/_+$/, "");
1096
+ };
1097
+ const buildForeignResponsesItemId = (itemId) => {
1098
+ const normalized = `fc_${shortHash(itemId)}`;
1099
+ return normalized.length > 64 ? normalized.slice(0, 64) : normalized;
1100
+ };
1101
+ const normalizeToolCallId = (id, _targetModel, source) => {
1102
+ if (!allowedToolCallProviders.has(model.provider)) return normalizeIdPart(id);
1103
+ if (!id.includes("|")) return normalizeIdPart(id);
1104
+ const [callId, itemId] = id.split("|");
1105
+ const normalizedCallId = normalizeIdPart(callId);
1106
+ let normalizedItemId = source.provider !== model.provider || source.api !== model.api ? buildForeignResponsesItemId(itemId) : normalizeIdPart(itemId);
1107
+ if (!normalizedItemId.startsWith("fc_")) normalizedItemId = normalizeIdPart(`fc_${normalizedItemId}`);
1108
+ return `${normalizedCallId}|${normalizedItemId}`;
1109
+ };
1110
+ const transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);
1111
+ if ((options?.includeSystemPrompt ?? true) && context.systemPrompt) {
1112
+ const compat = model.compat;
1113
+ const role = model.reasoning && compat?.supportsDeveloperRole !== false ? "developer" : "system";
1114
+ messages.push({
1115
+ role,
1116
+ content: sanitizeSurrogates(context.systemPrompt)
1117
+ });
1118
+ }
1119
+ let msgIndex = 0;
1120
+ for (const msg of transformedMessages) {
1121
+ if (msg.role === "user") if (typeof msg.content === "string") messages.push({
1122
+ role: "user",
1123
+ content: [{
1124
+ type: "input_text",
1125
+ text: sanitizeSurrogates(msg.content)
1126
+ }]
1127
+ });
1128
+ else {
1129
+ const content = msg.content.map((item) => {
1130
+ if (item.type === "text") return {
1131
+ type: "input_text",
1132
+ text: sanitizeSurrogates(item.text)
1133
+ };
1134
+ return {
1135
+ type: "input_image",
1136
+ detail: "auto",
1137
+ image_url: `data:${item.mimeType};base64,${item.data}`
1138
+ };
1139
+ });
1140
+ if (content.length === 0) continue;
1141
+ messages.push({
1142
+ role: "user",
1143
+ content
1144
+ });
1145
+ }
1146
+ else if (msg.role === "assistant") {
1147
+ const output = [];
1148
+ const assistantMsg = msg;
1149
+ const isSameProviderAndApi = assistantMsg.provider === model.provider && assistantMsg.api === model.api;
1150
+ const isSameModel = isSameProviderAndApi && assistantMsg.model === model.id;
1151
+ const isDifferentModel = isSameProviderAndApi && assistantMsg.model !== model.id;
1152
+ let textBlockIndex = 0;
1153
+ for (const block of msg.content) if (block.type === "thinking") {
1154
+ if (block.thinkingSignature) {
1155
+ const reasoningItem = JSON.parse(block.thinkingSignature);
1156
+ output.push(reasoningItem);
1157
+ }
1158
+ } else if (block.type === "text") {
1159
+ const textBlock = block;
1160
+ const parsedSignature = parseTextSignature(textBlock.textSignature);
1161
+ const fallbackMessageId = textBlockIndex === 0 ? `msg_pi_${msgIndex}` : `msg_pi_${msgIndex}_${textBlockIndex}`;
1162
+ textBlockIndex++;
1163
+ let msgId = parsedSignature?.id;
1164
+ if (!msgId) msgId = fallbackMessageId;
1165
+ else if (msgId.length > 64) msgId = `msg_${shortHash(msgId)}`;
1166
+ output.push({
1167
+ type: "message",
1168
+ role: "assistant",
1169
+ content: [{
1170
+ type: "output_text",
1171
+ text: sanitizeSurrogates(textBlock.text),
1172
+ annotations: []
1173
+ }],
1174
+ status: "completed",
1175
+ id: msgId,
1176
+ phase: parsedSignature?.phase
1177
+ });
1178
+ } else if (block.type === "toolCall") {
1179
+ const toolCall = block;
1180
+ const [callId, itemIdRaw] = toolCall.id.split("|");
1181
+ const customInputProperty = options?.grammarToolInputProperties?.get(toolCall.name);
1182
+ let itemId = itemIdRaw;
1183
+ if (isDifferentModel && itemId?.startsWith("fc_") || customInputProperty === void 0 && !itemId?.startsWith("fc_")) itemId = void 0;
1184
+ const canReplayNamespace = isSameModel || options?.deferredTools?.has(toolCall.name) === true;
1185
+ if (customInputProperty !== void 0) output.push({
1186
+ type: "custom_tool_call",
1187
+ id: itemId,
1188
+ call_id: callId,
1189
+ name: toolCall.name,
1190
+ input: sanitizeSurrogates(getGrammarToolInput(toolCall.name, toolCall.arguments, customInputProperty)),
1191
+ ...canReplayNamespace && toolCall.namespace !== void 0 ? { namespace: toolCall.namespace } : {}
1192
+ });
1193
+ else output.push({
1194
+ type: "function_call",
1195
+ id: itemId,
1196
+ call_id: callId,
1197
+ name: toolCall.name,
1198
+ arguments: JSON.stringify(toolCall.arguments),
1199
+ ...canReplayNamespace && toolCall.namespace !== void 0 ? { namespace: toolCall.namespace } : {}
1200
+ });
1201
+ }
1202
+ if (output.length === 0) continue;
1203
+ messages.push(...output);
1204
+ } else if (msg.role === "toolResult") {
1205
+ const [callId] = msg.toolCallId.split("|");
1206
+ const output = convertToolResultOutput(model, msg.content);
1207
+ if (options?.grammarToolInputProperties?.has(msg.toolName)) messages.push({
1208
+ type: "custom_tool_call_output",
1209
+ call_id: callId,
1210
+ output
1211
+ });
1212
+ else messages.push({
1213
+ type: "function_call_output",
1214
+ call_id: callId,
1215
+ output
1216
+ });
1217
+ const deferredTools = [];
1218
+ for (const name of msg.addedToolNames ?? []) {
1219
+ const tool = options?.deferredTools?.get(name);
1220
+ if (!tool || loadedToolNames.has(name)) continue;
1221
+ loadedToolNames.add(name);
1222
+ deferredTools.push(tool);
1223
+ }
1224
+ if (deferredTools.length > 0 && options?.deferredToolsMode === "additional-tools") messages.push({
1225
+ type: "additional_tools",
1226
+ role: "developer",
1227
+ tools: convertResponsesTools(deferredTools, options.toolOptions)
1228
+ });
1229
+ else if (deferredTools.length > 0 && options?.deferredToolsMode === "tool-search") {
1230
+ const names = deferredTools.map((tool) => tool.name);
1231
+ const searchCallId = `pi_tool_load_${shortHash(`${msg.toolCallId}:${names.join(",")}`)}`;
1232
+ messages.push({
1233
+ type: "tool_search_call",
1234
+ call_id: searchCallId,
1235
+ execution: "client",
1236
+ status: "completed",
1237
+ arguments: {
1238
+ query: names.join(" "),
1239
+ limit: names.length
1240
+ }
1241
+ });
1242
+ messages.push({
1243
+ type: "tool_search_output",
1244
+ call_id: searchCallId,
1245
+ execution: "client",
1246
+ status: "completed",
1247
+ tools: convertResponsesTools(deferredTools, {
1248
+ ...options.toolOptions,
1249
+ deferLoading: true
1250
+ })
1251
+ });
1252
+ }
1253
+ }
1254
+ msgIndex++;
1255
+ }
1256
+ return messages;
1257
+ }
1258
+ function convertResponsesTools(tools, options) {
1259
+ const defaultStrict = options?.strict === void 0 ? false : options.strict;
1260
+ const supportsStrictMode = options?.supportsStrictMode ?? true;
1261
+ const supportsOpenAIGrammarTools = options?.supportsOpenAIGrammarTools ?? false;
1262
+ return tools.map((tool) => {
1263
+ const grammar = resolveGrammarConstrainedSampling(tool, supportsOpenAIGrammarTools);
1264
+ if (grammar) return {
1265
+ type: "custom",
1266
+ name: tool.name,
1267
+ description: tool.description,
1268
+ format: {
1269
+ type: "grammar",
1270
+ syntax: grammar.format,
1271
+ definition: grammar.definition
1272
+ },
1273
+ ...options?.deferLoading ? { defer_loading: true } : {}
1274
+ };
1275
+ const strict = resolveJsonSchemaStrictSampling(tool, supportsStrictMode) ?? defaultStrict;
1276
+ const functionTool = {
1277
+ type: "function",
1278
+ name: tool.name,
1279
+ description: tool.description,
1280
+ parameters: getJsonSchemaToolParameters(tool, strict === true),
1281
+ ...options?.deferLoading ? { defer_loading: true } : {}
1282
+ };
1283
+ if (supportsStrictMode) functionTool.strict = strict;
1284
+ return functionTool;
1285
+ });
1286
+ }
1287
+ function getCustomToolCallInput(block) {
1288
+ const property = block.customInput?.property;
1289
+ if (property === void 0) return "";
1290
+ const value = block.arguments[property];
1291
+ return typeof value === "string" ? value : "";
1292
+ }
1293
+ function appendCustomToolCallInput(block, nextInput, close) {
1294
+ const customInput = block.customInput;
1295
+ if (!customInput) return void 0;
1296
+ const delta = appendGrammarToolInputJsonDelta(customInput.jsonBuffer, customInput.property, nextInput, close);
1297
+ block.arguments = { [customInput.property]: nextInput };
1298
+ return delta;
1299
+ }
1300
+ async function processResponsesStream(openaiStream, output, stream, model, options) {
1301
+ let sawTerminalResponseEvent = false;
1302
+ const outputSlots = /* @__PURE__ */ new Map();
1303
+ const reasoningBlocksById = /* @__PURE__ */ new Map();
1304
+ const applyMessagePhaseStopReason = (item) => {
1305
+ if (item.type === "message" && item.phase === "final_answer") output.stopReason = "stop";
1306
+ };
1307
+ const getSlot = (outputIndex, type) => {
1308
+ const slot = outputSlots.get(outputIndex);
1309
+ return slot?.type === type ? slot : void 0;
1310
+ };
1311
+ const pushToolCallDelta = (slot, delta) => {
1312
+ if (delta === void 0) return;
1313
+ stream.push({
1314
+ type: "toolcall_delta",
1315
+ contentIndex: slot.contentIndex,
1316
+ delta,
1317
+ partial: output
1318
+ });
1319
+ };
1320
+ const createSlot = (outputIndex, item) => {
1321
+ if (item.type === "reasoning") {
1322
+ const block = {
1323
+ type: "thinking",
1324
+ thinking: ""
1325
+ };
1326
+ output.content.push(block);
1327
+ const slot = {
1328
+ type: "thinking",
1329
+ block,
1330
+ contentIndex: output.content.length - 1
1331
+ };
1332
+ outputSlots.set(outputIndex, slot);
1333
+ stream.push({
1334
+ type: "thinking_start",
1335
+ contentIndex: slot.contentIndex,
1336
+ partial: output
1337
+ });
1338
+ return slot;
1339
+ }
1340
+ if (item.type === "message") {
1341
+ applyMessagePhaseStopReason(item);
1342
+ const block = {
1343
+ type: "text",
1344
+ text: ""
1345
+ };
1346
+ output.content.push(block);
1347
+ const slot = {
1348
+ type: "text",
1349
+ block,
1350
+ contentIndex: output.content.length - 1
1351
+ };
1352
+ outputSlots.set(outputIndex, slot);
1353
+ stream.push({
1354
+ type: "text_start",
1355
+ contentIndex: slot.contentIndex,
1356
+ partial: output
1357
+ });
1358
+ return slot;
1359
+ }
1360
+ if (item.type === "function_call") {
1361
+ const block = {
1362
+ type: "toolCall",
1363
+ id: `${item.call_id}|${item.id}`,
1364
+ name: item.name,
1365
+ arguments: {},
1366
+ ...item.namespace !== void 0 ? { namespace: item.namespace } : {},
1367
+ partialJson: item.arguments || ""
1368
+ };
1369
+ output.content.push(block);
1370
+ const slot = {
1371
+ type: "toolCall",
1372
+ block,
1373
+ contentIndex: output.content.length - 1
1374
+ };
1375
+ outputSlots.set(outputIndex, slot);
1376
+ stream.push({
1377
+ type: "toolcall_start",
1378
+ contentIndex: slot.contentIndex,
1379
+ partial: output
1380
+ });
1381
+ return slot;
1382
+ }
1383
+ if (item.type === "custom_tool_call") {
1384
+ const inputProperty = options?.grammarToolInputProperties?.get(item.name) ?? "input";
1385
+ const input = item.input || "";
1386
+ const block = {
1387
+ type: "toolCall",
1388
+ id: `${item.call_id}|${item.id}`,
1389
+ name: item.name,
1390
+ arguments: { [inputProperty]: input },
1391
+ ...item.namespace !== void 0 ? { namespace: item.namespace } : {},
1392
+ customInput: {
1393
+ property: inputProperty,
1394
+ jsonBuffer: {
1395
+ input: "",
1396
+ started: false,
1397
+ closed: false
1398
+ }
1399
+ }
1400
+ };
1401
+ output.content.push(block);
1402
+ const slot = {
1403
+ type: "toolCall",
1404
+ block,
1405
+ contentIndex: output.content.length - 1
1406
+ };
1407
+ outputSlots.set(outputIndex, slot);
1408
+ stream.push({
1409
+ type: "toolcall_start",
1410
+ contentIndex: slot.contentIndex,
1411
+ partial: output
1412
+ });
1413
+ return slot;
1414
+ }
1415
+ };
1416
+ const getOrCreateSlot = (outputIndex, item) => {
1417
+ return outputSlots.get(outputIndex) ?? createSlot(outputIndex, item);
1418
+ };
1419
+ const backfillReasoningSignatures = (responseOutput) => {
1420
+ for (const item of responseOutput) {
1421
+ if (item.type !== "reasoning" || !item.encrypted_content) continue;
1422
+ const block = reasoningBlocksById.get(item.id);
1423
+ if (!block?.thinkingSignature) continue;
1424
+ const storedItem = JSON.parse(block.thinkingSignature);
1425
+ if (storedItem.encrypted_content) continue;
1426
+ block.thinkingSignature = JSON.stringify({
1427
+ ...storedItem,
1428
+ encrypted_content: item.encrypted_content
1429
+ });
1430
+ }
1431
+ };
1432
+ const finalizeResponse = (response) => {
1433
+ sawTerminalResponseEvent = true;
1434
+ backfillReasoningSignatures(response.output ?? []);
1435
+ if (response?.id) output.responseId = response.id;
1436
+ if (response?.usage) {
1437
+ const inputDetails = response.usage.input_tokens_details;
1438
+ const cachedTokens = inputDetails?.cached_tokens || 0;
1439
+ const cacheWriteTokens = inputDetails?.cache_write_tokens || 0;
1440
+ output.usage = {
1441
+ input: Math.max(0, (response.usage.input_tokens || 0) - cachedTokens - cacheWriteTokens),
1442
+ output: response.usage.output_tokens || 0,
1443
+ cacheRead: cachedTokens,
1444
+ cacheWrite: cacheWriteTokens,
1445
+ reasoning: response.usage.output_tokens_details?.reasoning_tokens || 0,
1446
+ totalTokens: response.usage.total_tokens || 0,
1447
+ cost: {
1448
+ input: 0,
1449
+ output: 0,
1450
+ cacheRead: 0,
1451
+ cacheWrite: 0,
1452
+ total: 0
1453
+ }
1454
+ };
1455
+ }
1456
+ calculateCost(model, output.usage);
1457
+ if (options?.applyServiceTierPricing) {
1458
+ const serviceTier = options.resolveServiceTier ? options.resolveServiceTier(response?.service_tier, options.serviceTier) : response?.service_tier ?? options.serviceTier;
1459
+ options.applyServiceTierPricing(output.usage, serviceTier);
1460
+ }
1461
+ const status = response?.status;
1462
+ const incompleteDetails = response?.incomplete_details;
1463
+ const incompleteReason = typeof incompleteDetails?.reason === "string" ? incompleteDetails.reason : void 0;
1464
+ output.rawStopReason = incompleteReason ? `${status}.${incompleteReason}` : status;
1465
+ const mappedStop = mapStopReason(status, incompleteReason);
1466
+ output.stopReason = mappedStop.stopReason;
1467
+ if (mappedStop.errorMessage === void 0) delete output.errorMessage;
1468
+ else output.errorMessage = mappedStop.errorMessage;
1469
+ if (output.content.some((b) => b.type === "toolCall") && output.stopReason === "stop") output.stopReason = "toolUse";
1470
+ };
1471
+ for await (const event of openaiStream) if (event.type === "response.created") output.responseId = event.response.id;
1472
+ else if (event.type === "response.output_item.added") createSlot(event.output_index, event.item);
1473
+ else if (event.type === "response.reasoning_summary_text.delta") {
1474
+ const slot = getSlot(event.output_index, "thinking");
1475
+ if (!slot) continue;
1476
+ slot.block.thinking += event.delta;
1477
+ stream.push({
1478
+ type: "thinking_delta",
1479
+ contentIndex: slot.contentIndex,
1480
+ delta: event.delta,
1481
+ partial: output
1482
+ });
1483
+ } else if (event.type === "response.reasoning_summary_part.done") {
1484
+ const slot = getSlot(event.output_index, "thinking");
1485
+ if (!slot) continue;
1486
+ slot.block.thinking += "\n\n";
1487
+ stream.push({
1488
+ type: "thinking_delta",
1489
+ contentIndex: slot.contentIndex,
1490
+ delta: "\n\n",
1491
+ partial: output
1492
+ });
1493
+ } else if (event.type === "response.reasoning_text.delta") {
1494
+ const slot = getSlot(event.output_index, "thinking");
1495
+ if (!slot) continue;
1496
+ slot.block.thinking += event.delta;
1497
+ stream.push({
1498
+ type: "thinking_delta",
1499
+ contentIndex: slot.contentIndex,
1500
+ delta: event.delta,
1501
+ partial: output
1502
+ });
1503
+ } else if (event.type === "response.output_text.delta") {
1504
+ const slot = getSlot(event.output_index, "text");
1505
+ if (!slot) continue;
1506
+ slot.block.text += event.delta;
1507
+ stream.push({
1508
+ type: "text_delta",
1509
+ contentIndex: slot.contentIndex,
1510
+ delta: event.delta,
1511
+ partial: output
1512
+ });
1513
+ } else if (event.type === "response.refusal.delta") {
1514
+ const slot = getSlot(event.output_index, "text");
1515
+ if (!slot) continue;
1516
+ slot.block.text += event.delta;
1517
+ stream.push({
1518
+ type: "text_delta",
1519
+ contentIndex: slot.contentIndex,
1520
+ delta: event.delta,
1521
+ partial: output
1522
+ });
1523
+ } else if (event.type === "response.function_call_arguments.delta") {
1524
+ const slot = getSlot(event.output_index, "toolCall");
1525
+ if (!slot || slot.block.partialJson === void 0) continue;
1526
+ slot.block.partialJson += event.delta;
1527
+ slot.block.arguments = parseStreamingJson(slot.block.partialJson);
1528
+ pushToolCallDelta(slot, event.delta);
1529
+ } else if (event.type === "response.function_call_arguments.done") {
1530
+ const slot = getSlot(event.output_index, "toolCall");
1531
+ if (!slot || slot.block.partialJson === void 0) continue;
1532
+ const previousPartialJson = slot.block.partialJson;
1533
+ slot.block.partialJson = event.arguments;
1534
+ slot.block.arguments = parseStreamingJson(slot.block.partialJson);
1535
+ if (event.arguments.startsWith(previousPartialJson)) {
1536
+ const delta = event.arguments.slice(previousPartialJson.length);
1537
+ if (delta.length > 0) pushToolCallDelta(slot, delta);
1538
+ }
1539
+ } else if (event.type === "response.custom_tool_call_input.delta") {
1540
+ const slot = getSlot(event.output_index, "toolCall");
1541
+ if (!slot || !slot.block.customInput) continue;
1542
+ pushToolCallDelta(slot, appendCustomToolCallInput(slot.block, getCustomToolCallInput(slot.block) + event.delta, false));
1543
+ } else if (event.type === "response.custom_tool_call_input.done") {
1544
+ const slot = getSlot(event.output_index, "toolCall");
1545
+ if (!slot || !slot.block.customInput) continue;
1546
+ pushToolCallDelta(slot, appendCustomToolCallInput(slot.block, event.input, true));
1547
+ } else if (event.type === "response.output_item.done") {
1548
+ const item = event.item;
1549
+ applyMessagePhaseStopReason(item);
1550
+ const slot = getOrCreateSlot(event.output_index, item);
1551
+ if (item.type === "reasoning" && slot?.type === "thinking") {
1552
+ const summaryText = item.summary?.map((s) => s.text).join("\n\n") || "";
1553
+ const contentText = item.content?.map((c) => c.text).join("\n\n") || "";
1554
+ slot.block.thinking = summaryText || contentText || slot.block.thinking;
1555
+ slot.block.thinkingSignature = JSON.stringify(item);
1556
+ reasoningBlocksById.set(item.id, slot.block);
1557
+ stream.push({
1558
+ type: "thinking_end",
1559
+ contentIndex: slot.contentIndex,
1560
+ content: slot.block.thinking,
1561
+ partial: output
1562
+ });
1563
+ outputSlots.delete(event.output_index);
1564
+ } else if (item.type === "message" && slot?.type === "text") {
1565
+ slot.block.text = item.content?.map((c) => c.type === "output_text" ? c.text : c.refusal).join("") || "";
1566
+ slot.block.textSignature = encodeTextSignatureV1(item.id, item.phase ?? void 0);
1567
+ stream.push({
1568
+ type: "text_end",
1569
+ contentIndex: slot.contentIndex,
1570
+ content: slot.block.text,
1571
+ partial: output
1572
+ });
1573
+ outputSlots.delete(event.output_index);
1574
+ } else if (item.type === "function_call" && slot?.type === "toolCall" && slot.block.partialJson !== void 0) {
1575
+ slot.block.arguments = parseStreamingJson(item.arguments || slot.block.partialJson || "{}");
1576
+ if (item.namespace !== void 0) slot.block.namespace = item.namespace;
1577
+ delete slot.block.partialJson;
1578
+ stream.push({
1579
+ type: "toolcall_end",
1580
+ contentIndex: slot.contentIndex,
1581
+ toolCall: slot.block,
1582
+ partial: output
1583
+ });
1584
+ outputSlots.delete(event.output_index);
1585
+ } else if (item.type === "custom_tool_call" && slot?.type === "toolCall" && slot.block.customInput) {
1586
+ pushToolCallDelta(slot, appendCustomToolCallInput(slot.block, item.input ?? getCustomToolCallInput(slot.block), true));
1587
+ if (item.namespace !== void 0) slot.block.namespace = item.namespace;
1588
+ delete slot.block.customInput;
1589
+ stream.push({
1590
+ type: "toolcall_end",
1591
+ contentIndex: slot.contentIndex,
1592
+ toolCall: slot.block,
1593
+ partial: output
1594
+ });
1595
+ outputSlots.delete(event.output_index);
1596
+ }
1597
+ } else if (event.type === "response.completed" || event.type === "response.incomplete") finalizeResponse(event.response);
1598
+ else if (event.type === "error") throw new Error(`Error Code ${event.code}: ${event.message}` || "Unknown error");
1599
+ else if (event.type === "response.failed") {
1600
+ sawTerminalResponseEvent = true;
1601
+ output.rawStopReason = event.response?.status;
1602
+ const error = event.response?.error;
1603
+ const details = event.response?.incomplete_details;
1604
+ const msg = error ? `${error.code || "unknown"}: ${error.message || "no message"}` : details?.reason ? `incomplete: ${details.reason}` : "Unknown error (no error details in response)";
1605
+ throw new Error(msg);
1606
+ }
1607
+ if (!sawTerminalResponseEvent) throw new Error("OpenAI Responses stream ended before a terminal response event");
1608
+ }
1609
+ function mapStopReason(status, incompleteReason) {
1610
+ if (!status) return { stopReason: "stop" };
1611
+ switch (status) {
1612
+ case "completed": return { stopReason: "stop" };
1613
+ case "incomplete":
1614
+ if (incompleteReason === "max_output_tokens") return { stopReason: "length" };
1615
+ return {
1616
+ stopReason: "error",
1617
+ errorMessage: incompleteReason ? `Response incomplete: ${incompleteReason}` : "Response incomplete without a provider reason"
1618
+ };
1619
+ case "failed":
1620
+ case "cancelled": return { stopReason: "error" };
1621
+ case "in_progress":
1622
+ case "queued": return { stopReason: "stop" };
1623
+ default: throw new Error(`Unhandled stop reason: ${status}`);
1624
+ }
1625
+ }
1626
+ //#endregion
1627
+ //#region node_modules/@earendil-works/pi-ai/dist/utils/estimate.js
1628
+ const CHARS_PER_TOKEN = 4;
1629
+ const ESTIMATED_IMAGE_CHARS = 4800;
1630
+ function calculateContextTokens(usage) {
1631
+ return usage.totalTokens || usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
1632
+ }
1633
+ function safeJsonStringify(value) {
1634
+ try {
1635
+ return JSON.stringify(value) ?? "undefined";
1636
+ } catch {
1637
+ return "[unserializable]";
1638
+ }
1639
+ }
1640
+ function estimateTextAndImageContentChars(content) {
1641
+ if (typeof content === "string") return content.length;
1642
+ let chars = 0;
1643
+ for (const block of content) chars += block.type === "text" ? block.text.length : ESTIMATED_IMAGE_CHARS;
1644
+ return chars;
1645
+ }
1646
+ function estimateTextTokens(text) {
1647
+ return Math.ceil(text.length / CHARS_PER_TOKEN);
1648
+ }
1649
+ function estimateTextAndImageContentTokens(content) {
1650
+ return Math.ceil(estimateTextAndImageContentChars(content) / CHARS_PER_TOKEN);
1651
+ }
1652
+ function estimateMessageTokens(message) {
1653
+ let chars = 0;
1654
+ if (message.role === "user") return estimateTextAndImageContentTokens(message.content);
1655
+ if (message.role === "toolResult") return estimateTextAndImageContentTokens(message.content);
1656
+ for (const block of message.content) if (block.type === "text") chars += block.text.length;
1657
+ else if (block.type === "thinking") chars += block.thinking.length;
1658
+ else chars += block.name.length + safeJsonStringify(block.arguments).length;
1659
+ return Math.ceil(chars / CHARS_PER_TOKEN);
1660
+ }
1661
+ function getLastAssistantUsageInfo(messages) {
1662
+ let latestPrefixTimestamp = Number.NEGATIVE_INFINITY;
1663
+ let usageInfo;
1664
+ for (let i = 0; i < messages.length; i++) {
1665
+ const message = messages[i];
1666
+ if (message.role === "assistant") {
1667
+ const assistant = message;
1668
+ if (assistant.timestamp >= latestPrefixTimestamp && assistant.stopReason !== "aborted" && assistant.stopReason !== "error" && calculateContextTokens(assistant.usage) > 0) usageInfo = {
1669
+ usage: assistant.usage,
1670
+ index: i
1671
+ };
1672
+ }
1673
+ latestPrefixTimestamp = Math.max(latestPrefixTimestamp, message.timestamp);
1674
+ }
1675
+ return usageInfo;
1676
+ }
1677
+ function estimateMessages(messages) {
1678
+ const usageInfo = getLastAssistantUsageInfo(messages);
1679
+ if (usageInfo) {
1680
+ const usageTokens = calculateContextTokens(usageInfo.usage);
1681
+ let trailingTokens = 0;
1682
+ for (let i = usageInfo.index + 1; i < messages.length; i++) trailingTokens += estimateMessageTokens(messages[i]);
1683
+ return {
1684
+ tokens: usageTokens + trailingTokens,
1685
+ usageTokens,
1686
+ trailingTokens,
1687
+ lastUsageIndex: usageInfo.index
1688
+ };
1689
+ }
1690
+ let tokens = 0;
1691
+ for (const message of messages) tokens += estimateMessageTokens(message);
1692
+ return {
1693
+ tokens,
1694
+ usageTokens: 0,
1695
+ trailingTokens: tokens,
1696
+ lastUsageIndex: null
1697
+ };
1698
+ }
1699
+ function estimateToolsTokens(tools) {
1700
+ if (!tools || tools.length === 0) return 0;
1701
+ return estimateTextTokens(safeJsonStringify(tools));
1702
+ }
1703
+ function isMessageArray(value) {
1704
+ return Array.isArray(value);
1705
+ }
1706
+ function estimateContextTokens(context) {
1707
+ if (isMessageArray(context)) return estimateMessages(context);
1708
+ const estimate = estimateMessages(context.messages);
1709
+ if (estimate.lastUsageIndex !== null) {
1710
+ const addedNames = new Set(context.messages.slice(estimate.lastUsageIndex + 1).filter((message) => message.role === "toolResult").flatMap((message) => message.addedToolNames ?? []));
1711
+ const addedToolTokens = estimateToolsTokens(context.tools?.filter((tool) => addedNames.has(tool.name)));
1712
+ return {
1713
+ tokens: estimate.tokens + addedToolTokens,
1714
+ usageTokens: estimate.usageTokens,
1715
+ trailingTokens: estimate.trailingTokens + addedToolTokens,
1716
+ lastUsageIndex: estimate.lastUsageIndex
1717
+ };
1718
+ }
1719
+ const prefixTokens = (context.systemPrompt ? estimateTextTokens(context.systemPrompt) : 0) + estimateToolsTokens(context.tools);
1720
+ return {
1721
+ tokens: estimate.tokens + prefixTokens,
1722
+ usageTokens: estimate.usageTokens,
1723
+ trailingTokens: estimate.trailingTokens + prefixTokens,
1724
+ lastUsageIndex: estimate.lastUsageIndex
1725
+ };
1726
+ }
1727
+ //#endregion
1728
+ //#region node_modules/@earendil-works/pi-ai/dist/api/simple-options.js
1729
+ const CONTEXT_SAFETY_TOKENS = 4096;
1730
+ const MIN_MAX_TOKENS = 1;
1731
+ function clampMaxTokensToContext(model, context, maxTokens) {
1732
+ if (model.contextWindow <= 0) return Math.max(MIN_MAX_TOKENS, maxTokens);
1733
+ const available = model.contextWindow - estimateContextTokens(context).tokens - CONTEXT_SAFETY_TOKENS;
1734
+ return Math.min(maxTokens, Math.max(MIN_MAX_TOKENS, available));
1735
+ }
1736
+ function buildBaseOptions(model, context, options, apiKey) {
1737
+ const samplingParams = model.samplingParams || options?.samplingParams ? {
1738
+ ...model.samplingParams,
1739
+ ...options?.samplingParams
1740
+ } : void 0;
1741
+ return {
1742
+ temperature: options?.temperature,
1743
+ samplingParams,
1744
+ maxTokens: clampMaxTokensToContext(model, context, options?.maxTokens ?? model.maxTokens),
1745
+ signal: options?.signal,
1746
+ telemetryContext: options?.telemetryContext,
1747
+ apiKey: apiKey || options?.apiKey,
1748
+ fetch: options?.fetch,
1749
+ transport: options?.transport,
1750
+ cacheRetention: options?.cacheRetention,
1751
+ sessionId: options?.sessionId,
1752
+ headers: options?.headers,
1753
+ onPayload: options?.onPayload,
1754
+ onResponse: options?.onResponse,
1755
+ timeoutMs: options?.timeoutMs,
1756
+ websocketConnectTimeoutMs: options?.websocketConnectTimeoutMs,
1757
+ maxRetries: options?.maxRetries,
1758
+ maxRetryDelayMs: options?.maxRetryDelayMs,
1759
+ metadata: options?.metadata,
1760
+ env: options?.env
1761
+ };
1762
+ }
1763
+ //#endregion
1764
+ //#region node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js
1765
+ const DEFAULT_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
1766
+ const JWT_CLAIM_PATH = "https://api.openai.com/auth";
1767
+ const DEFAULT_MAX_RETRIES = 0;
1768
+ const BASE_DELAY_MS = 1e3;
1769
+ const DEFAULT_MAX_RETRY_DELAY_MS = 6e4;
1770
+ const DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS = 15e3;
1771
+ const REQUEST_COMPRESSION_ZSTD_LEVEL = 3;
1772
+ const CODEX_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set([
1773
+ "openai",
1774
+ "openai-codex",
1775
+ "opencode"
1776
+ ]);
1777
+ const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
1778
+ const WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE = "websocket_connection_limit_reached";
1779
+ const PREVIOUS_RESPONSE_NOT_FOUND_CODE = "previous_response_not_found";
1780
+ const CODEX_RESPONSE_STATUSES = /* @__PURE__ */ new Set([
1781
+ "completed",
1782
+ "incomplete",
1783
+ "failed",
1784
+ "cancelled",
1785
+ "queued",
1786
+ "in_progress"
1787
+ ]);
1788
+ function assertSuccessfulOutput(output) {
1789
+ if (output.stopReason === "pending") throw new Error("Codex stream ended without a stop reason");
1790
+ if (output.stopReason === "error" || output.stopReason === "aborted") throw new Error(output.errorMessage || "An unknown error occurred");
1791
+ }
1792
+ function isTerminalRateLimitError(errorText) {
1793
+ return /GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing/i.test(errorText);
1794
+ }
1795
+ function isRetryableError(status, errorText) {
1796
+ if (status === 429 && isTerminalRateLimitError(errorText)) return false;
1797
+ if (status === 429 || status === 500 || status === 502 || status === 503 || status === 504) return true;
1798
+ return /rate.?limit|overloaded|service.?unavailable|upstream.?connect|connection.?refused/i.test(errorText);
1799
+ }
1800
+ function getRetryAfterDelayMs(headers) {
1801
+ const retryAfterMs = headers.get("retry-after-ms");
1802
+ if (retryAfterMs !== null) {
1803
+ const millis = Number(retryAfterMs);
1804
+ if (Number.isFinite(millis)) return Math.max(0, millis);
1805
+ }
1806
+ const retryAfter = headers.get("retry-after");
1807
+ if (!retryAfter) return;
1808
+ const seconds = Number(retryAfter);
1809
+ if (Number.isFinite(seconds)) return Math.max(0, seconds * 1e3);
1810
+ const date = Date.parse(retryAfter);
1811
+ if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
1812
+ }
1813
+ var RetryDelayExceededError = class extends Error {};
1814
+ function validateRetryDelayMs(delayMs, options) {
1815
+ const maxRetryDelayMs = options?.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;
1816
+ if (maxRetryDelayMs > 0 && delayMs > maxRetryDelayMs) throw new RetryDelayExceededError(`Server requested ${Math.ceil(delayMs / 1e3)}s retry delay (max: ${Math.ceil(maxRetryDelayMs / 1e3)}s)`);
1817
+ return delayMs;
1818
+ }
1819
+ function sleep(ms, signal) {
1820
+ return new Promise((resolve, reject) => {
1821
+ if (signal?.aborted) {
1822
+ reject(/* @__PURE__ */ new Error("Request was aborted"));
1823
+ return;
1824
+ }
1825
+ const timeout = setTimeout(resolve, ms);
1826
+ signal?.addEventListener("abort", () => {
1827
+ clearTimeout(timeout);
1828
+ reject(/* @__PURE__ */ new Error("Request was aborted"));
1829
+ });
1830
+ });
1831
+ }
1832
+ function normalizeTimeoutMs(value) {
1833
+ if (value === void 0) return void 0;
1834
+ if (!Number.isFinite(value) || value < 0) throw new Error(`Invalid timeoutMs: ${String(value)}`);
1835
+ return Math.floor(value);
1836
+ }
1837
+ function loadNodeZlib() {
1838
+ if (typeof process === "undefined" || !(process.versions?.node || process.versions?.bun)) return null;
1839
+ return process.getBuiltinModule?.("node:zlib") ?? null;
1840
+ }
1841
+ function compressRequestBodyZstd(bodyJson) {
1842
+ const zlib = loadNodeZlib();
1843
+ if (!zlib || typeof zlib.zstdCompressSync !== "function") return null;
1844
+ try {
1845
+ const compressed = zlib.zstdCompressSync(bodyJson, { params: { [zlib.constants.ZSTD_c_compressionLevel]: REQUEST_COMPRESSION_ZSTD_LEVEL } });
1846
+ return new Uint8Array(compressed.buffer, compressed.byteOffset, compressed.byteLength);
1847
+ } catch {
1848
+ return null;
1849
+ }
1850
+ }
1851
+ const stream = (model, context, options) => {
1852
+ const stream = new AssistantMessageEventStream();
1853
+ (async () => {
1854
+ const output = {
1855
+ role: "assistant",
1856
+ content: [],
1857
+ api: "openai-codex-responses",
1858
+ provider: model.provider,
1859
+ model: model.id,
1860
+ usage: {
1861
+ input: 0,
1862
+ output: 0,
1863
+ cacheRead: 0,
1864
+ cacheWrite: 0,
1865
+ totalTokens: 0,
1866
+ cost: {
1867
+ input: 0,
1868
+ output: 0,
1869
+ cacheRead: 0,
1870
+ cacheWrite: 0,
1871
+ total: 0
1872
+ }
1873
+ },
1874
+ stopReason: "pending",
1875
+ timestamp: Date.now()
1876
+ };
1877
+ try {
1878
+ const apiKey = options?.apiKey;
1879
+ if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
1880
+ const accountId = extractAccountId(apiKey);
1881
+ const grammarToolInputProperties = createGrammarToolInputProperties(context.tools, model.compat?.supportsOpenAIGrammarTools ?? false);
1882
+ const cacheSessionId = options?.cacheRetention === "none" ? void 0 : options?.sessionId;
1883
+ const codexSessionId = clampOpenAIPromptCacheKey(cacheSessionId);
1884
+ let body = buildRequestBody(model, context, options, codexSessionId, grammarToolInputProperties);
1885
+ const nextBody = await options?.onPayload?.(body, model);
1886
+ if (nextBody !== void 0) body = nextBody;
1887
+ const websocketRequestId = codexSessionId || uuidv7();
1888
+ const sseHeaders = buildSSEHeaders(model.headers, options?.headers, accountId, apiKey, codexSessionId);
1889
+ const websocketHeaders = buildWebSocketHeaders(model.headers, options?.headers, accountId, apiKey, websocketRequestId);
1890
+ const bodyJson = JSON.stringify(body);
1891
+ const httpTimeoutMs = normalizeTimeoutMs(options?.timeoutMs);
1892
+ const websocketConnectTimeoutMs = normalizeTimeoutMs(options?.websocketConnectTimeoutMs);
1893
+ const transport = options?.transport || "auto";
1894
+ let startEmitted = false;
1895
+ const websocketDisabledForSession = transport !== "sse" && isWebSocketSseFallbackActive(cacheSessionId);
1896
+ if (websocketDisabledForSession) recordWebSocketSseFallback(cacheSessionId);
1897
+ if (transport !== "sse" && !websocketDisabledForSession) {
1898
+ let websocketStarted = false;
1899
+ let retriedWebSocketConnectionLimit = false;
1900
+ let retriedMissingWebSocketContinuation = false;
1901
+ while (true) {
1902
+ websocketStarted = false;
1903
+ try {
1904
+ await processWebSocketStream(resolveCodexWebSocketUrl(model.baseUrl), body, websocketHeaders, output, stream, model, () => {
1905
+ websocketStarted = true;
1906
+ if (!startEmitted) {
1907
+ startEmitted = true;
1908
+ stream.push({
1909
+ type: "start",
1910
+ partial: output
1911
+ });
1912
+ }
1913
+ }, httpTimeoutMs, websocketConnectTimeoutMs, cacheSessionId, accountId, grammarToolInputProperties, options);
1914
+ if (options?.signal?.aborted) throw new Error("Request was aborted");
1915
+ assertSuccessfulOutput(output);
1916
+ stream.push({
1917
+ type: "done",
1918
+ reason: output.stopReason,
1919
+ message: output
1920
+ });
1921
+ stream.end();
1922
+ return;
1923
+ } catch (error) {
1924
+ const aborted = options?.signal?.aborted;
1925
+ const connectionLimitBeforeStart = !websocketStarted && isWebSocketConnectionLimitReachedError(error);
1926
+ const previousResponseNotFound = isPreviousResponseNotFoundError(error);
1927
+ if (!aborted && previousResponseNotFound && !retriedMissingWebSocketContinuation) {
1928
+ retriedMissingWebSocketContinuation = true;
1929
+ continue;
1930
+ }
1931
+ if (!aborted && connectionLimitBeforeStart && !retriedWebSocketConnectionLimit) {
1932
+ retriedWebSocketConnectionLimit = true;
1933
+ continue;
1934
+ }
1935
+ if (aborted || isCodexNonTransportError(error) && !connectionLimitBeforeStart) throw error;
1936
+ appendAssistantMessageDiagnostic(output, createAssistantMessageDiagnostic("provider_transport_failure", error, {
1937
+ configuredTransport: transport,
1938
+ fallbackTransport: websocketStarted ? void 0 : "sse",
1939
+ eventsEmitted: websocketStarted,
1940
+ phase: websocketStarted ? "after_message_stream_start" : "before_message_stream_start",
1941
+ requestBytes: new TextEncoder().encode(bodyJson).byteLength
1942
+ }));
1943
+ recordWebSocketFailure(cacheSessionId, error);
1944
+ if (websocketStarted) throw error;
1945
+ recordWebSocketSseFallback(cacheSessionId);
1946
+ break;
1947
+ }
1948
+ }
1949
+ }
1950
+ const compressedBody = compressRequestBodyZstd(bodyJson);
1951
+ if (compressedBody) sseHeaders.set("content-encoding", "zstd");
1952
+ const sseBody = compressedBody ?? bodyJson;
1953
+ let response;
1954
+ let lastError;
1955
+ const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;
1956
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1957
+ if (options?.signal?.aborted) throw new Error("Request was aborted");
1958
+ try {
1959
+ const headerTimeoutSignal = httpTimeoutMs !== void 0 && httpTimeoutMs > 0 ? AbortSignal.timeout(httpTimeoutMs) : void 0;
1960
+ const combinedSignal = combineAbortSignals([options?.signal, headerTimeoutSignal]);
1961
+ try {
1962
+ response = await (options?.fetch ?? globalThis.fetch)(resolveCodexUrl(model.baseUrl), {
1963
+ method: "POST",
1964
+ headers: sseHeaders,
1965
+ body: sseBody,
1966
+ signal: combinedSignal.signal
1967
+ });
1968
+ } catch (error) {
1969
+ if (headerTimeoutSignal?.aborted && !options?.signal?.aborted) throw new Error(`Codex SSE response headers timed out after ${httpTimeoutMs}ms`);
1970
+ throw error;
1971
+ } finally {
1972
+ combinedSignal.cleanup();
1973
+ }
1974
+ await options?.onResponse?.({
1975
+ status: response.status,
1976
+ headers: headersToRecord(response.headers)
1977
+ }, model);
1978
+ if (response.ok) break;
1979
+ const errorText = await response.text();
1980
+ if (attempt < maxRetries && isRetryableError(response.status, errorText)) {
1981
+ const retryAfterDelayMs = getRetryAfterDelayMs(response.headers);
1982
+ await sleep(retryAfterDelayMs === void 0 ? BASE_DELAY_MS * 2 ** attempt : validateRetryDelayMs(retryAfterDelayMs, options), options?.signal);
1983
+ continue;
1984
+ }
1985
+ const info = await parseErrorResponse(new Response(errorText, {
1986
+ status: response.status,
1987
+ statusText: response.statusText
1988
+ }));
1989
+ throw new Error(info.friendlyMessage || info.message);
1990
+ } catch (error) {
1991
+ if (error instanceof Error) {
1992
+ if (error.name === "AbortError" || error.message === "Request was aborted") throw new Error("Request was aborted");
1993
+ }
1994
+ lastError = error instanceof Error ? error : new Error(String(error));
1995
+ if (attempt < maxRetries && !(lastError instanceof RetryDelayExceededError) && !lastError.message.includes("usage limit")) {
1996
+ await sleep(BASE_DELAY_MS * 2 ** attempt, options?.signal);
1997
+ continue;
1998
+ }
1999
+ throw lastError;
2000
+ }
2001
+ }
2002
+ if (!response?.ok) throw lastError ?? /* @__PURE__ */ new Error("Failed after retries");
2003
+ if (!response.body) throw new Error("No response body");
2004
+ if (!startEmitted) {
2005
+ startEmitted = true;
2006
+ stream.push({
2007
+ type: "start",
2008
+ partial: output
2009
+ });
2010
+ }
2011
+ await processStream(response, output, stream, model, grammarToolInputProperties, options);
2012
+ if (options?.signal?.aborted) throw new Error("Request was aborted");
2013
+ assertSuccessfulOutput(output);
2014
+ stream.push({
2015
+ type: "done",
2016
+ reason: output.stopReason,
2017
+ message: output
2018
+ });
2019
+ stream.end();
2020
+ } catch (error) {
2021
+ for (const block of output.content) {
2022
+ delete block.partialJson;
2023
+ delete block.customInput;
2024
+ }
2025
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
2026
+ output.errorMessage = formatProviderError(normalizeProviderError(error));
2027
+ stream.push({
2028
+ type: "error",
2029
+ reason: output.stopReason,
2030
+ error: output
2031
+ });
2032
+ stream.end();
2033
+ }
2034
+ })();
2035
+ return stream;
2036
+ };
2037
+ const streamSimple = (model, context, options) => {
2038
+ const apiKey = options?.apiKey;
2039
+ if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
2040
+ const base = {
2041
+ ...buildBaseOptions(model, context, options, apiKey),
2042
+ toolChoice: options?.toolChoice
2043
+ };
2044
+ const clampedReasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : void 0;
2045
+ const reasoningEffort = clampedReasoning === "off" ? void 0 : clampedReasoning;
2046
+ return stream(model, context, {
2047
+ ...base,
2048
+ reasoningEffort
2049
+ });
2050
+ };
2051
+ function buildRequestBody(model, context, options, cacheSessionId, grammarToolInputProperties = createGrammarToolInputProperties(context.tools, model.compat?.supportsOpenAIGrammarTools ?? false)) {
2052
+ const supportsStrictMode = model.compat?.supportsStrictMode ?? true;
2053
+ const supportsOpenAIGrammarTools = model.compat?.supportsOpenAIGrammarTools ?? false;
2054
+ const deferredToolsMode = model.compat?.supportsAdditionalTools ? "additional-tools" : model.compat?.supportsToolSearch ? "tool-search" : void 0;
2055
+ const toolPlacement = splitDeferredTools(context, deferredToolsMode !== void 0);
2056
+ const messages = convertResponsesMessages(model, context, CODEX_TOOL_CALL_PROVIDERS, {
2057
+ includeSystemPrompt: false,
2058
+ grammarToolInputProperties,
2059
+ deferredTools: toolPlacement.deferred,
2060
+ deferredToolsMode,
2061
+ toolOptions: {
2062
+ strict: null,
2063
+ supportsStrictMode,
2064
+ supportsOpenAIGrammarTools
2065
+ }
2066
+ });
2067
+ const body = {
2068
+ model: model.id,
2069
+ store: false,
2070
+ stream: true,
2071
+ instructions: context.systemPrompt || "You are a helpful assistant.",
2072
+ input: messages,
2073
+ text: { verbosity: options?.textVerbosity || "low" },
2074
+ include: ["reasoning.encrypted_content"],
2075
+ prompt_cache_key: cacheSessionId,
2076
+ tool_choice: options?.toolChoice ?? "auto",
2077
+ parallel_tool_calls: true
2078
+ };
2079
+ if (options?.temperature !== void 0) body.temperature = options.temperature;
2080
+ if (options?.serviceTier !== void 0) body.service_tier = options.serviceTier;
2081
+ if (toolPlacement.immediate.length > 0) body.tools = convertResponsesTools(toolPlacement.immediate, {
2082
+ strict: null,
2083
+ supportsStrictMode,
2084
+ supportsOpenAIGrammarTools
2085
+ });
2086
+ if (options?.reasoningEffort !== void 0) {
2087
+ const effort = options.reasoningEffort === "none" ? model.thinkingLevelMap?.off ?? "none" : model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort;
2088
+ if (effort !== null) body.reasoning = {
2089
+ effort,
2090
+ summary: options.reasoningSummary ?? "auto"
2091
+ };
2092
+ }
2093
+ return body;
2094
+ }
2095
+ function getServiceTierCostMultiplier(model, serviceTier) {
2096
+ switch (serviceTier) {
2097
+ case "flex": return .5;
2098
+ case "priority": return model.id === "gpt-5.5" ? 2.5 : 2;
2099
+ default: return 1;
2100
+ }
2101
+ }
2102
+ function applyServiceTierPricing(usage, serviceTier, model) {
2103
+ const multiplier = getServiceTierCostMultiplier(model, serviceTier);
2104
+ if (multiplier === 1) return;
2105
+ usage.cost.input *= multiplier;
2106
+ usage.cost.output *= multiplier;
2107
+ usage.cost.cacheRead *= multiplier;
2108
+ usage.cost.cacheWrite *= multiplier;
2109
+ usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
2110
+ }
2111
+ function resolveCodexServiceTier(responseServiceTier, requestServiceTier) {
2112
+ if (responseServiceTier === "default" && (requestServiceTier === "flex" || requestServiceTier === "priority")) return requestServiceTier;
2113
+ return responseServiceTier ?? requestServiceTier;
2114
+ }
2115
+ function resolveCodexUrl(baseUrl) {
2116
+ const normalized = (baseUrl && baseUrl.trim().length > 0 ? baseUrl : DEFAULT_CODEX_BASE_URL).replace(/\/+$/, "");
2117
+ if (normalized.endsWith("/codex/responses")) return normalized;
2118
+ if (normalized.endsWith("/codex")) return `${normalized}/responses`;
2119
+ return `${normalized}/codex/responses`;
2120
+ }
2121
+ function resolveCodexWebSocketUrl(baseUrl) {
2122
+ const url = new URL(resolveCodexUrl(baseUrl));
2123
+ if (url.protocol === "https:") url.protocol = "wss:";
2124
+ if (url.protocol === "http:") url.protocol = "ws:";
2125
+ return url.toString();
2126
+ }
2127
+ async function processStream(response, output, stream, model, grammarToolInputProperties, options) {
2128
+ await processResponsesStream(mapCodexEvents(parseSSE(response, options?.signal), output), output, stream, model, {
2129
+ serviceTier: options?.serviceTier,
2130
+ grammarToolInputProperties,
2131
+ resolveServiceTier: resolveCodexServiceTier,
2132
+ applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model)
2133
+ });
2134
+ }
2135
+ var CodexApiError = class extends Error {
2136
+ code;
2137
+ payload;
2138
+ constructor(message, options) {
2139
+ super(message);
2140
+ this.name = "CodexApiError";
2141
+ this.code = options?.code;
2142
+ this.payload = options?.payload;
2143
+ this.cause = options?.cause;
2144
+ }
2145
+ };
2146
+ var CodexProtocolError = class extends Error {
2147
+ payload;
2148
+ constructor(message, options) {
2149
+ super(message);
2150
+ this.name = "CodexProtocolError";
2151
+ this.payload = options?.payload;
2152
+ this.cause = options?.cause;
2153
+ }
2154
+ };
2155
+ function isCodexNonTransportError(error) {
2156
+ return error instanceof CodexApiError || error instanceof CodexProtocolError;
2157
+ }
2158
+ function isWebSocketConnectionLimitReachedError(error) {
2159
+ return error instanceof CodexApiError && error.code === WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE;
2160
+ }
2161
+ function isPreviousResponseNotFoundError(error) {
2162
+ return error instanceof CodexApiError && error.code === PREVIOUS_RESPONSE_NOT_FOUND_CODE;
2163
+ }
2164
+ function extractCodexEventError(event) {
2165
+ const nested = event.error && typeof event.error === "object" ? event.error : void 0;
2166
+ return {
2167
+ code: typeof event.code === "string" ? event.code : typeof nested?.code === "string" ? nested.code : void 0,
2168
+ message: typeof event.message === "string" ? event.message : typeof nested?.message === "string" ? nested.message : void 0
2169
+ };
2170
+ }
2171
+ async function* mapCodexEvents(events, output) {
2172
+ for await (const event of events) {
2173
+ const type = typeof event.type === "string" ? event.type : void 0;
2174
+ if (!type) continue;
2175
+ if (type === "error") {
2176
+ const { code, message } = extractCodexEventError(event);
2177
+ throw new CodexApiError(`Codex error: ${message || code || JSON.stringify(event)}`, {
2178
+ code,
2179
+ payload: event
2180
+ });
2181
+ }
2182
+ if (type === "response.failed") {
2183
+ const response = event.response;
2184
+ const code = response?.error?.code;
2185
+ const message = response?.error?.message;
2186
+ throw new CodexApiError(message || "Codex response failed", {
2187
+ code,
2188
+ payload: event
2189
+ });
2190
+ }
2191
+ if (type === "response.done" || type === "response.completed" || type === "response.incomplete") {
2192
+ const response = event.response;
2193
+ if (typeof response?.end_turn === "boolean") output.endTurn = response.end_turn;
2194
+ const normalizedResponse = response ? {
2195
+ ...response,
2196
+ status: normalizeCodexStatus(response.status)
2197
+ } : response;
2198
+ yield {
2199
+ ...event,
2200
+ type: "response.completed",
2201
+ response: normalizedResponse
2202
+ };
2203
+ return;
2204
+ }
2205
+ yield event;
2206
+ }
2207
+ }
2208
+ function normalizeCodexStatus(status) {
2209
+ if (typeof status !== "string") return void 0;
2210
+ return CODEX_RESPONSE_STATUSES.has(status) ? status : void 0;
2211
+ }
2212
+ async function* parseSSE(response, signal) {
2213
+ if (!response.body) return;
2214
+ const reader = response.body.getReader();
2215
+ const decoder = new TextDecoder();
2216
+ let buffer = "";
2217
+ const onAbort = () => {
2218
+ reader.cancel().catch(() => {});
2219
+ };
2220
+ signal?.addEventListener("abort", onAbort, { once: true });
2221
+ try {
2222
+ while (true) {
2223
+ if (signal?.aborted) throw new Error("Request was aborted");
2224
+ const { done, value } = await reader.read();
2225
+ if (signal?.aborted) throw new Error("Request was aborted");
2226
+ buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });
2227
+ if (done && buffer.trim()) buffer += "\n\n";
2228
+ let idx = buffer.indexOf("\n\n");
2229
+ while (idx !== -1) {
2230
+ const chunk = buffer.slice(0, idx);
2231
+ buffer = buffer.slice(idx + 2);
2232
+ const dataLines = chunk.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim());
2233
+ if (dataLines.length > 0) {
2234
+ const data = dataLines.join("\n").trim();
2235
+ if (data && data !== "[DONE]") try {
2236
+ yield JSON.parse(data);
2237
+ } catch (cause) {
2238
+ throw new CodexProtocolError(`Invalid Codex SSE JSON: ${formatThrownValue(cause)}`, {
2239
+ cause,
2240
+ payload: data
2241
+ });
2242
+ }
2243
+ }
2244
+ idx = buffer.indexOf("\n\n");
2245
+ }
2246
+ if (done) break;
2247
+ }
2248
+ } finally {
2249
+ signal?.removeEventListener("abort", onAbort);
2250
+ try {
2251
+ await reader.cancel();
2252
+ } catch {}
2253
+ try {
2254
+ reader.releaseLock();
2255
+ } catch {}
2256
+ }
2257
+ }
2258
+ const OPENAI_BETA_RESPONSES_WEBSOCKETS = "responses_websockets=2026-02-06";
2259
+ const SESSION_WEBSOCKET_CACHE_TTL_MS = 300 * 1e3;
2260
+ const SESSION_WEBSOCKET_MAX_AGE_MS = 3300 * 1e3;
2261
+ const websocketSessionCache = /* @__PURE__ */ new Map();
2262
+ const websocketDebugStats = /* @__PURE__ */ new Map();
2263
+ const websocketSseFallbackSessions = /* @__PURE__ */ new Set();
2264
+ function getOrCreateWebSocketDebugStats(sessionId) {
2265
+ let stats = websocketDebugStats.get(sessionId);
2266
+ if (!stats) {
2267
+ stats = {
2268
+ requests: 0,
2269
+ connectionsCreated: 0,
2270
+ connectionsReused: 0,
2271
+ cachedContextRequests: 0,
2272
+ storeTrueRequests: 0,
2273
+ fullContextRequests: 0,
2274
+ deltaRequests: 0,
2275
+ lastInputItems: 0,
2276
+ websocketFailures: 0,
2277
+ sseFallbacks: 0
2278
+ };
2279
+ websocketDebugStats.set(sessionId, stats);
2280
+ }
2281
+ return stats;
2282
+ }
2283
+ function getOpenAICodexWebSocketDebugStats(sessionId) {
2284
+ const stats = websocketDebugStats.get(sessionId);
2285
+ return stats ? { ...stats } : void 0;
2286
+ }
2287
+ function resetOpenAICodexWebSocketDebugStats(sessionId) {
2288
+ if (sessionId) {
2289
+ websocketDebugStats.delete(sessionId);
2290
+ websocketSseFallbackSessions.delete(sessionId);
2291
+ return;
2292
+ }
2293
+ websocketDebugStats.clear();
2294
+ websocketSseFallbackSessions.clear();
2295
+ }
2296
+ function closeOpenAICodexWebSocketSessions(sessionId) {
2297
+ const closeEntry = (entry) => {
2298
+ if (entry.idleTimer) clearTimeout(entry.idleTimer);
2299
+ closeWebSocketSilently(entry.socket, 1e3, "debug_close");
2300
+ };
2301
+ if (sessionId) {
2302
+ for (const entry of websocketSessionCache.get(sessionId)?.values() ?? []) closeEntry(entry);
2303
+ websocketSessionCache.delete(sessionId);
2304
+ return;
2305
+ }
2306
+ for (const accountEntries of websocketSessionCache.values()) for (const entry of accountEntries.values()) closeEntry(entry);
2307
+ websocketSessionCache.clear();
2308
+ }
2309
+ registerSessionResourceCleanup(closeOpenAICodexWebSocketSessions);
2310
+ function isWebSocketSseFallbackActive(sessionId) {
2311
+ return sessionId ? websocketSseFallbackSessions.has(sessionId) : false;
2312
+ }
2313
+ function recordWebSocketSseFallback(sessionId) {
2314
+ if (!sessionId) return;
2315
+ const stats = getOrCreateWebSocketDebugStats(sessionId);
2316
+ stats.sseFallbacks++;
2317
+ stats.websocketFallbackActive = isWebSocketSseFallbackActive(sessionId);
2318
+ }
2319
+ function recordWebSocketFailure(sessionId, error) {
2320
+ if (!sessionId) return;
2321
+ websocketSseFallbackSessions.add(sessionId);
2322
+ const stats = getOrCreateWebSocketDebugStats(sessionId);
2323
+ stats.websocketFailures++;
2324
+ stats.lastWebSocketError = formatThrownValue(error);
2325
+ stats.websocketFallbackActive = true;
2326
+ }
2327
+ let _cachedWebsocket = null;
2328
+ async function getWebSocketConstructor(env) {
2329
+ if (!env && _cachedWebsocket) return _cachedWebsocket;
2330
+ if (typeof process !== "undefined" && process.versions?.bun) {
2331
+ const WebSocketWithProxy = class extends WebSocket {
2332
+ constructor(url, options) {
2333
+ let _opts = {};
2334
+ if (Array.isArray(options) || typeof options === "string") _opts = { protocols: options };
2335
+ else _opts = { ...options };
2336
+ const proxyUrl = resolveHttpProxyUrlForTarget(url.toString().replace(/^wss:/, "https:").replace(/^ws:/, "http:"), env);
2337
+ super(url, {
2338
+ ..._opts,
2339
+ ...proxyUrl ? { proxy: proxyUrl.toString() } : {}
2340
+ });
2341
+ }
2342
+ };
2343
+ if (!env) _cachedWebsocket = WebSocketWithProxy;
2344
+ return WebSocketWithProxy;
2345
+ }
2346
+ const ctor = globalThis.WebSocket;
2347
+ if (typeof ctor !== "function") return null;
2348
+ return ctor;
2349
+ }
2350
+ var WebSocketCloseError = class extends Error {
2351
+ code;
2352
+ reason;
2353
+ wasClean;
2354
+ constructor(message, options) {
2355
+ super(message);
2356
+ this.name = "WebSocketCloseError";
2357
+ this.code = options?.code;
2358
+ this.reason = options?.reason;
2359
+ this.wasClean = options?.wasClean;
2360
+ }
2361
+ };
2362
+ function getWebSocketReadyState(socket) {
2363
+ const readyState = socket.readyState;
2364
+ return typeof readyState === "number" ? readyState : void 0;
2365
+ }
2366
+ function isWebSocketReusable(socket) {
2367
+ const readyState = getWebSocketReadyState(socket);
2368
+ return readyState === void 0 || readyState === 1;
2369
+ }
2370
+ function isWebSocketSessionExpired(entry) {
2371
+ return Date.now() - entry.createdAt >= SESSION_WEBSOCKET_MAX_AGE_MS;
2372
+ }
2373
+ function closeWebSocketSilently(socket, code = 1e3, reason = "done") {
2374
+ try {
2375
+ socket.close(code, reason);
2376
+ } catch {}
2377
+ }
2378
+ function scheduleSessionWebSocketExpiry(sessionId, accountId, entry) {
2379
+ if (entry.idleTimer) clearTimeout(entry.idleTimer);
2380
+ entry.idleTimer = setTimeout(() => {
2381
+ if (entry.busy) return;
2382
+ closeWebSocketSilently(entry.socket, 1e3, "idle_timeout");
2383
+ const accountEntries = websocketSessionCache.get(sessionId);
2384
+ if (accountEntries?.get(accountId) === entry) accountEntries.delete(accountId);
2385
+ if (accountEntries?.size === 0) websocketSessionCache.delete(sessionId);
2386
+ }, SESSION_WEBSOCKET_CACHE_TTL_MS);
2387
+ }
2388
+ async function connectWebSocket(url, headers, signal, connectTimeoutMs = DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS, env) {
2389
+ const WebSocketCtor = await getWebSocketConstructor(env);
2390
+ if (!WebSocketCtor) throw new Error("WebSocket transport is not available in this runtime");
2391
+ const wsHeaders = headersToRecord(headers);
2392
+ delete wsHeaders["OpenAI-Beta"];
2393
+ return new Promise((resolve, reject) => {
2394
+ let settled = false;
2395
+ let timeout;
2396
+ let socket;
2397
+ try {
2398
+ socket = new WebSocketCtor(url, { headers: wsHeaders });
2399
+ } catch (error) {
2400
+ reject(error instanceof Error ? error : new Error(String(error)));
2401
+ return;
2402
+ }
2403
+ const cleanup = () => {
2404
+ if (timeout) {
2405
+ clearTimeout(timeout);
2406
+ timeout = void 0;
2407
+ }
2408
+ socket.removeEventListener("open", onOpen);
2409
+ socket.removeEventListener("error", onError);
2410
+ socket.removeEventListener("close", onClose);
2411
+ signal?.removeEventListener("abort", onAbort);
2412
+ };
2413
+ const fail = (error, closeReason) => {
2414
+ if (settled) return;
2415
+ settled = true;
2416
+ cleanup();
2417
+ if (closeReason) closeWebSocketSilently(socket, 1e3, closeReason);
2418
+ reject(error);
2419
+ };
2420
+ const onOpen = () => {
2421
+ if (settled) return;
2422
+ settled = true;
2423
+ cleanup();
2424
+ resolve(socket);
2425
+ };
2426
+ const onError = (event) => {
2427
+ fail(extractWebSocketError(event));
2428
+ };
2429
+ const onClose = (event) => {
2430
+ fail(extractWebSocketCloseError(event));
2431
+ };
2432
+ const onAbort = () => {
2433
+ fail(/* @__PURE__ */ new Error("Request was aborted"), "aborted");
2434
+ };
2435
+ socket.addEventListener("open", onOpen);
2436
+ socket.addEventListener("error", onError);
2437
+ socket.addEventListener("close", onClose);
2438
+ signal?.addEventListener("abort", onAbort);
2439
+ if (connectTimeoutMs > 0) timeout = setTimeout(() => {
2440
+ fail(/* @__PURE__ */ new Error(`WebSocket connect timeout after ${connectTimeoutMs}ms`), "connect_timeout");
2441
+ }, connectTimeoutMs);
2442
+ if (signal?.aborted) onAbort();
2443
+ });
2444
+ }
2445
+ async function acquireWebSocket(url, headers, sessionId, accountId, signal, connectTimeoutMs, env) {
2446
+ if (!sessionId) {
2447
+ const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs, env);
2448
+ return {
2449
+ socket,
2450
+ reused: false,
2451
+ release: () => closeWebSocketSilently(socket)
2452
+ };
2453
+ }
2454
+ let accountEntries = websocketSessionCache.get(sessionId);
2455
+ const cached = accountEntries?.get(accountId);
2456
+ if (cached) {
2457
+ if (cached.idleTimer) {
2458
+ clearTimeout(cached.idleTimer);
2459
+ cached.idleTimer = void 0;
2460
+ }
2461
+ if (!cached.busy && isWebSocketSessionExpired(cached)) {
2462
+ closeWebSocketSilently(cached.socket, 1e3, "connection_age_limit");
2463
+ accountEntries?.delete(accountId);
2464
+ if (accountEntries?.size === 0) websocketSessionCache.delete(sessionId);
2465
+ } else if (!cached.busy && isWebSocketReusable(cached.socket)) {
2466
+ cached.busy = true;
2467
+ return {
2468
+ socket: cached.socket,
2469
+ entry: cached,
2470
+ reused: true,
2471
+ release: ({ keep } = {}) => {
2472
+ if (!keep || !isWebSocketReusable(cached.socket)) {
2473
+ closeWebSocketSilently(cached.socket);
2474
+ const currentEntries = websocketSessionCache.get(sessionId);
2475
+ if (currentEntries?.get(accountId) === cached) currentEntries.delete(accountId);
2476
+ if (currentEntries?.size === 0) websocketSessionCache.delete(sessionId);
2477
+ return;
2478
+ }
2479
+ cached.busy = false;
2480
+ scheduleSessionWebSocketExpiry(sessionId, accountId, cached);
2481
+ }
2482
+ };
2483
+ }
2484
+ if (cached.busy) {
2485
+ const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs, env);
2486
+ return {
2487
+ socket,
2488
+ reused: false,
2489
+ release: () => {
2490
+ closeWebSocketSilently(socket);
2491
+ }
2492
+ };
2493
+ }
2494
+ if (!isWebSocketReusable(cached.socket)) {
2495
+ closeWebSocketSilently(cached.socket);
2496
+ accountEntries?.delete(accountId);
2497
+ if (accountEntries?.size === 0) websocketSessionCache.delete(sessionId);
2498
+ }
2499
+ }
2500
+ const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs, env);
2501
+ const entry = {
2502
+ socket,
2503
+ busy: true,
2504
+ createdAt: Date.now()
2505
+ };
2506
+ accountEntries = websocketSessionCache.get(sessionId);
2507
+ if (!accountEntries) {
2508
+ accountEntries = /* @__PURE__ */ new Map();
2509
+ websocketSessionCache.set(sessionId, accountEntries);
2510
+ }
2511
+ accountEntries.set(accountId, entry);
2512
+ return {
2513
+ socket,
2514
+ entry,
2515
+ reused: false,
2516
+ release: ({ keep } = {}) => {
2517
+ if (!keep || !isWebSocketReusable(entry.socket)) {
2518
+ closeWebSocketSilently(entry.socket);
2519
+ if (entry.idleTimer) clearTimeout(entry.idleTimer);
2520
+ const currentEntries = websocketSessionCache.get(sessionId);
2521
+ if (currentEntries?.get(accountId) === entry) currentEntries.delete(accountId);
2522
+ if (currentEntries?.size === 0) websocketSessionCache.delete(sessionId);
2523
+ return;
2524
+ }
2525
+ entry.busy = false;
2526
+ scheduleSessionWebSocketExpiry(sessionId, accountId, entry);
2527
+ }
2528
+ };
2529
+ }
2530
+ function extractWebSocketError(event) {
2531
+ if (event && typeof event === "object") {
2532
+ const message = "message" in event ? event.message : void 0;
2533
+ if (typeof message === "string" && message.length > 0) return new Error(message);
2534
+ const nestedError = "error" in event ? event.error : void 0;
2535
+ if (nestedError instanceof Error && nestedError.message.length > 0) return nestedError;
2536
+ if (nestedError && typeof nestedError === "object" && "message" in nestedError) {
2537
+ const nestedMessage = nestedError.message;
2538
+ if (typeof nestedMessage === "string" && nestedMessage.length > 0) return new Error(nestedMessage);
2539
+ }
2540
+ }
2541
+ return /* @__PURE__ */ new Error("WebSocket error");
2542
+ }
2543
+ function extractWebSocketCloseError(event) {
2544
+ if (event && typeof event === "object") {
2545
+ const code = "code" in event ? event.code : void 0;
2546
+ const reason = "reason" in event ? event.reason : void 0;
2547
+ const wasClean = "wasClean" in event ? event.wasClean : void 0;
2548
+ const codeText = typeof code === "number" ? ` ${code}` : "";
2549
+ let reasonText = typeof reason === "string" && reason.length > 0 ? ` ${reason}` : "";
2550
+ if (!reasonText && code === WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE) reasonText = " message too big";
2551
+ return new WebSocketCloseError(`WebSocket closed${codeText}${reasonText}`.trim(), {
2552
+ code: typeof code === "number" ? code : void 0,
2553
+ reason: typeof reason === "string" && reason.length > 0 ? reason : void 0,
2554
+ wasClean: typeof wasClean === "boolean" ? wasClean : void 0
2555
+ });
2556
+ }
2557
+ return /* @__PURE__ */ new Error("WebSocket closed");
2558
+ }
2559
+ async function decodeWebSocketData(data) {
2560
+ if (typeof data === "string") return data;
2561
+ if (data instanceof ArrayBuffer) return new TextDecoder().decode(new Uint8Array(data));
2562
+ if (ArrayBuffer.isView(data)) {
2563
+ const view = data;
2564
+ return new TextDecoder().decode(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
2565
+ }
2566
+ if (data && typeof data === "object" && "arrayBuffer" in data) {
2567
+ const arrayBuffer = await data.arrayBuffer();
2568
+ return new TextDecoder().decode(new Uint8Array(arrayBuffer));
2569
+ }
2570
+ return null;
2571
+ }
2572
+ async function* parseWebSocket(socket, signal, idleTimeoutMs) {
2573
+ const queue = [];
2574
+ let pending = null;
2575
+ let done = false;
2576
+ let failed = null;
2577
+ let sawCompletion = false;
2578
+ const wake = () => {
2579
+ if (!pending) return;
2580
+ const resolve = pending;
2581
+ pending = null;
2582
+ resolve();
2583
+ };
2584
+ const onMessage = (event) => {
2585
+ (async () => {
2586
+ let text = null;
2587
+ try {
2588
+ if (!event || typeof event !== "object" || !("data" in event)) return;
2589
+ text = await decodeWebSocketData(event.data);
2590
+ if (!text) return;
2591
+ const parsed = JSON.parse(text);
2592
+ const type = typeof parsed.type === "string" ? parsed.type : "";
2593
+ if (type === "response.completed" || type === "response.done" || type === "response.incomplete") {
2594
+ sawCompletion = true;
2595
+ done = true;
2596
+ }
2597
+ queue.push(parsed);
2598
+ wake();
2599
+ } catch (cause) {
2600
+ failed = new CodexProtocolError(`Invalid Codex WebSocket JSON: ${formatThrownValue(cause)}`, {
2601
+ cause,
2602
+ payload: text
2603
+ });
2604
+ done = true;
2605
+ wake();
2606
+ }
2607
+ })();
2608
+ };
2609
+ const onError = (event) => {
2610
+ failed = extractWebSocketError(event);
2611
+ done = true;
2612
+ wake();
2613
+ };
2614
+ const onClose = (event) => {
2615
+ if (sawCompletion) {
2616
+ done = true;
2617
+ wake();
2618
+ return;
2619
+ }
2620
+ if (!failed) failed = extractWebSocketCloseError(event);
2621
+ done = true;
2622
+ wake();
2623
+ };
2624
+ const onAbort = () => {
2625
+ failed = /* @__PURE__ */ new Error("Request was aborted");
2626
+ done = true;
2627
+ wake();
2628
+ };
2629
+ socket.addEventListener("message", onMessage);
2630
+ socket.addEventListener("error", onError);
2631
+ socket.addEventListener("close", onClose);
2632
+ signal?.addEventListener("abort", onAbort);
2633
+ try {
2634
+ while (true) {
2635
+ if (signal?.aborted) throw new Error("Request was aborted");
2636
+ if (queue.length > 0) {
2637
+ yield queue.shift();
2638
+ continue;
2639
+ }
2640
+ if (done) break;
2641
+ let timeout;
2642
+ await new Promise((resolve, reject) => {
2643
+ pending = resolve;
2644
+ if (idleTimeoutMs !== void 0 && idleTimeoutMs > 0) timeout = setTimeout(() => {
2645
+ const error = /* @__PURE__ */ new Error(`WebSocket idle timeout after ${idleTimeoutMs}ms`);
2646
+ failed = error;
2647
+ done = true;
2648
+ pending = null;
2649
+ closeWebSocketSilently(socket, 1e3, "idle_timeout");
2650
+ reject(error);
2651
+ }, idleTimeoutMs);
2652
+ }).finally(() => {
2653
+ if (timeout) clearTimeout(timeout);
2654
+ });
2655
+ }
2656
+ if (failed) throw failed;
2657
+ if (!sawCompletion) throw new Error("WebSocket stream closed before response.completed");
2658
+ } finally {
2659
+ socket.removeEventListener("message", onMessage);
2660
+ socket.removeEventListener("error", onError);
2661
+ socket.removeEventListener("close", onClose);
2662
+ signal?.removeEventListener("abort", onAbort);
2663
+ }
2664
+ }
2665
+ function requestBodyWithoutInput(body) {
2666
+ const { input: _input, previous_response_id: _previousResponseId, ...rest } = body;
2667
+ return rest;
2668
+ }
2669
+ function responseInputsEqual(a, b) {
2670
+ return JSON.stringify(a ?? []) === JSON.stringify(b ?? []);
2671
+ }
2672
+ function requestBodiesMatchExceptInput(a, b) {
2673
+ return JSON.stringify(requestBodyWithoutInput(a)) === JSON.stringify(requestBodyWithoutInput(b));
2674
+ }
2675
+ function getCachedWebSocketInputDelta(body, continuation) {
2676
+ if (!requestBodiesMatchExceptInput(body, continuation.lastRequestBody)) return;
2677
+ const currentInput = body.input ?? [];
2678
+ const baseline = [...continuation.lastRequestBody.input ?? [], ...continuation.lastResponseItems];
2679
+ if (currentInput.length < baseline.length) return;
2680
+ if (!responseInputsEqual(currentInput.slice(0, baseline.length), baseline)) return;
2681
+ return currentInput.slice(baseline.length);
2682
+ }
2683
+ function buildCachedWebSocketRequestBody(entry, body) {
2684
+ const continuation = entry.continuation;
2685
+ if (!continuation) return body;
2686
+ const delta = getCachedWebSocketInputDelta(body, continuation);
2687
+ if (!delta || !continuation.lastResponseId) {
2688
+ entry.continuation = void 0;
2689
+ return body;
2690
+ }
2691
+ return {
2692
+ ...body,
2693
+ previous_response_id: continuation.lastResponseId,
2694
+ input: delta
2695
+ };
2696
+ }
2697
+ async function* startWebSocketOutputOnFirstEvent(events, onStart) {
2698
+ let started = false;
2699
+ for await (const event of events) {
2700
+ if (!started) {
2701
+ started = true;
2702
+ onStart();
2703
+ }
2704
+ yield event;
2705
+ }
2706
+ }
2707
+ async function processWebSocketStream(url, body, headers, output, stream, model, onStart, idleTimeoutMs, websocketConnectTimeoutMs, cacheSessionId, accountId, grammarToolInputProperties, options) {
2708
+ const { socket, entry, reused, release } = await acquireWebSocket(url, headers, cacheSessionId, accountId, options?.signal, websocketConnectTimeoutMs, options?.env);
2709
+ let keepConnection = true;
2710
+ const useCachedContext = options?.transport === "websocket-cached" || options?.transport === "auto";
2711
+ const fullBody = body;
2712
+ const requestBody = useCachedContext && entry ? buildCachedWebSocketRequestBody(entry, fullBody) : fullBody;
2713
+ const stats = cacheSessionId ? getOrCreateWebSocketDebugStats(cacheSessionId) : void 0;
2714
+ if (stats) {
2715
+ stats.requests++;
2716
+ if (reused) stats.connectionsReused++;
2717
+ else stats.connectionsCreated++;
2718
+ if (useCachedContext) stats.cachedContextRequests++;
2719
+ if (requestBody.store === true) stats.storeTrueRequests++;
2720
+ stats.lastInputItems = requestBody.input?.length ?? 0;
2721
+ if (requestBody.previous_response_id) {
2722
+ stats.deltaRequests++;
2723
+ stats.lastDeltaInputItems = requestBody.input?.length ?? 0;
2724
+ stats.lastPreviousResponseId = requestBody.previous_response_id;
2725
+ } else {
2726
+ stats.fullContextRequests++;
2727
+ stats.lastDeltaInputItems = void 0;
2728
+ stats.lastPreviousResponseId = void 0;
2729
+ }
2730
+ }
2731
+ try {
2732
+ socket.send(JSON.stringify({
2733
+ type: "response.create",
2734
+ ...requestBody
2735
+ }));
2736
+ await processResponsesStream(startWebSocketOutputOnFirstEvent(mapCodexEvents(parseWebSocket(socket, options?.signal, idleTimeoutMs), output), onStart), output, stream, model, {
2737
+ serviceTier: options?.serviceTier,
2738
+ grammarToolInputProperties,
2739
+ resolveServiceTier: resolveCodexServiceTier,
2740
+ applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model)
2741
+ });
2742
+ if (options?.signal?.aborted) keepConnection = false;
2743
+ else if (useCachedContext && entry && output.responseId) {
2744
+ const responseItems = convertResponsesMessages(model, { messages: [output] }, CODEX_TOOL_CALL_PROVIDERS, {
2745
+ includeSystemPrompt: false,
2746
+ grammarToolInputProperties
2747
+ }).filter((item) => item.type !== "function_call_output" && item.type !== "custom_tool_call_output");
2748
+ entry.continuation = {
2749
+ lastRequestBody: fullBody,
2750
+ lastResponseId: output.responseId,
2751
+ lastResponseItems: responseItems
2752
+ };
2753
+ }
2754
+ } catch (error) {
2755
+ if (entry) entry.continuation = void 0;
2756
+ keepConnection = false;
2757
+ throw error;
2758
+ } finally {
2759
+ release({ keep: keepConnection });
2760
+ }
2761
+ }
2762
+ async function parseErrorResponse(response) {
2763
+ const raw = await response.text();
2764
+ let message = raw || response.statusText || "Request failed";
2765
+ let friendlyMessage;
2766
+ try {
2767
+ const err = JSON.parse(raw)?.error;
2768
+ if (err) {
2769
+ const code = err.code || err.type || "";
2770
+ if (/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(code) || response.status === 429) {
2771
+ const plan = err.plan_type ? ` (${err.plan_type.toLowerCase()} plan)` : "";
2772
+ const mins = err.resets_at ? Math.max(0, Math.round((err.resets_at * 1e3 - Date.now()) / 6e4)) : void 0;
2773
+ friendlyMessage = `You have hit your ChatGPT usage limit${plan}.${mins !== void 0 ? ` Try again in ~${mins} min.` : ""}`.trim();
2774
+ }
2775
+ message = err.message || friendlyMessage || message;
2776
+ }
2777
+ } catch {}
2778
+ return {
2779
+ message,
2780
+ friendlyMessage
2781
+ };
2782
+ }
2783
+ function extractAccountId(token) {
2784
+ try {
2785
+ const parts = token.split(".");
2786
+ if (parts.length !== 3) throw new Error("Invalid token");
2787
+ const accountId = JSON.parse(atob(parts[1]))?.[JWT_CLAIM_PATH]?.chatgpt_account_id;
2788
+ if (!accountId) throw new Error("No account ID in token");
2789
+ return accountId;
2790
+ } catch {
2791
+ throw new Error("Failed to extract accountId from token");
2792
+ }
2793
+ }
2794
+ function buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token) {
2795
+ const headers = new Headers(initHeaders);
2796
+ for (const [key, value] of Object.entries(additionalHeaders || {})) if (value === null) headers.delete(key);
2797
+ else headers.set(key, value);
2798
+ headers.set("Authorization", `Bearer ${token}`);
2799
+ headers.set("chatgpt-account-id", accountId);
2800
+ headers.set("originator", "pi");
2801
+ headers.set("User-Agent", getPiUserAgent());
2802
+ return headers;
2803
+ }
2804
+ function buildSSEHeaders(initHeaders, additionalHeaders, accountId, token, sessionId) {
2805
+ const headers = buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token);
2806
+ headers.set("OpenAI-Beta", "responses=experimental");
2807
+ headers.set("accept", "text/event-stream");
2808
+ headers.set("content-type", "application/json");
2809
+ if (sessionId) {
2810
+ headers.set("session-id", sessionId);
2811
+ headers.set("x-client-request-id", sessionId);
2812
+ }
2813
+ return headers;
2814
+ }
2815
+ function buildWebSocketHeaders(initHeaders, additionalHeaders, accountId, token, requestId) {
2816
+ const headers = buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token);
2817
+ headers.delete("accept");
2818
+ headers.delete("content-type");
2819
+ headers.delete("OpenAI-Beta");
2820
+ headers.delete("openai-beta");
2821
+ headers.set("OpenAI-Beta", OPENAI_BETA_RESPONSES_WEBSOCKETS);
2822
+ headers.set("x-client-request-id", requestId);
2823
+ headers.set("session-id", requestId);
2824
+ return headers;
2825
+ }
2826
+ //#endregion
2827
+ export { closeOpenAICodexWebSocketSessions, getOpenAICodexWebSocketDebugStats, resetOpenAICodexWebSocketDebugStats, stream, streamSimple };