@prestyj/ai 4.3.210 → 4.3.237

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,18 @@
1
1
  // src/errors.ts
2
+ function readHeader(headers, ...names) {
3
+ if (!headers) return void 0;
4
+ const getter = typeof headers.get === "function" ? (name) => headers.get(name) ?? void 0 : typeof headers === "object" ? (name) => {
5
+ const rec = headers;
6
+ const value = rec[name] ?? rec[name.toLowerCase()];
7
+ return typeof value === "string" ? value : void 0;
8
+ } : void 0;
9
+ if (!getter) return void 0;
10
+ for (const name of names) {
11
+ const value = getter(name);
12
+ if (value != null) return value;
13
+ }
14
+ return void 0;
15
+ }
2
16
  var EZCoderAIError = class extends Error {
3
17
  source;
4
18
  requestId;
@@ -14,6 +28,8 @@ var EZCoderAIError = class extends Error {
14
28
  var ProviderError = class extends EZCoderAIError {
15
29
  provider;
16
30
  statusCode;
31
+ /** Unix seconds when a usage/rate limit resets, when the provider reports it. */
32
+ resetsAt;
17
33
  constructor(provider, message, options) {
18
34
  super(message, {
19
35
  source: "provider",
@@ -24,6 +40,7 @@ var ProviderError = class extends EZCoderAIError {
24
40
  this.name = "ProviderError";
25
41
  this.provider = provider;
26
42
  this.statusCode = options?.statusCode;
43
+ this.resetsAt = options?.resetsAt;
27
44
  }
28
45
  };
29
46
  var PROVIDER_DISPLAY = {
@@ -44,10 +61,36 @@ var PROVIDER_STATUS_URL = {
44
61
  function providerDisplayName(provider) {
45
62
  return PROVIDER_DISPLAY[provider] ?? provider;
46
63
  }
64
+ function isUsageLimitError(err) {
65
+ if (!(err instanceof Error)) return false;
66
+ return /usage limit reached/i.test(err.message);
67
+ }
68
+ function formatResetTime(resetsAt) {
69
+ const when = new Date(resetsAt * 1e3);
70
+ const sameDay = when.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
71
+ return sameDay ? when.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" }) : when.toLocaleString(void 0, {
72
+ weekday: "short",
73
+ hour: "numeric",
74
+ minute: "2-digit"
75
+ });
76
+ }
47
77
  function formatError(err) {
48
78
  if (err instanceof ProviderError) {
49
79
  const name = providerDisplayName(err.provider);
50
80
  const cleanMessage = cleanProviderMessage(err.message);
81
+ if (isUsageLimitError(err)) {
82
+ const resetClause = err.resetsAt ? ` It resets at ${formatResetTime(err.resetsAt)}.` : "";
83
+ return {
84
+ headline: `${name} usage limit reached.`,
85
+ source: "provider",
86
+ message: `Your ${name} usage is finished.${resetClause}`,
87
+ provider: err.provider,
88
+ statusCode: err.statusCode,
89
+ ...err.requestId ? { requestId: err.requestId } : {},
90
+ ...err.resetsAt ? { resetsAt: err.resetsAt } : {},
91
+ guidance: "Try again once it's back. Your conversation is preserved."
92
+ };
93
+ }
51
94
  return {
52
95
  headline: `${name} returned an error.`,
53
96
  source: "provider",
@@ -271,6 +314,9 @@ function zodToJsonSchema(schema) {
271
314
  const { $schema: _schema, ...rest } = jsonSchema;
272
315
  return normalizeRootForAnthropic(rest);
273
316
  }
317
+ function resolveToolSchema(tool) {
318
+ return tool.rawInputSchema ?? zodToJsonSchema(tool.parameters);
319
+ }
274
320
  function normalizeRootForAnthropic(schema) {
275
321
  const branches = schema.oneOf ?? schema.anyOf;
276
322
  if (!branches || branches.length === 0) {
@@ -327,6 +373,61 @@ function normalizeRootForAnthropic(schema) {
327
373
  }
328
374
 
329
375
  // src/providers/transform.ts
376
+ function hasValidThinkingSignature(part) {
377
+ return typeof part.signature === "string" && part.signature.trim().length > 0;
378
+ }
379
+ function isRawThinking(part) {
380
+ if (part.type !== "raw") return false;
381
+ const t = part.data.type;
382
+ return t === "thinking" || t === "redacted_thinking";
383
+ }
384
+ function isPositionSensitiveThinking(part) {
385
+ if (part.type === "thinking") return hasValidThinkingSignature(part);
386
+ return isRawThinking(part);
387
+ }
388
+ function toAnthropicAssistantPart(part, idMap) {
389
+ if (part.type === "text") return { type: "text", text: part.text };
390
+ if (part.type === "thinking") {
391
+ const sig = part.signature;
392
+ return sig && sig.trim().length > 0 ? { type: "thinking", thinking: part.text, signature: sig } : { type: "text", text: part.text };
393
+ }
394
+ if (part.type === "tool_call")
395
+ return {
396
+ type: "tool_use",
397
+ id: remapAnthropicToolCallId(part.id, idMap),
398
+ name: part.name,
399
+ input: part.args
400
+ };
401
+ if (part.type === "server_tool_call")
402
+ return {
403
+ type: "server_tool_use",
404
+ id: part.id,
405
+ name: part.name,
406
+ input: part.input
407
+ };
408
+ if (part.type === "server_tool_result")
409
+ return part.data;
410
+ if (part.type === "raw") return part.data;
411
+ return null;
412
+ }
413
+ function toAnthropicAssistantContent(content, isLatest, idMap) {
414
+ if (!isLatest) {
415
+ return content.filter((part) => {
416
+ if (part.type === "thinking" || isRawThinking(part)) return false;
417
+ if (part.type === "text" && !part.text) return false;
418
+ return true;
419
+ }).map((part) => toAnthropicAssistantPart(part, idMap)).filter((b) => b !== null);
420
+ }
421
+ const lastThinkingIdx = content.reduce(
422
+ (last, part, idx) => isPositionSensitiveThinking(part) ? idx : last,
423
+ -1
424
+ );
425
+ return content.filter((part, idx) => {
426
+ if (part.type === "thinking" && !hasValidThinkingSignature(part) && !part.text) return false;
427
+ if (part.type === "text" && !part.text && idx > lastThinkingIdx) return false;
428
+ return true;
429
+ }).map((part) => toAnthropicAssistantPart(part, idMap)).filter((b) => b !== null);
430
+ }
330
431
  var NON_VISION_USER_IMAGE_PLACEHOLDER = "(image omitted: model does not support images)";
331
432
  var NON_VISION_TOOL_IMAGE_PLACEHOLDER = "(tool image omitted: model does not support images)";
332
433
  function stripImages(content, placeholder) {
@@ -403,7 +504,13 @@ function toAnthropicMessages(messages, cacheControl) {
403
504
  let systemText;
404
505
  const out = [];
405
506
  const idMap = /* @__PURE__ */ new Map();
507
+ const lastAssistantIdx = messages.reduce(
508
+ (last, m, i) => m.role === "assistant" ? i : last,
509
+ -1
510
+ );
511
+ let msgIdx = -1;
406
512
  for (const msg of messages) {
513
+ msgIdx++;
407
514
  if (msg.role === "system") {
408
515
  systemText = msg.content;
409
516
  continue;
@@ -426,33 +533,7 @@ function toAnthropicMessages(messages, cacheControl) {
426
533
  continue;
427
534
  }
428
535
  if (msg.role === "assistant") {
429
- const content = typeof msg.content === "string" ? msg.content : msg.content.filter((part) => {
430
- if (part.type === "thinking" && !part.signature) return false;
431
- if (part.type === "text" && !part.text) return false;
432
- return true;
433
- }).map((part) => {
434
- if (part.type === "text") return { type: "text", text: part.text };
435
- if (part.type === "thinking")
436
- return { type: "thinking", thinking: part.text, signature: part.signature };
437
- if (part.type === "tool_call")
438
- return {
439
- type: "tool_use",
440
- id: remapAnthropicToolCallId(part.id, idMap),
441
- name: part.name,
442
- input: part.args
443
- };
444
- if (part.type === "server_tool_call")
445
- return {
446
- type: "server_tool_use",
447
- id: part.id,
448
- name: part.name,
449
- input: part.input
450
- };
451
- if (part.type === "server_tool_result")
452
- return part.data;
453
- if (part.type === "raw") return part.data;
454
- return null;
455
- }).filter(Boolean);
536
+ const content = typeof msg.content === "string" ? msg.content : toAnthropicAssistantContent(msg.content, msgIdx === lastAssistantIdx, idMap);
456
537
  if (Array.isArray(content) && content.length === 0) continue;
457
538
  out.push({ role: "assistant", content });
458
539
  continue;
@@ -538,13 +619,13 @@ function toAnthropicToolChoice(choice) {
538
619
  if (choice === "required") return { type: "any" };
539
620
  return { type: "tool", name: choice.name };
540
621
  }
541
- function supportsAdaptiveThinking(model) {
542
- return /opus-4-7|opus-4-6|sonnet-4-6/.test(model);
622
+ function isAdaptiveThinkingModel(model) {
623
+ return /opus-4[-.]8|opus-4[-.]7|opus-4[-.]6|sonnet-4[-.]6/.test(model);
543
624
  }
544
625
  function toAnthropicThinking(level, maxTokens, model) {
545
- if (supportsAdaptiveThinking(model)) {
546
- let effort = level === "xhigh" ? "max" : level;
547
- if (effort === "max" && !model.includes("opus")) {
626
+ if (isAdaptiveThinkingModel(model)) {
627
+ let effort = level;
628
+ if (effort === "xhigh" && !/opus-4-8|opus-4-7/.test(model)) {
548
629
  effort = "high";
549
630
  }
550
631
  return {
@@ -553,7 +634,7 @@ function toAnthropicThinking(level, maxTokens, model) {
553
634
  outputConfig: { effort }
554
635
  };
555
636
  }
556
- const effectiveLevel = level === "xhigh" ? "high" : level;
637
+ const effectiveLevel = level === "xhigh" || level === "max" ? "high" : level;
557
638
  const budgetMap = {
558
639
  low: Math.max(1024, Math.floor(maxTokens * 0.25)),
559
640
  medium: Math.max(2048, Math.floor(maxTokens * 0.5)),
@@ -676,7 +757,7 @@ function toOpenAITools(tools) {
676
757
  function: {
677
758
  name: tool.name,
678
759
  description: tool.description,
679
- parameters: tool.rawInputSchema ?? zodToJsonSchema(tool.parameters)
760
+ parameters: resolveToolSchema(tool)
680
761
  }
681
762
  }));
682
763
  }
@@ -687,7 +768,7 @@ function toOpenAIToolChoice(choice) {
687
768
  return { type: "function", function: { name: choice.name } };
688
769
  }
689
770
  function toOpenAIReasoningEffort(level, _model) {
690
- return level;
771
+ return level === "max" ? "xhigh" : level;
691
772
  }
692
773
  function normalizeAnthropicStopReason(reason) {
693
774
  switch (reason) {
@@ -718,10 +799,22 @@ function normalizeOpenAIStopReason(reason) {
718
799
  }
719
800
  }
720
801
 
721
- // src/providers/anthropic.ts
802
+ // src/utils/json.ts
722
803
  function isJsonObject(value) {
723
804
  return value != null && typeof value === "object" && !Array.isArray(value);
724
805
  }
806
+ function parseToolArguments(argsJson) {
807
+ if (!argsJson) return {};
808
+ try {
809
+ const parsed = JSON.parse(argsJson);
810
+ const unwrapped = typeof parsed === "string" ? JSON.parse(parsed) : parsed;
811
+ return isJsonObject(unwrapped) ? unwrapped : {};
812
+ } catch {
813
+ return {};
814
+ }
815
+ }
816
+
817
+ // src/providers/anthropic.ts
725
818
  function createClient(options) {
726
819
  const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
727
820
  return new Anthropic({
@@ -814,7 +907,7 @@ async function* runStream(options) {
814
907
  })(),
815
908
  stream: useStreaming
816
909
  };
817
- const hasAdaptiveThinking = options.model.includes("opus-4-7") || options.model.includes("opus-4.7") || options.model.includes("opus-4-6") || options.model.includes("opus-4.6") || options.model.includes("sonnet-4-6") || options.model.includes("sonnet-4.6");
910
+ const hasAdaptiveThinking = isAdaptiveThinkingModel(options.model);
818
911
  const betaHeaders = [
819
912
  ...isOAuth ? ["claude-code-20250219", "oauth-2025-04-20"] : [],
820
913
  ...options.compaction ? ["compact-2026-01-12"] : [],
@@ -1138,6 +1231,18 @@ function messageToResponse(message) {
1138
1231
  }
1139
1232
  };
1140
1233
  }
1234
+ function readUnifiedRateLimit(headers) {
1235
+ const status = readHeader(headers, "anthropic-ratelimit-unified-status");
1236
+ const resetRaw = readHeader(
1237
+ headers,
1238
+ "anthropic-ratelimit-unified-reset",
1239
+ "anthropic-ratelimit-unified-5h-reset",
1240
+ "anthropic-ratelimit-unified-7d-reset"
1241
+ );
1242
+ const resetNum = resetRaw != null ? Number(resetRaw) : Number.NaN;
1243
+ const resetsAt = Number.isFinite(resetNum) && resetNum > 0 ? resetNum : void 0;
1244
+ return { rejected: status === "rejected", ...resetsAt ? { resetsAt } : {} };
1245
+ }
1141
1246
  function toError(err) {
1142
1247
  if (err instanceof Anthropic.APIError) {
1143
1248
  const errorBody = err.error;
@@ -1146,6 +1251,18 @@ function toError(err) {
1146
1251
  const bodyMessage = typeof nestedError?.message === "string" ? nestedError.message : typeof errorBody?.message === "string" ? errorBody.message : void 0;
1147
1252
  const bodyType = typeof nestedError?.type === "string" ? nestedError.type : typeof errorBody?.type === "string" ? errorBody.type : typeof err.type === "string" ? err.type : void 0;
1148
1253
  const message = bodyType && bodyMessage ? `${bodyType}: ${bodyMessage}` : bodyMessage ?? err.message;
1254
+ if (err.status === 429) {
1255
+ const limit = readUnifiedRateLimit(err.headers);
1256
+ const farOff = limit.resetsAt != null && limit.resetsAt * 1e3 - Date.now() > 6e4;
1257
+ if (limit.rejected || farOff) {
1258
+ return new ProviderError("anthropic", "Claude usage limit reached", {
1259
+ statusCode: 429,
1260
+ ...requestId ? { requestId } : {},
1261
+ ...limit.resetsAt ? { resetsAt: limit.resetsAt } : {},
1262
+ cause: err
1263
+ });
1264
+ }
1265
+ }
1149
1266
  return new ProviderError("anthropic", message, {
1150
1267
  statusCode: err.status,
1151
1268
  ...requestId ? { requestId } : {},
@@ -1178,19 +1295,30 @@ function fnv1aHash(value) {
1178
1295
  return (hash >>> 0).toString(16).padStart(8, "0");
1179
1296
  }
1180
1297
 
1181
- // src/providers/openai.ts
1182
- function isJsonObject2(value) {
1183
- return value != null && typeof value === "object" && !Array.isArray(value);
1298
+ // src/utils/env.ts
1299
+ function getEnvironment() {
1300
+ return globalThis.process?.env;
1184
1301
  }
1185
- function parseToolArguments(argsJson) {
1186
- if (!argsJson) return {};
1187
- try {
1188
- const parsed = JSON.parse(argsJson);
1189
- const unwrapped = typeof parsed === "string" ? JSON.parse(parsed) : parsed;
1190
- return isJsonObject2(unwrapped) ? unwrapped : {};
1191
- } catch {
1192
- return {};
1302
+
1303
+ // src/providers/openai.ts
1304
+ function extractOpenAIUsage(usage) {
1305
+ let cacheRead = 0;
1306
+ const details = usage.prompt_tokens_details;
1307
+ if (details?.cached_tokens) {
1308
+ cacheRead = details.cached_tokens;
1193
1309
  }
1310
+ const usageAny = usage;
1311
+ if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
1312
+ cacheRead = usageAny.cached_tokens;
1313
+ }
1314
+ if (!cacheRead && typeof usageAny.prompt_cache_hit_tokens === "number" && usageAny.prompt_cache_hit_tokens > 0) {
1315
+ cacheRead = usageAny.prompt_cache_hit_tokens;
1316
+ }
1317
+ return {
1318
+ inputTokens: usage.prompt_tokens - cacheRead,
1319
+ outputTokens: usage.completion_tokens,
1320
+ cacheRead
1321
+ };
1194
1322
  }
1195
1323
  function createClient2(options) {
1196
1324
  return new OpenAI({
@@ -1246,7 +1374,7 @@ async function* runStream2(options) {
1246
1374
  params.thinking = { type: "disabled" };
1247
1375
  }
1248
1376
  }
1249
- if (globalThis.process && globalThis.process.env?.GGAI_DUMP_REQUEST) {
1377
+ if (getEnvironment()?.GGAI_DUMP_REQUEST) {
1250
1378
  const fs = await import("fs");
1251
1379
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1252
1380
  const dumpPath = `/tmp/ggai-request-${ts}.json`;
@@ -1287,19 +1415,7 @@ async function* runStream2(options) {
1287
1415
  for await (const chunk of stream2) {
1288
1416
  const choice = chunk.choices?.[0];
1289
1417
  if (chunk.usage) {
1290
- outputTokens = chunk.usage.completion_tokens;
1291
- const details = chunk.usage.prompt_tokens_details;
1292
- if (details?.cached_tokens) {
1293
- cacheRead = details.cached_tokens;
1294
- }
1295
- const usageAny = chunk.usage;
1296
- if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
1297
- cacheRead = usageAny.cached_tokens;
1298
- }
1299
- if (!cacheRead && typeof usageAny.prompt_cache_hit_tokens === "number" && usageAny.prompt_cache_hit_tokens > 0) {
1300
- cacheRead = usageAny.prompt_cache_hit_tokens;
1301
- }
1302
- inputTokens = chunk.usage.prompt_tokens - cacheRead;
1418
+ ({ inputTokens, outputTokens, cacheRead } = extractOpenAIUsage(chunk.usage));
1303
1419
  }
1304
1420
  if (!choice) continue;
1305
1421
  if (choice.finish_reason) {
@@ -1445,17 +1561,7 @@ function completionToResponse(completion) {
1445
1561
  let outputTokens = 0;
1446
1562
  let cacheRead = 0;
1447
1563
  if (completion.usage) {
1448
- outputTokens = completion.usage.completion_tokens;
1449
- const details = completion.usage.prompt_tokens_details;
1450
- if (details?.cached_tokens) cacheRead = details.cached_tokens;
1451
- const usageAny = completion.usage;
1452
- if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
1453
- cacheRead = usageAny.cached_tokens;
1454
- }
1455
- if (!cacheRead && typeof usageAny.prompt_cache_hit_tokens === "number" && usageAny.prompt_cache_hit_tokens > 0) {
1456
- cacheRead = usageAny.prompt_cache_hit_tokens;
1457
- }
1458
- inputTokens = completion.usage.prompt_tokens - cacheRead;
1564
+ ({ inputTokens, outputTokens, cacheRead } = extractOpenAIUsage(completion.usage));
1459
1565
  }
1460
1566
  const stopReason = normalizeOpenAIStopReason(choice?.finish_reason ?? null);
1461
1567
  return {
@@ -1503,24 +1609,65 @@ function providerDiag(phase, data) {
1503
1609
  _diagFn?.(phase, data);
1504
1610
  }
1505
1611
 
1506
- // src/providers/openai-codex.ts
1507
- var DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
1508
- function isJsonObject3(value) {
1509
- return value != null && typeof value === "object" && !Array.isArray(value);
1612
+ // src/utils/sse.ts
1613
+ function parseSseBuffer(buffer) {
1614
+ const events = [];
1615
+ let cursor = 0;
1616
+ while (true) {
1617
+ const next = buffer.indexOf("\n\n", cursor);
1618
+ if (next === -1) break;
1619
+ const raw = buffer.slice(cursor, next);
1620
+ cursor = next + 2;
1621
+ let eventName;
1622
+ const dataLines = [];
1623
+ for (const line of raw.split("\n")) {
1624
+ if (line.startsWith("event:")) {
1625
+ eventName = line.slice("event:".length).trim();
1626
+ } else if (line.startsWith("data:")) {
1627
+ dataLines.push(line.slice("data:".length).trimStart());
1628
+ }
1629
+ }
1630
+ if (dataLines.length > 0) {
1631
+ events.push({ event: eventName, data: dataLines.join("\n") });
1632
+ }
1633
+ }
1634
+ return { events, remaining: buffer.slice(cursor) };
1510
1635
  }
1511
- function parseToolArguments2(argsJson) {
1512
- if (!argsJson) return {};
1636
+ async function* readSseStream(body) {
1637
+ const reader = body.getReader();
1638
+ const decoder = new TextDecoder();
1639
+ let buffer = "";
1513
1640
  try {
1514
- const parsed = JSON.parse(argsJson);
1515
- const unwrapped = typeof parsed === "string" ? JSON.parse(parsed) : parsed;
1516
- return isJsonObject3(unwrapped) ? unwrapped : {};
1517
- } catch {
1518
- return {};
1641
+ while (true) {
1642
+ const { done, value } = await reader.read();
1643
+ if (done) break;
1644
+ buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
1645
+ const parsed2 = parseSseBuffer(buffer);
1646
+ buffer = parsed2.remaining;
1647
+ yield* parsed2.events;
1648
+ }
1649
+ buffer += decoder.decode().replace(/\r\n/g, "\n");
1650
+ const parsed = parseSseBuffer(buffer + "\n\n");
1651
+ yield* parsed.events;
1652
+ } finally {
1653
+ reader.releaseLock();
1519
1654
  }
1520
1655
  }
1656
+
1657
+ // src/utils/request-id.ts
1658
+ function extractRequestIdFromMessage(message) {
1659
+ const match = message.match(/request ID ([a-z0-9-]{8,})/i);
1660
+ return match?.[1];
1661
+ }
1662
+
1663
+ // src/providers/openai-codex.ts
1664
+ var DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
1521
1665
  function outputTextKey(itemId, contentIndex) {
1522
1666
  return `${itemId ?? ""}:${contentIndex ?? 0}`;
1523
1667
  }
1668
+ function isVisibleOutputItem(itemType) {
1669
+ return itemType === "message";
1670
+ }
1524
1671
  function streamOpenAICodex(options) {
1525
1672
  return new StreamResult(runStream3(options));
1526
1673
  }
@@ -1579,7 +1726,9 @@ async function* runStream3(options) {
1579
1726
  const text = await response.text().catch(() => "");
1580
1727
  const parsed = parseCodexErrorBody(text);
1581
1728
  const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
1582
- const requestId = parsed.requestId ?? response.headers.get("x-request-id") ?? response.headers.get("openai-request-id") ?? response.headers.get("x-oai-request-id") ?? void 0;
1729
+ const requestId = parsed.requestId ?? readHeader(response.headers, "x-request-id", "openai-request-id", "x-oai-request-id");
1730
+ const usageLimit = codexUsageLimitError(parsed.errorObj, response.status, requestId);
1731
+ if (usageLimit) throw usageLimit;
1583
1732
  let hint;
1584
1733
  if (response.status === 400 && text.includes("not supported")) {
1585
1734
  if (options.model === "gpt-5.5-pro") {
@@ -1604,6 +1753,7 @@ async function* runStream3(options) {
1604
1753
  const toolCalls = /* @__PURE__ */ new Map();
1605
1754
  const outputItemTypes = /* @__PURE__ */ new Map();
1606
1755
  const outputTextByPart = /* @__PURE__ */ new Map();
1756
+ const pendingOutputTextByPart = /* @__PURE__ */ new Map();
1607
1757
  let inputTokens = 0;
1608
1758
  let outputTokens = 0;
1609
1759
  let cacheRead = 0;
@@ -1620,7 +1770,13 @@ async function* runStream3(options) {
1620
1770
  const nested = event.error ?? void 0;
1621
1771
  const message = nested?.message ?? event.message ?? "Codex stream emitted an error chunk without a message.";
1622
1772
  const code = nested?.code ?? nested?.type ?? event.code ?? "server_error";
1623
- const requestId = extractCodexRequestId(message) ?? event.request_id;
1773
+ const requestId = extractRequestIdFromMessage(message) ?? event.request_id;
1774
+ const usageLimit = codexUsageLimitError(
1775
+ nested ?? event,
1776
+ void 0,
1777
+ requestId
1778
+ );
1779
+ if (usageLimit) throw usageLimit;
1624
1780
  throw new ProviderError("openai", message, {
1625
1781
  ...requestId != null ? { requestId } : {},
1626
1782
  ...code === "server_error" ? { statusCode: 500 } : {}
@@ -1629,7 +1785,7 @@ async function* runStream3(options) {
1629
1785
  if (type === "response.failed") {
1630
1786
  const nested = event.error;
1631
1787
  const message = nested?.message ?? "Codex response failed.";
1632
- const requestId = extractCodexRequestId(message) ?? event.request_id;
1788
+ const requestId = extractRequestIdFromMessage(message) ?? event.request_id;
1633
1789
  throw new ProviderError("openai", message, {
1634
1790
  ...requestId != null ? { requestId } : {}
1635
1791
  });
@@ -1640,11 +1796,17 @@ async function* runStream3(options) {
1640
1796
  const contentIndex = event.content_index;
1641
1797
  const key = outputTextKey(itemId, contentIndex);
1642
1798
  outputTextByPart.set(key, `${outputTextByPart.get(key) ?? ""}${delta}`);
1643
- if (itemId && outputItemTypes.get(itemId) === "reasoning") {
1644
- if (options.thinking) yield { type: "thinking_delta", text: delta };
1645
- } else {
1799
+ const itemType = itemId ? outputItemTypes.get(itemId) : void 0;
1800
+ if (itemId && isVisibleOutputItem(itemType)) {
1646
1801
  textAccum += delta;
1647
1802
  yield { type: "text_delta", text: delta };
1803
+ } else if (itemId && itemType == null) {
1804
+ const pending = pendingOutputTextByPart.get(key);
1805
+ pendingOutputTextByPart.set(key, {
1806
+ itemId,
1807
+ contentIndex: contentIndex ?? 0,
1808
+ text: `${pending?.text ?? ""}${delta}`
1809
+ });
1648
1810
  }
1649
1811
  }
1650
1812
  if (type === "response.output_text.done") {
@@ -1657,11 +1819,17 @@ async function* runStream3(options) {
1657
1819
  const missingText = streamedText ? fullText.slice(streamedText.length) : fullText;
1658
1820
  outputTextByPart.set(key, fullText);
1659
1821
  if (missingText && fullText.startsWith(streamedText)) {
1660
- if (itemId && outputItemTypes.get(itemId) === "reasoning") {
1661
- if (options.thinking) yield { type: "thinking_delta", text: missingText };
1662
- } else {
1822
+ const itemType = itemId ? outputItemTypes.get(itemId) : void 0;
1823
+ if (itemId && isVisibleOutputItem(itemType)) {
1663
1824
  textAccum += missingText;
1664
1825
  yield { type: "text_delta", text: missingText };
1826
+ } else if (itemId && itemType == null) {
1827
+ const pending = pendingOutputTextByPart.get(key);
1828
+ pendingOutputTextByPart.set(key, {
1829
+ itemId,
1830
+ contentIndex: contentIndex ?? 0,
1831
+ text: `${pending?.text ?? ""}${missingText}`
1832
+ });
1665
1833
  }
1666
1834
  }
1667
1835
  }
@@ -1680,6 +1848,17 @@ async function* runStream3(options) {
1680
1848
  if (itemType === "reasoning" && options.thinking) {
1681
1849
  yield { type: "thinking_delta", text: "" };
1682
1850
  }
1851
+ if (itemId && itemType) {
1852
+ const pending = [...pendingOutputTextByPart.entries()].filter(([, pendingPart]) => pendingPart.itemId === itemId).sort(([, a], [, b]) => a.contentIndex - b.contentIndex);
1853
+ for (const [key, pendingPart] of pending) {
1854
+ pendingOutputTextByPart.delete(key);
1855
+ if (!pendingPart.text) continue;
1856
+ if (isVisibleOutputItem(itemType)) {
1857
+ textAccum += pendingPart.text;
1858
+ yield { type: "text_delta", text: pendingPart.text };
1859
+ }
1860
+ }
1861
+ }
1683
1862
  }
1684
1863
  if (type === "response.output_item.added") {
1685
1864
  const item = event.item;
@@ -1725,7 +1904,7 @@ async function* runStream3(options) {
1725
1904
  const id = `${callId}|${itemId}`;
1726
1905
  const tc = toolCalls.get(id);
1727
1906
  if (tc) {
1728
- const args = parseToolArguments2(tc.argsJson);
1907
+ const args = parseToolArguments(tc.argsJson);
1729
1908
  yield {
1730
1909
  type: "toolcall_done",
1731
1910
  id: tc.id,
@@ -1749,7 +1928,7 @@ async function* runStream3(options) {
1749
1928
  contentParts.push({ type: "text", text: textAccum });
1750
1929
  }
1751
1930
  for (const [, tc] of toolCalls) {
1752
- const args = parseToolArguments2(tc.argsJson);
1931
+ const args = parseToolArguments(tc.argsJson);
1753
1932
  const toolCall = {
1754
1933
  type: "tool_call",
1755
1934
  id: tc.id,
@@ -1772,33 +1951,13 @@ async function* runStream3(options) {
1772
1951
  return streamResponse;
1773
1952
  }
1774
1953
  async function* parseSSE(body) {
1775
- const reader = body.getReader();
1776
- const decoder = new TextDecoder();
1777
- let buffer = "";
1778
- try {
1779
- while (true) {
1780
- const { done, value } = await reader.read();
1781
- if (done) break;
1782
- buffer += decoder.decode(value, { stream: true });
1783
- let idx = buffer.indexOf("\n\n");
1784
- while (idx !== -1) {
1785
- const chunk = buffer.slice(0, idx);
1786
- buffer = buffer.slice(idx + 2);
1787
- const dataLines = chunk.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim());
1788
- if (dataLines.length > 0) {
1789
- const data = dataLines.join("\n").trim();
1790
- if (data && data !== "[DONE]") {
1791
- try {
1792
- yield JSON.parse(data);
1793
- } catch {
1794
- }
1795
- }
1796
- }
1797
- idx = buffer.indexOf("\n\n");
1798
- }
1954
+ for await (const event of readSseStream(body)) {
1955
+ const data = event.data.trim();
1956
+ if (!data || data === "[DONE]") continue;
1957
+ try {
1958
+ yield JSON.parse(data);
1959
+ } catch {
1799
1960
  }
1800
- } finally {
1801
- reader.releaseLock();
1802
1961
  }
1803
1962
  }
1804
1963
  function remapCodexId(id, idMap) {
@@ -1809,10 +1968,6 @@ function remapCodexId(id, idMap) {
1809
1968
  idMap.set(id, mapped);
1810
1969
  return mapped;
1811
1970
  }
1812
- function codexToolResultText(content) {
1813
- if (typeof content === "string") return content;
1814
- return content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
1815
- }
1816
1971
  function toCodexInput(messages, options) {
1817
1972
  let system;
1818
1973
  const input = [];
@@ -1869,7 +2024,7 @@ function toCodexInput(messages, options) {
1869
2024
  const toolImages = [];
1870
2025
  for (const result of msg.content) {
1871
2026
  const [callId] = result.toolCallId.includes("|") ? result.toolCallId.split("|", 2) : [result.toolCallId];
1872
- const text = codexToolResultText(result.content);
2027
+ const text = toolResultText(result.content);
1873
2028
  input.push({
1874
2029
  type: "function_call_output",
1875
2030
  call_id: remapCodexId(callId, idMap),
@@ -1904,14 +2059,10 @@ function toCodexTools(tools) {
1904
2059
  type: "function",
1905
2060
  name: tool.name,
1906
2061
  description: tool.description,
1907
- parameters: tool.rawInputSchema ?? zodToJsonSchema(tool.parameters),
2062
+ parameters: resolveToolSchema(tool),
1908
2063
  strict: null
1909
2064
  }));
1910
2065
  }
1911
- function extractCodexRequestId(message) {
1912
- const match = message.match(/request ID ([a-z0-9-]{8,})/i);
1913
- return match?.[1];
1914
- }
1915
2066
  function parseCodexErrorBody(text) {
1916
2067
  if (!text) return {};
1917
2068
  try {
@@ -1919,13 +2070,35 @@ function parseCodexErrorBody(text) {
1919
2070
  const error = parsed.error;
1920
2071
  const detail = parsed.detail;
1921
2072
  const message = error?.message ?? parsed.message ?? (typeof detail === "string" ? detail : void 0);
1922
- const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractCodexRequestId(message) : void 0);
1923
- return { ...message ? { message } : {}, ...requestId ? { requestId } : {} };
2073
+ const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractRequestIdFromMessage(message) : void 0);
2074
+ const errorObj = error ?? parsed;
2075
+ return {
2076
+ ...message ? { message } : {},
2077
+ ...requestId ? { requestId } : {},
2078
+ ...errorObj ? { errorObj } : {}
2079
+ };
1924
2080
  } catch {
1925
2081
  const trimmed = text.trim().slice(0, 240);
1926
2082
  return trimmed ? { message: trimmed } : {};
1927
2083
  }
1928
2084
  }
2085
+ var CODEX_USAGE_LIMIT_CODE = /usage_limit_reached|usage_not_included/i;
2086
+ var CODEX_RATE_LIMIT_CODE = /rate_limit_exceeded/i;
2087
+ function codexUsageLimitError(errorObj, statusCode, requestId) {
2088
+ const code = String(errorObj?.code ?? errorObj?.type ?? "");
2089
+ const rateLimits = errorObj?.rate_limits;
2090
+ const resetsAtRaw = (typeof errorObj?.resets_at === "number" ? errorObj.resets_at : void 0) ?? rateLimits?.primary?.resets_at ?? rateLimits?.secondary?.resets_at;
2091
+ const resetsInSeconds = typeof errorObj?.resets_in_seconds === "number" ? errorObj.resets_in_seconds : void 0;
2092
+ const resetsAt = typeof resetsAtRaw === "number" && resetsAtRaw > 0 ? resetsAtRaw : resetsInSeconds != null && resetsInSeconds > 0 ? Math.floor(Date.now() / 1e3) + resetsInSeconds : void 0;
2093
+ const isHardUsage = CODEX_USAGE_LIMIT_CODE.test(code);
2094
+ const isRateOr429 = CODEX_RATE_LIMIT_CODE.test(code) || statusCode === 429;
2095
+ if (!isHardUsage && !(isRateOr429 && resetsAt != null)) return null;
2096
+ return new ProviderError("openai", "ChatGPT usage limit reached", {
2097
+ statusCode: statusCode ?? 429,
2098
+ ...requestId ? { requestId } : {},
2099
+ ...resetsAt ? { resetsAt } : {}
2100
+ });
2101
+ }
1929
2102
 
1930
2103
  // src/providers/gemini.ts
1931
2104
  var DEFAULT_CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com";
@@ -1947,12 +2120,6 @@ var CODE_ASSIST_SUPPORTED_MODELS = /* @__PURE__ */ new Set([
1947
2120
  "gemma-4-31b-it",
1948
2121
  "gemma-4-26b-a4b-it"
1949
2122
  ]);
1950
- function isJsonObject4(value) {
1951
- return value != null && typeof value === "object" && !Array.isArray(value);
1952
- }
1953
- function getEnvironment() {
1954
- return globalThis.process?.env;
1955
- }
1956
2123
  function getGoogleProject(options) {
1957
2124
  const env = getEnvironment();
1958
2125
  return options.projectId ?? env?.GOOGLE_CLOUD_PROJECT ?? env?.GOOGLE_CLOUD_PROJECT_ID;
@@ -2052,7 +2219,7 @@ function toGeminiTools(tools) {
2052
2219
  functionDeclarations: tools.map((tool) => ({
2053
2220
  name: tool.name,
2054
2221
  description: tool.description,
2055
- parameters: sanitizeSchema(tool.rawInputSchema ?? zodToJsonSchema(tool.parameters))
2222
+ parameters: sanitizeSchema(resolveToolSchema(tool))
2056
2223
  }))
2057
2224
  }
2058
2225
  ];
@@ -2063,7 +2230,7 @@ function sanitizeSchema(schema) {
2063
2230
  return clone;
2064
2231
  }
2065
2232
  function stripUnsupportedSchemaFields(value) {
2066
- if (!isJsonObject4(value)) {
2233
+ if (!isJsonObject(value)) {
2067
2234
  if (Array.isArray(value)) {
2068
2235
  for (const item of value) stripUnsupportedSchemaFields(item);
2069
2236
  }
@@ -2072,7 +2239,7 @@ function stripUnsupportedSchemaFields(value) {
2072
2239
  delete value.$schema;
2073
2240
  delete value.additionalProperties;
2074
2241
  for (const item of Object.values(value)) {
2075
- if (isJsonObject4(item) || Array.isArray(item)) {
2242
+ if (isJsonObject(item) || Array.isArray(item)) {
2076
2243
  stripUnsupportedSchemaFields(item);
2077
2244
  }
2078
2245
  }
@@ -2095,6 +2262,7 @@ function toGemini3ThinkingLevel(level) {
2095
2262
  return "MEDIUM";
2096
2263
  case "high":
2097
2264
  case "xhigh":
2265
+ case "max":
2098
2266
  return "HIGH";
2099
2267
  }
2100
2268
  }
@@ -2106,6 +2274,7 @@ function toThinkingBudget(level) {
2106
2274
  return 8192;
2107
2275
  case "high":
2108
2276
  case "xhigh":
2277
+ case "max":
2109
2278
  return 8192;
2110
2279
  }
2111
2280
  }
@@ -2185,54 +2354,11 @@ function normalizeGeminiStopReason(reason) {
2185
2354
  return "end_turn";
2186
2355
  }
2187
2356
  }
2188
- function parseSseEvents(buffer) {
2189
- const events = [];
2190
- let cursor = 0;
2191
- while (true) {
2192
- const next = buffer.indexOf("\n\n", cursor);
2193
- if (next === -1) break;
2194
- const raw = buffer.slice(cursor, next);
2195
- cursor = next + 2;
2196
- let eventName;
2197
- const dataLines = [];
2198
- for (const line of raw.split("\n")) {
2199
- if (line.startsWith("event:")) {
2200
- eventName = line.slice("event:".length).trim();
2201
- } else if (line.startsWith("data:")) {
2202
- dataLines.push(line.slice("data:".length).trimStart());
2203
- }
2204
- }
2205
- if (dataLines.length > 0) {
2206
- events.push({ event: eventName, data: dataLines.join("\n") });
2207
- }
2208
- }
2209
- return { events, remaining: buffer.slice(cursor) };
2210
- }
2211
2357
  async function* streamSse(response) {
2212
2358
  if (!response.body) return;
2213
- const reader = response.body.getReader();
2214
- const decoder = new TextDecoder();
2215
- let buffer = "";
2216
- try {
2217
- while (true) {
2218
- const { done, value } = await reader.read();
2219
- if (done) break;
2220
- buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
2221
- const parsed2 = parseSseEvents(buffer);
2222
- buffer = parsed2.remaining;
2223
- for (const event of parsed2.events) {
2224
- if (event.data === "[DONE]") continue;
2225
- yield JSON.parse(event.data);
2226
- }
2227
- }
2228
- buffer += decoder.decode().replace(/\r\n/g, "\n");
2229
- const parsed = parseSseEvents(buffer + "\n\n");
2230
- for (const event of parsed.events) {
2231
- if (event.data === "[DONE]") continue;
2232
- yield JSON.parse(event.data);
2233
- }
2234
- } finally {
2235
- reader.releaseLock();
2359
+ for await (const event of readSseStream(response.body)) {
2360
+ if (event.data === "[DONE]") continue;
2361
+ yield JSON.parse(event.data);
2236
2362
  }
2237
2363
  }
2238
2364
  function candidatesFromResponse(response) {
@@ -2255,7 +2381,7 @@ function readFunctionCallPart(part) {
2255
2381
  return {
2256
2382
  ...part.functionCall.id ? { id: part.functionCall.id } : {},
2257
2383
  name: part.functionCall.name,
2258
- args: isJsonObject4(part.functionCall.args) ? part.functionCall.args : {}
2384
+ args: isJsonObject(part.functionCall.args) ? part.functionCall.args : {}
2259
2385
  };
2260
2386
  }
2261
2387
  function makeToolCallId(index, providerId) {
@@ -2686,6 +2812,7 @@ export {
2686
2812
  StreamResult,
2687
2813
  formatError,
2688
2814
  formatErrorForDisplay,
2815
+ isUsageLimitError,
2689
2816
  palsuAssistantMessage,
2690
2817
  palsuText,
2691
2818
  palsuThinking,
@@ -2693,6 +2820,8 @@ export {
2693
2820
  providerRegistry,
2694
2821
  registerPalsuProvider,
2695
2822
  setProviderDiagnostic,
2696
- stream
2823
+ stream,
2824
+ toAnthropicMessages,
2825
+ toOpenAIMessages
2697
2826
  };
2698
2827
  //# sourceMappingURL=index.js.map