@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.cjs CHANGED
@@ -36,6 +36,7 @@ __export(index_exports, {
36
36
  StreamResult: () => StreamResult,
37
37
  formatError: () => formatError,
38
38
  formatErrorForDisplay: () => formatErrorForDisplay,
39
+ isUsageLimitError: () => isUsageLimitError,
39
40
  palsuAssistantMessage: () => palsuAssistantMessage,
40
41
  palsuText: () => palsuText,
41
42
  palsuThinking: () => palsuThinking,
@@ -43,11 +44,27 @@ __export(index_exports, {
43
44
  providerRegistry: () => providerRegistry,
44
45
  registerPalsuProvider: () => registerPalsuProvider,
45
46
  setProviderDiagnostic: () => setProviderDiagnostic,
46
- stream: () => stream
47
+ stream: () => stream,
48
+ toAnthropicMessages: () => toAnthropicMessages,
49
+ toOpenAIMessages: () => toOpenAIMessages
47
50
  });
48
51
  module.exports = __toCommonJS(index_exports);
49
52
 
50
53
  // src/errors.ts
54
+ function readHeader(headers, ...names) {
55
+ if (!headers) return void 0;
56
+ const getter = typeof headers.get === "function" ? (name) => headers.get(name) ?? void 0 : typeof headers === "object" ? (name) => {
57
+ const rec = headers;
58
+ const value = rec[name] ?? rec[name.toLowerCase()];
59
+ return typeof value === "string" ? value : void 0;
60
+ } : void 0;
61
+ if (!getter) return void 0;
62
+ for (const name of names) {
63
+ const value = getter(name);
64
+ if (value != null) return value;
65
+ }
66
+ return void 0;
67
+ }
51
68
  var EZCoderAIError = class extends Error {
52
69
  source;
53
70
  requestId;
@@ -63,6 +80,8 @@ var EZCoderAIError = class extends Error {
63
80
  var ProviderError = class extends EZCoderAIError {
64
81
  provider;
65
82
  statusCode;
83
+ /** Unix seconds when a usage/rate limit resets, when the provider reports it. */
84
+ resetsAt;
66
85
  constructor(provider, message, options) {
67
86
  super(message, {
68
87
  source: "provider",
@@ -73,6 +92,7 @@ var ProviderError = class extends EZCoderAIError {
73
92
  this.name = "ProviderError";
74
93
  this.provider = provider;
75
94
  this.statusCode = options?.statusCode;
95
+ this.resetsAt = options?.resetsAt;
76
96
  }
77
97
  };
78
98
  var PROVIDER_DISPLAY = {
@@ -93,10 +113,36 @@ var PROVIDER_STATUS_URL = {
93
113
  function providerDisplayName(provider) {
94
114
  return PROVIDER_DISPLAY[provider] ?? provider;
95
115
  }
116
+ function isUsageLimitError(err) {
117
+ if (!(err instanceof Error)) return false;
118
+ return /usage limit reached/i.test(err.message);
119
+ }
120
+ function formatResetTime(resetsAt) {
121
+ const when = new Date(resetsAt * 1e3);
122
+ const sameDay = when.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
123
+ return sameDay ? when.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" }) : when.toLocaleString(void 0, {
124
+ weekday: "short",
125
+ hour: "numeric",
126
+ minute: "2-digit"
127
+ });
128
+ }
96
129
  function formatError(err) {
97
130
  if (err instanceof ProviderError) {
98
131
  const name = providerDisplayName(err.provider);
99
132
  const cleanMessage = cleanProviderMessage(err.message);
133
+ if (isUsageLimitError(err)) {
134
+ const resetClause = err.resetsAt ? ` It resets at ${formatResetTime(err.resetsAt)}.` : "";
135
+ return {
136
+ headline: `${name} usage limit reached.`,
137
+ source: "provider",
138
+ message: `Your ${name} usage is finished.${resetClause}`,
139
+ provider: err.provider,
140
+ statusCode: err.statusCode,
141
+ ...err.requestId ? { requestId: err.requestId } : {},
142
+ ...err.resetsAt ? { resetsAt: err.resetsAt } : {},
143
+ guidance: "Try again once it's back. Your conversation is preserved."
144
+ };
145
+ }
100
146
  return {
101
147
  headline: `${name} returned an error.`,
102
148
  source: "provider",
@@ -320,6 +366,9 @@ function zodToJsonSchema(schema) {
320
366
  const { $schema: _schema, ...rest } = jsonSchema;
321
367
  return normalizeRootForAnthropic(rest);
322
368
  }
369
+ function resolveToolSchema(tool) {
370
+ return tool.rawInputSchema ?? zodToJsonSchema(tool.parameters);
371
+ }
323
372
  function normalizeRootForAnthropic(schema) {
324
373
  const branches = schema.oneOf ?? schema.anyOf;
325
374
  if (!branches || branches.length === 0) {
@@ -376,6 +425,61 @@ function normalizeRootForAnthropic(schema) {
376
425
  }
377
426
 
378
427
  // src/providers/transform.ts
428
+ function hasValidThinkingSignature(part) {
429
+ return typeof part.signature === "string" && part.signature.trim().length > 0;
430
+ }
431
+ function isRawThinking(part) {
432
+ if (part.type !== "raw") return false;
433
+ const t = part.data.type;
434
+ return t === "thinking" || t === "redacted_thinking";
435
+ }
436
+ function isPositionSensitiveThinking(part) {
437
+ if (part.type === "thinking") return hasValidThinkingSignature(part);
438
+ return isRawThinking(part);
439
+ }
440
+ function toAnthropicAssistantPart(part, idMap) {
441
+ if (part.type === "text") return { type: "text", text: part.text };
442
+ if (part.type === "thinking") {
443
+ const sig = part.signature;
444
+ return sig && sig.trim().length > 0 ? { type: "thinking", thinking: part.text, signature: sig } : { type: "text", text: part.text };
445
+ }
446
+ if (part.type === "tool_call")
447
+ return {
448
+ type: "tool_use",
449
+ id: remapAnthropicToolCallId(part.id, idMap),
450
+ name: part.name,
451
+ input: part.args
452
+ };
453
+ if (part.type === "server_tool_call")
454
+ return {
455
+ type: "server_tool_use",
456
+ id: part.id,
457
+ name: part.name,
458
+ input: part.input
459
+ };
460
+ if (part.type === "server_tool_result")
461
+ return part.data;
462
+ if (part.type === "raw") return part.data;
463
+ return null;
464
+ }
465
+ function toAnthropicAssistantContent(content, isLatest, idMap) {
466
+ if (!isLatest) {
467
+ return content.filter((part) => {
468
+ if (part.type === "thinking" || isRawThinking(part)) return false;
469
+ if (part.type === "text" && !part.text) return false;
470
+ return true;
471
+ }).map((part) => toAnthropicAssistantPart(part, idMap)).filter((b) => b !== null);
472
+ }
473
+ const lastThinkingIdx = content.reduce(
474
+ (last, part, idx) => isPositionSensitiveThinking(part) ? idx : last,
475
+ -1
476
+ );
477
+ return content.filter((part, idx) => {
478
+ if (part.type === "thinking" && !hasValidThinkingSignature(part) && !part.text) return false;
479
+ if (part.type === "text" && !part.text && idx > lastThinkingIdx) return false;
480
+ return true;
481
+ }).map((part) => toAnthropicAssistantPart(part, idMap)).filter((b) => b !== null);
482
+ }
379
483
  var NON_VISION_USER_IMAGE_PLACEHOLDER = "(image omitted: model does not support images)";
380
484
  var NON_VISION_TOOL_IMAGE_PLACEHOLDER = "(tool image omitted: model does not support images)";
381
485
  function stripImages(content, placeholder) {
@@ -452,7 +556,13 @@ function toAnthropicMessages(messages, cacheControl) {
452
556
  let systemText;
453
557
  const out = [];
454
558
  const idMap = /* @__PURE__ */ new Map();
559
+ const lastAssistantIdx = messages.reduce(
560
+ (last, m, i) => m.role === "assistant" ? i : last,
561
+ -1
562
+ );
563
+ let msgIdx = -1;
455
564
  for (const msg of messages) {
565
+ msgIdx++;
456
566
  if (msg.role === "system") {
457
567
  systemText = msg.content;
458
568
  continue;
@@ -475,33 +585,7 @@ function toAnthropicMessages(messages, cacheControl) {
475
585
  continue;
476
586
  }
477
587
  if (msg.role === "assistant") {
478
- const content = typeof msg.content === "string" ? msg.content : msg.content.filter((part) => {
479
- if (part.type === "thinking" && !part.signature) return false;
480
- if (part.type === "text" && !part.text) return false;
481
- return true;
482
- }).map((part) => {
483
- if (part.type === "text") return { type: "text", text: part.text };
484
- if (part.type === "thinking")
485
- return { type: "thinking", thinking: part.text, signature: part.signature };
486
- if (part.type === "tool_call")
487
- return {
488
- type: "tool_use",
489
- id: remapAnthropicToolCallId(part.id, idMap),
490
- name: part.name,
491
- input: part.args
492
- };
493
- if (part.type === "server_tool_call")
494
- return {
495
- type: "server_tool_use",
496
- id: part.id,
497
- name: part.name,
498
- input: part.input
499
- };
500
- if (part.type === "server_tool_result")
501
- return part.data;
502
- if (part.type === "raw") return part.data;
503
- return null;
504
- }).filter(Boolean);
588
+ const content = typeof msg.content === "string" ? msg.content : toAnthropicAssistantContent(msg.content, msgIdx === lastAssistantIdx, idMap);
505
589
  if (Array.isArray(content) && content.length === 0) continue;
506
590
  out.push({ role: "assistant", content });
507
591
  continue;
@@ -587,13 +671,13 @@ function toAnthropicToolChoice(choice) {
587
671
  if (choice === "required") return { type: "any" };
588
672
  return { type: "tool", name: choice.name };
589
673
  }
590
- function supportsAdaptiveThinking(model) {
591
- return /opus-4-7|opus-4-6|sonnet-4-6/.test(model);
674
+ function isAdaptiveThinkingModel(model) {
675
+ return /opus-4[-.]8|opus-4[-.]7|opus-4[-.]6|sonnet-4[-.]6/.test(model);
592
676
  }
593
677
  function toAnthropicThinking(level, maxTokens, model) {
594
- if (supportsAdaptiveThinking(model)) {
595
- let effort = level === "xhigh" ? "max" : level;
596
- if (effort === "max" && !model.includes("opus")) {
678
+ if (isAdaptiveThinkingModel(model)) {
679
+ let effort = level;
680
+ if (effort === "xhigh" && !/opus-4-8|opus-4-7/.test(model)) {
597
681
  effort = "high";
598
682
  }
599
683
  return {
@@ -602,7 +686,7 @@ function toAnthropicThinking(level, maxTokens, model) {
602
686
  outputConfig: { effort }
603
687
  };
604
688
  }
605
- const effectiveLevel = level === "xhigh" ? "high" : level;
689
+ const effectiveLevel = level === "xhigh" || level === "max" ? "high" : level;
606
690
  const budgetMap = {
607
691
  low: Math.max(1024, Math.floor(maxTokens * 0.25)),
608
692
  medium: Math.max(2048, Math.floor(maxTokens * 0.5)),
@@ -725,7 +809,7 @@ function toOpenAITools(tools) {
725
809
  function: {
726
810
  name: tool.name,
727
811
  description: tool.description,
728
- parameters: tool.rawInputSchema ?? zodToJsonSchema(tool.parameters)
812
+ parameters: resolveToolSchema(tool)
729
813
  }
730
814
  }));
731
815
  }
@@ -736,7 +820,7 @@ function toOpenAIToolChoice(choice) {
736
820
  return { type: "function", function: { name: choice.name } };
737
821
  }
738
822
  function toOpenAIReasoningEffort(level, _model) {
739
- return level;
823
+ return level === "max" ? "xhigh" : level;
740
824
  }
741
825
  function normalizeAnthropicStopReason(reason) {
742
826
  switch (reason) {
@@ -767,10 +851,22 @@ function normalizeOpenAIStopReason(reason) {
767
851
  }
768
852
  }
769
853
 
770
- // src/providers/anthropic.ts
854
+ // src/utils/json.ts
771
855
  function isJsonObject(value) {
772
856
  return value != null && typeof value === "object" && !Array.isArray(value);
773
857
  }
858
+ function parseToolArguments(argsJson) {
859
+ if (!argsJson) return {};
860
+ try {
861
+ const parsed = JSON.parse(argsJson);
862
+ const unwrapped = typeof parsed === "string" ? JSON.parse(parsed) : parsed;
863
+ return isJsonObject(unwrapped) ? unwrapped : {};
864
+ } catch {
865
+ return {};
866
+ }
867
+ }
868
+
869
+ // src/providers/anthropic.ts
774
870
  function createClient(options) {
775
871
  const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
776
872
  return new import_sdk.default({
@@ -863,7 +959,7 @@ async function* runStream(options) {
863
959
  })(),
864
960
  stream: useStreaming
865
961
  };
866
- 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");
962
+ const hasAdaptiveThinking = isAdaptiveThinkingModel(options.model);
867
963
  const betaHeaders = [
868
964
  ...isOAuth ? ["claude-code-20250219", "oauth-2025-04-20"] : [],
869
965
  ...options.compaction ? ["compact-2026-01-12"] : [],
@@ -1187,6 +1283,18 @@ function messageToResponse(message) {
1187
1283
  }
1188
1284
  };
1189
1285
  }
1286
+ function readUnifiedRateLimit(headers) {
1287
+ const status = readHeader(headers, "anthropic-ratelimit-unified-status");
1288
+ const resetRaw = readHeader(
1289
+ headers,
1290
+ "anthropic-ratelimit-unified-reset",
1291
+ "anthropic-ratelimit-unified-5h-reset",
1292
+ "anthropic-ratelimit-unified-7d-reset"
1293
+ );
1294
+ const resetNum = resetRaw != null ? Number(resetRaw) : Number.NaN;
1295
+ const resetsAt = Number.isFinite(resetNum) && resetNum > 0 ? resetNum : void 0;
1296
+ return { rejected: status === "rejected", ...resetsAt ? { resetsAt } : {} };
1297
+ }
1190
1298
  function toError(err) {
1191
1299
  if (err instanceof import_sdk.default.APIError) {
1192
1300
  const errorBody = err.error;
@@ -1195,6 +1303,18 @@ function toError(err) {
1195
1303
  const bodyMessage = typeof nestedError?.message === "string" ? nestedError.message : typeof errorBody?.message === "string" ? errorBody.message : void 0;
1196
1304
  const bodyType = typeof nestedError?.type === "string" ? nestedError.type : typeof errorBody?.type === "string" ? errorBody.type : typeof err.type === "string" ? err.type : void 0;
1197
1305
  const message = bodyType && bodyMessage ? `${bodyType}: ${bodyMessage}` : bodyMessage ?? err.message;
1306
+ if (err.status === 429) {
1307
+ const limit = readUnifiedRateLimit(err.headers);
1308
+ const farOff = limit.resetsAt != null && limit.resetsAt * 1e3 - Date.now() > 6e4;
1309
+ if (limit.rejected || farOff) {
1310
+ return new ProviderError("anthropic", "Claude usage limit reached", {
1311
+ statusCode: 429,
1312
+ ...requestId ? { requestId } : {},
1313
+ ...limit.resetsAt ? { resetsAt: limit.resetsAt } : {},
1314
+ cause: err
1315
+ });
1316
+ }
1317
+ }
1198
1318
  return new ProviderError("anthropic", message, {
1199
1319
  statusCode: err.status,
1200
1320
  ...requestId ? { requestId } : {},
@@ -1227,19 +1347,30 @@ function fnv1aHash(value) {
1227
1347
  return (hash >>> 0).toString(16).padStart(8, "0");
1228
1348
  }
1229
1349
 
1230
- // src/providers/openai.ts
1231
- function isJsonObject2(value) {
1232
- return value != null && typeof value === "object" && !Array.isArray(value);
1350
+ // src/utils/env.ts
1351
+ function getEnvironment() {
1352
+ return globalThis.process?.env;
1233
1353
  }
1234
- function parseToolArguments(argsJson) {
1235
- if (!argsJson) return {};
1236
- try {
1237
- const parsed = JSON.parse(argsJson);
1238
- const unwrapped = typeof parsed === "string" ? JSON.parse(parsed) : parsed;
1239
- return isJsonObject2(unwrapped) ? unwrapped : {};
1240
- } catch {
1241
- return {};
1354
+
1355
+ // src/providers/openai.ts
1356
+ function extractOpenAIUsage(usage) {
1357
+ let cacheRead = 0;
1358
+ const details = usage.prompt_tokens_details;
1359
+ if (details?.cached_tokens) {
1360
+ cacheRead = details.cached_tokens;
1242
1361
  }
1362
+ const usageAny = usage;
1363
+ if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
1364
+ cacheRead = usageAny.cached_tokens;
1365
+ }
1366
+ if (!cacheRead && typeof usageAny.prompt_cache_hit_tokens === "number" && usageAny.prompt_cache_hit_tokens > 0) {
1367
+ cacheRead = usageAny.prompt_cache_hit_tokens;
1368
+ }
1369
+ return {
1370
+ inputTokens: usage.prompt_tokens - cacheRead,
1371
+ outputTokens: usage.completion_tokens,
1372
+ cacheRead
1373
+ };
1243
1374
  }
1244
1375
  function createClient2(options) {
1245
1376
  return new import_openai.default({
@@ -1295,7 +1426,7 @@ async function* runStream2(options) {
1295
1426
  params.thinking = { type: "disabled" };
1296
1427
  }
1297
1428
  }
1298
- if (globalThis.process && globalThis.process.env?.GGAI_DUMP_REQUEST) {
1429
+ if (getEnvironment()?.GGAI_DUMP_REQUEST) {
1299
1430
  const fs = await import("fs");
1300
1431
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1301
1432
  const dumpPath = `/tmp/ggai-request-${ts}.json`;
@@ -1336,19 +1467,7 @@ async function* runStream2(options) {
1336
1467
  for await (const chunk of stream2) {
1337
1468
  const choice = chunk.choices?.[0];
1338
1469
  if (chunk.usage) {
1339
- outputTokens = chunk.usage.completion_tokens;
1340
- const details = chunk.usage.prompt_tokens_details;
1341
- if (details?.cached_tokens) {
1342
- cacheRead = details.cached_tokens;
1343
- }
1344
- const usageAny = chunk.usage;
1345
- if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
1346
- cacheRead = usageAny.cached_tokens;
1347
- }
1348
- if (!cacheRead && typeof usageAny.prompt_cache_hit_tokens === "number" && usageAny.prompt_cache_hit_tokens > 0) {
1349
- cacheRead = usageAny.prompt_cache_hit_tokens;
1350
- }
1351
- inputTokens = chunk.usage.prompt_tokens - cacheRead;
1470
+ ({ inputTokens, outputTokens, cacheRead } = extractOpenAIUsage(chunk.usage));
1352
1471
  }
1353
1472
  if (!choice) continue;
1354
1473
  if (choice.finish_reason) {
@@ -1494,17 +1613,7 @@ function completionToResponse(completion) {
1494
1613
  let outputTokens = 0;
1495
1614
  let cacheRead = 0;
1496
1615
  if (completion.usage) {
1497
- outputTokens = completion.usage.completion_tokens;
1498
- const details = completion.usage.prompt_tokens_details;
1499
- if (details?.cached_tokens) cacheRead = details.cached_tokens;
1500
- const usageAny = completion.usage;
1501
- if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
1502
- cacheRead = usageAny.cached_tokens;
1503
- }
1504
- if (!cacheRead && typeof usageAny.prompt_cache_hit_tokens === "number" && usageAny.prompt_cache_hit_tokens > 0) {
1505
- cacheRead = usageAny.prompt_cache_hit_tokens;
1506
- }
1507
- inputTokens = completion.usage.prompt_tokens - cacheRead;
1616
+ ({ inputTokens, outputTokens, cacheRead } = extractOpenAIUsage(completion.usage));
1508
1617
  }
1509
1618
  const stopReason = normalizeOpenAIStopReason(choice?.finish_reason ?? null);
1510
1619
  return {
@@ -1552,24 +1661,65 @@ function providerDiag(phase, data) {
1552
1661
  _diagFn?.(phase, data);
1553
1662
  }
1554
1663
 
1555
- // src/providers/openai-codex.ts
1556
- var DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
1557
- function isJsonObject3(value) {
1558
- return value != null && typeof value === "object" && !Array.isArray(value);
1664
+ // src/utils/sse.ts
1665
+ function parseSseBuffer(buffer) {
1666
+ const events = [];
1667
+ let cursor = 0;
1668
+ while (true) {
1669
+ const next = buffer.indexOf("\n\n", cursor);
1670
+ if (next === -1) break;
1671
+ const raw = buffer.slice(cursor, next);
1672
+ cursor = next + 2;
1673
+ let eventName;
1674
+ const dataLines = [];
1675
+ for (const line of raw.split("\n")) {
1676
+ if (line.startsWith("event:")) {
1677
+ eventName = line.slice("event:".length).trim();
1678
+ } else if (line.startsWith("data:")) {
1679
+ dataLines.push(line.slice("data:".length).trimStart());
1680
+ }
1681
+ }
1682
+ if (dataLines.length > 0) {
1683
+ events.push({ event: eventName, data: dataLines.join("\n") });
1684
+ }
1685
+ }
1686
+ return { events, remaining: buffer.slice(cursor) };
1559
1687
  }
1560
- function parseToolArguments2(argsJson) {
1561
- if (!argsJson) return {};
1688
+ async function* readSseStream(body) {
1689
+ const reader = body.getReader();
1690
+ const decoder = new TextDecoder();
1691
+ let buffer = "";
1562
1692
  try {
1563
- const parsed = JSON.parse(argsJson);
1564
- const unwrapped = typeof parsed === "string" ? JSON.parse(parsed) : parsed;
1565
- return isJsonObject3(unwrapped) ? unwrapped : {};
1566
- } catch {
1567
- return {};
1693
+ while (true) {
1694
+ const { done, value } = await reader.read();
1695
+ if (done) break;
1696
+ buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
1697
+ const parsed2 = parseSseBuffer(buffer);
1698
+ buffer = parsed2.remaining;
1699
+ yield* parsed2.events;
1700
+ }
1701
+ buffer += decoder.decode().replace(/\r\n/g, "\n");
1702
+ const parsed = parseSseBuffer(buffer + "\n\n");
1703
+ yield* parsed.events;
1704
+ } finally {
1705
+ reader.releaseLock();
1568
1706
  }
1569
1707
  }
1708
+
1709
+ // src/utils/request-id.ts
1710
+ function extractRequestIdFromMessage(message) {
1711
+ const match = message.match(/request ID ([a-z0-9-]{8,})/i);
1712
+ return match?.[1];
1713
+ }
1714
+
1715
+ // src/providers/openai-codex.ts
1716
+ var DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
1570
1717
  function outputTextKey(itemId, contentIndex) {
1571
1718
  return `${itemId ?? ""}:${contentIndex ?? 0}`;
1572
1719
  }
1720
+ function isVisibleOutputItem(itemType) {
1721
+ return itemType === "message";
1722
+ }
1573
1723
  function streamOpenAICodex(options) {
1574
1724
  return new StreamResult(runStream3(options));
1575
1725
  }
@@ -1628,7 +1778,9 @@ async function* runStream3(options) {
1628
1778
  const text = await response.text().catch(() => "");
1629
1779
  const parsed = parseCodexErrorBody(text);
1630
1780
  const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
1631
- 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;
1781
+ const requestId = parsed.requestId ?? readHeader(response.headers, "x-request-id", "openai-request-id", "x-oai-request-id");
1782
+ const usageLimit = codexUsageLimitError(parsed.errorObj, response.status, requestId);
1783
+ if (usageLimit) throw usageLimit;
1632
1784
  let hint;
1633
1785
  if (response.status === 400 && text.includes("not supported")) {
1634
1786
  if (options.model === "gpt-5.5-pro") {
@@ -1653,6 +1805,7 @@ async function* runStream3(options) {
1653
1805
  const toolCalls = /* @__PURE__ */ new Map();
1654
1806
  const outputItemTypes = /* @__PURE__ */ new Map();
1655
1807
  const outputTextByPart = /* @__PURE__ */ new Map();
1808
+ const pendingOutputTextByPart = /* @__PURE__ */ new Map();
1656
1809
  let inputTokens = 0;
1657
1810
  let outputTokens = 0;
1658
1811
  let cacheRead = 0;
@@ -1669,7 +1822,13 @@ async function* runStream3(options) {
1669
1822
  const nested = event.error ?? void 0;
1670
1823
  const message = nested?.message ?? event.message ?? "Codex stream emitted an error chunk without a message.";
1671
1824
  const code = nested?.code ?? nested?.type ?? event.code ?? "server_error";
1672
- const requestId = extractCodexRequestId(message) ?? event.request_id;
1825
+ const requestId = extractRequestIdFromMessage(message) ?? event.request_id;
1826
+ const usageLimit = codexUsageLimitError(
1827
+ nested ?? event,
1828
+ void 0,
1829
+ requestId
1830
+ );
1831
+ if (usageLimit) throw usageLimit;
1673
1832
  throw new ProviderError("openai", message, {
1674
1833
  ...requestId != null ? { requestId } : {},
1675
1834
  ...code === "server_error" ? { statusCode: 500 } : {}
@@ -1678,7 +1837,7 @@ async function* runStream3(options) {
1678
1837
  if (type === "response.failed") {
1679
1838
  const nested = event.error;
1680
1839
  const message = nested?.message ?? "Codex response failed.";
1681
- const requestId = extractCodexRequestId(message) ?? event.request_id;
1840
+ const requestId = extractRequestIdFromMessage(message) ?? event.request_id;
1682
1841
  throw new ProviderError("openai", message, {
1683
1842
  ...requestId != null ? { requestId } : {}
1684
1843
  });
@@ -1689,11 +1848,17 @@ async function* runStream3(options) {
1689
1848
  const contentIndex = event.content_index;
1690
1849
  const key = outputTextKey(itemId, contentIndex);
1691
1850
  outputTextByPart.set(key, `${outputTextByPart.get(key) ?? ""}${delta}`);
1692
- if (itemId && outputItemTypes.get(itemId) === "reasoning") {
1693
- if (options.thinking) yield { type: "thinking_delta", text: delta };
1694
- } else {
1851
+ const itemType = itemId ? outputItemTypes.get(itemId) : void 0;
1852
+ if (itemId && isVisibleOutputItem(itemType)) {
1695
1853
  textAccum += delta;
1696
1854
  yield { type: "text_delta", text: delta };
1855
+ } else if (itemId && itemType == null) {
1856
+ const pending = pendingOutputTextByPart.get(key);
1857
+ pendingOutputTextByPart.set(key, {
1858
+ itemId,
1859
+ contentIndex: contentIndex ?? 0,
1860
+ text: `${pending?.text ?? ""}${delta}`
1861
+ });
1697
1862
  }
1698
1863
  }
1699
1864
  if (type === "response.output_text.done") {
@@ -1706,11 +1871,17 @@ async function* runStream3(options) {
1706
1871
  const missingText = streamedText ? fullText.slice(streamedText.length) : fullText;
1707
1872
  outputTextByPart.set(key, fullText);
1708
1873
  if (missingText && fullText.startsWith(streamedText)) {
1709
- if (itemId && outputItemTypes.get(itemId) === "reasoning") {
1710
- if (options.thinking) yield { type: "thinking_delta", text: missingText };
1711
- } else {
1874
+ const itemType = itemId ? outputItemTypes.get(itemId) : void 0;
1875
+ if (itemId && isVisibleOutputItem(itemType)) {
1712
1876
  textAccum += missingText;
1713
1877
  yield { type: "text_delta", text: missingText };
1878
+ } else if (itemId && itemType == null) {
1879
+ const pending = pendingOutputTextByPart.get(key);
1880
+ pendingOutputTextByPart.set(key, {
1881
+ itemId,
1882
+ contentIndex: contentIndex ?? 0,
1883
+ text: `${pending?.text ?? ""}${missingText}`
1884
+ });
1714
1885
  }
1715
1886
  }
1716
1887
  }
@@ -1729,6 +1900,17 @@ async function* runStream3(options) {
1729
1900
  if (itemType === "reasoning" && options.thinking) {
1730
1901
  yield { type: "thinking_delta", text: "" };
1731
1902
  }
1903
+ if (itemId && itemType) {
1904
+ const pending = [...pendingOutputTextByPart.entries()].filter(([, pendingPart]) => pendingPart.itemId === itemId).sort(([, a], [, b]) => a.contentIndex - b.contentIndex);
1905
+ for (const [key, pendingPart] of pending) {
1906
+ pendingOutputTextByPart.delete(key);
1907
+ if (!pendingPart.text) continue;
1908
+ if (isVisibleOutputItem(itemType)) {
1909
+ textAccum += pendingPart.text;
1910
+ yield { type: "text_delta", text: pendingPart.text };
1911
+ }
1912
+ }
1913
+ }
1732
1914
  }
1733
1915
  if (type === "response.output_item.added") {
1734
1916
  const item = event.item;
@@ -1774,7 +1956,7 @@ async function* runStream3(options) {
1774
1956
  const id = `${callId}|${itemId}`;
1775
1957
  const tc = toolCalls.get(id);
1776
1958
  if (tc) {
1777
- const args = parseToolArguments2(tc.argsJson);
1959
+ const args = parseToolArguments(tc.argsJson);
1778
1960
  yield {
1779
1961
  type: "toolcall_done",
1780
1962
  id: tc.id,
@@ -1798,7 +1980,7 @@ async function* runStream3(options) {
1798
1980
  contentParts.push({ type: "text", text: textAccum });
1799
1981
  }
1800
1982
  for (const [, tc] of toolCalls) {
1801
- const args = parseToolArguments2(tc.argsJson);
1983
+ const args = parseToolArguments(tc.argsJson);
1802
1984
  const toolCall = {
1803
1985
  type: "tool_call",
1804
1986
  id: tc.id,
@@ -1821,33 +2003,13 @@ async function* runStream3(options) {
1821
2003
  return streamResponse;
1822
2004
  }
1823
2005
  async function* parseSSE(body) {
1824
- const reader = body.getReader();
1825
- const decoder = new TextDecoder();
1826
- let buffer = "";
1827
- try {
1828
- while (true) {
1829
- const { done, value } = await reader.read();
1830
- if (done) break;
1831
- buffer += decoder.decode(value, { stream: true });
1832
- let idx = buffer.indexOf("\n\n");
1833
- while (idx !== -1) {
1834
- const chunk = buffer.slice(0, idx);
1835
- buffer = buffer.slice(idx + 2);
1836
- const dataLines = chunk.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim());
1837
- if (dataLines.length > 0) {
1838
- const data = dataLines.join("\n").trim();
1839
- if (data && data !== "[DONE]") {
1840
- try {
1841
- yield JSON.parse(data);
1842
- } catch {
1843
- }
1844
- }
1845
- }
1846
- idx = buffer.indexOf("\n\n");
1847
- }
2006
+ for await (const event of readSseStream(body)) {
2007
+ const data = event.data.trim();
2008
+ if (!data || data === "[DONE]") continue;
2009
+ try {
2010
+ yield JSON.parse(data);
2011
+ } catch {
1848
2012
  }
1849
- } finally {
1850
- reader.releaseLock();
1851
2013
  }
1852
2014
  }
1853
2015
  function remapCodexId(id, idMap) {
@@ -1858,10 +2020,6 @@ function remapCodexId(id, idMap) {
1858
2020
  idMap.set(id, mapped);
1859
2021
  return mapped;
1860
2022
  }
1861
- function codexToolResultText(content) {
1862
- if (typeof content === "string") return content;
1863
- return content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
1864
- }
1865
2023
  function toCodexInput(messages, options) {
1866
2024
  let system;
1867
2025
  const input = [];
@@ -1918,7 +2076,7 @@ function toCodexInput(messages, options) {
1918
2076
  const toolImages = [];
1919
2077
  for (const result of msg.content) {
1920
2078
  const [callId] = result.toolCallId.includes("|") ? result.toolCallId.split("|", 2) : [result.toolCallId];
1921
- const text = codexToolResultText(result.content);
2079
+ const text = toolResultText(result.content);
1922
2080
  input.push({
1923
2081
  type: "function_call_output",
1924
2082
  call_id: remapCodexId(callId, idMap),
@@ -1953,14 +2111,10 @@ function toCodexTools(tools) {
1953
2111
  type: "function",
1954
2112
  name: tool.name,
1955
2113
  description: tool.description,
1956
- parameters: tool.rawInputSchema ?? zodToJsonSchema(tool.parameters),
2114
+ parameters: resolveToolSchema(tool),
1957
2115
  strict: null
1958
2116
  }));
1959
2117
  }
1960
- function extractCodexRequestId(message) {
1961
- const match = message.match(/request ID ([a-z0-9-]{8,})/i);
1962
- return match?.[1];
1963
- }
1964
2118
  function parseCodexErrorBody(text) {
1965
2119
  if (!text) return {};
1966
2120
  try {
@@ -1968,13 +2122,35 @@ function parseCodexErrorBody(text) {
1968
2122
  const error = parsed.error;
1969
2123
  const detail = parsed.detail;
1970
2124
  const message = error?.message ?? parsed.message ?? (typeof detail === "string" ? detail : void 0);
1971
- const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractCodexRequestId(message) : void 0);
1972
- return { ...message ? { message } : {}, ...requestId ? { requestId } : {} };
2125
+ const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractRequestIdFromMessage(message) : void 0);
2126
+ const errorObj = error ?? parsed;
2127
+ return {
2128
+ ...message ? { message } : {},
2129
+ ...requestId ? { requestId } : {},
2130
+ ...errorObj ? { errorObj } : {}
2131
+ };
1973
2132
  } catch {
1974
2133
  const trimmed = text.trim().slice(0, 240);
1975
2134
  return trimmed ? { message: trimmed } : {};
1976
2135
  }
1977
2136
  }
2137
+ var CODEX_USAGE_LIMIT_CODE = /usage_limit_reached|usage_not_included/i;
2138
+ var CODEX_RATE_LIMIT_CODE = /rate_limit_exceeded/i;
2139
+ function codexUsageLimitError(errorObj, statusCode, requestId) {
2140
+ const code = String(errorObj?.code ?? errorObj?.type ?? "");
2141
+ const rateLimits = errorObj?.rate_limits;
2142
+ const resetsAtRaw = (typeof errorObj?.resets_at === "number" ? errorObj.resets_at : void 0) ?? rateLimits?.primary?.resets_at ?? rateLimits?.secondary?.resets_at;
2143
+ const resetsInSeconds = typeof errorObj?.resets_in_seconds === "number" ? errorObj.resets_in_seconds : void 0;
2144
+ const resetsAt = typeof resetsAtRaw === "number" && resetsAtRaw > 0 ? resetsAtRaw : resetsInSeconds != null && resetsInSeconds > 0 ? Math.floor(Date.now() / 1e3) + resetsInSeconds : void 0;
2145
+ const isHardUsage = CODEX_USAGE_LIMIT_CODE.test(code);
2146
+ const isRateOr429 = CODEX_RATE_LIMIT_CODE.test(code) || statusCode === 429;
2147
+ if (!isHardUsage && !(isRateOr429 && resetsAt != null)) return null;
2148
+ return new ProviderError("openai", "ChatGPT usage limit reached", {
2149
+ statusCode: statusCode ?? 429,
2150
+ ...requestId ? { requestId } : {},
2151
+ ...resetsAt ? { resetsAt } : {}
2152
+ });
2153
+ }
1978
2154
 
1979
2155
  // src/providers/gemini.ts
1980
2156
  var DEFAULT_CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com";
@@ -1996,12 +2172,6 @@ var CODE_ASSIST_SUPPORTED_MODELS = /* @__PURE__ */ new Set([
1996
2172
  "gemma-4-31b-it",
1997
2173
  "gemma-4-26b-a4b-it"
1998
2174
  ]);
1999
- function isJsonObject4(value) {
2000
- return value != null && typeof value === "object" && !Array.isArray(value);
2001
- }
2002
- function getEnvironment() {
2003
- return globalThis.process?.env;
2004
- }
2005
2175
  function getGoogleProject(options) {
2006
2176
  const env = getEnvironment();
2007
2177
  return options.projectId ?? env?.GOOGLE_CLOUD_PROJECT ?? env?.GOOGLE_CLOUD_PROJECT_ID;
@@ -2101,7 +2271,7 @@ function toGeminiTools(tools) {
2101
2271
  functionDeclarations: tools.map((tool) => ({
2102
2272
  name: tool.name,
2103
2273
  description: tool.description,
2104
- parameters: sanitizeSchema(tool.rawInputSchema ?? zodToJsonSchema(tool.parameters))
2274
+ parameters: sanitizeSchema(resolveToolSchema(tool))
2105
2275
  }))
2106
2276
  }
2107
2277
  ];
@@ -2112,7 +2282,7 @@ function sanitizeSchema(schema) {
2112
2282
  return clone;
2113
2283
  }
2114
2284
  function stripUnsupportedSchemaFields(value) {
2115
- if (!isJsonObject4(value)) {
2285
+ if (!isJsonObject(value)) {
2116
2286
  if (Array.isArray(value)) {
2117
2287
  for (const item of value) stripUnsupportedSchemaFields(item);
2118
2288
  }
@@ -2121,7 +2291,7 @@ function stripUnsupportedSchemaFields(value) {
2121
2291
  delete value.$schema;
2122
2292
  delete value.additionalProperties;
2123
2293
  for (const item of Object.values(value)) {
2124
- if (isJsonObject4(item) || Array.isArray(item)) {
2294
+ if (isJsonObject(item) || Array.isArray(item)) {
2125
2295
  stripUnsupportedSchemaFields(item);
2126
2296
  }
2127
2297
  }
@@ -2144,6 +2314,7 @@ function toGemini3ThinkingLevel(level) {
2144
2314
  return "MEDIUM";
2145
2315
  case "high":
2146
2316
  case "xhigh":
2317
+ case "max":
2147
2318
  return "HIGH";
2148
2319
  }
2149
2320
  }
@@ -2155,6 +2326,7 @@ function toThinkingBudget(level) {
2155
2326
  return 8192;
2156
2327
  case "high":
2157
2328
  case "xhigh":
2329
+ case "max":
2158
2330
  return 8192;
2159
2331
  }
2160
2332
  }
@@ -2234,54 +2406,11 @@ function normalizeGeminiStopReason(reason) {
2234
2406
  return "end_turn";
2235
2407
  }
2236
2408
  }
2237
- function parseSseEvents(buffer) {
2238
- const events = [];
2239
- let cursor = 0;
2240
- while (true) {
2241
- const next = buffer.indexOf("\n\n", cursor);
2242
- if (next === -1) break;
2243
- const raw = buffer.slice(cursor, next);
2244
- cursor = next + 2;
2245
- let eventName;
2246
- const dataLines = [];
2247
- for (const line of raw.split("\n")) {
2248
- if (line.startsWith("event:")) {
2249
- eventName = line.slice("event:".length).trim();
2250
- } else if (line.startsWith("data:")) {
2251
- dataLines.push(line.slice("data:".length).trimStart());
2252
- }
2253
- }
2254
- if (dataLines.length > 0) {
2255
- events.push({ event: eventName, data: dataLines.join("\n") });
2256
- }
2257
- }
2258
- return { events, remaining: buffer.slice(cursor) };
2259
- }
2260
2409
  async function* streamSse(response) {
2261
2410
  if (!response.body) return;
2262
- const reader = response.body.getReader();
2263
- const decoder = new TextDecoder();
2264
- let buffer = "";
2265
- try {
2266
- while (true) {
2267
- const { done, value } = await reader.read();
2268
- if (done) break;
2269
- buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
2270
- const parsed2 = parseSseEvents(buffer);
2271
- buffer = parsed2.remaining;
2272
- for (const event of parsed2.events) {
2273
- if (event.data === "[DONE]") continue;
2274
- yield JSON.parse(event.data);
2275
- }
2276
- }
2277
- buffer += decoder.decode().replace(/\r\n/g, "\n");
2278
- const parsed = parseSseEvents(buffer + "\n\n");
2279
- for (const event of parsed.events) {
2280
- if (event.data === "[DONE]") continue;
2281
- yield JSON.parse(event.data);
2282
- }
2283
- } finally {
2284
- reader.releaseLock();
2411
+ for await (const event of readSseStream(response.body)) {
2412
+ if (event.data === "[DONE]") continue;
2413
+ yield JSON.parse(event.data);
2285
2414
  }
2286
2415
  }
2287
2416
  function candidatesFromResponse(response) {
@@ -2304,7 +2433,7 @@ function readFunctionCallPart(part) {
2304
2433
  return {
2305
2434
  ...part.functionCall.id ? { id: part.functionCall.id } : {},
2306
2435
  name: part.functionCall.name,
2307
- args: isJsonObject4(part.functionCall.args) ? part.functionCall.args : {}
2436
+ args: isJsonObject(part.functionCall.args) ? part.functionCall.args : {}
2308
2437
  };
2309
2438
  }
2310
2439
  function makeToolCallId(index, providerId) {
@@ -2736,6 +2865,7 @@ function registerPalsuProvider(config) {
2736
2865
  StreamResult,
2737
2866
  formatError,
2738
2867
  formatErrorForDisplay,
2868
+ isUsageLimitError,
2739
2869
  palsuAssistantMessage,
2740
2870
  palsuText,
2741
2871
  palsuThinking,
@@ -2743,6 +2873,8 @@ function registerPalsuProvider(config) {
2743
2873
  providerRegistry,
2744
2874
  registerPalsuProvider,
2745
2875
  setProviderDiagnostic,
2746
- stream
2876
+ stream,
2877
+ toAnthropicMessages,
2878
+ toOpenAIMessages
2747
2879
  });
2748
2880
  //# sourceMappingURL=index.cjs.map