@kenkaiiii/gg-ai 4.3.226 → 4.3.228

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,
@@ -63,6 +64,8 @@ var GGAIError = class extends Error {
63
64
  var ProviderError = class extends GGAIError {
64
65
  provider;
65
66
  statusCode;
67
+ /** Unix seconds when a usage/rate limit resets, when the provider reports it. */
68
+ resetsAt;
66
69
  constructor(provider, message, options) {
67
70
  super(message, {
68
71
  source: "provider",
@@ -73,6 +76,7 @@ var ProviderError = class extends GGAIError {
73
76
  this.name = "ProviderError";
74
77
  this.provider = provider;
75
78
  this.statusCode = options?.statusCode;
79
+ this.resetsAt = options?.resetsAt;
76
80
  }
77
81
  };
78
82
  var PROVIDER_DISPLAY = {
@@ -93,10 +97,36 @@ var PROVIDER_STATUS_URL = {
93
97
  function providerDisplayName(provider) {
94
98
  return PROVIDER_DISPLAY[provider] ?? provider;
95
99
  }
100
+ function isUsageLimitError(err) {
101
+ if (!(err instanceof Error)) return false;
102
+ return /usage limit reached/i.test(err.message);
103
+ }
104
+ function formatResetTime(resetsAt) {
105
+ const when = new Date(resetsAt * 1e3);
106
+ const sameDay = when.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
107
+ return sameDay ? when.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" }) : when.toLocaleString(void 0, {
108
+ weekday: "short",
109
+ hour: "numeric",
110
+ minute: "2-digit"
111
+ });
112
+ }
96
113
  function formatError(err) {
97
114
  if (err instanceof ProviderError) {
98
115
  const name = providerDisplayName(err.provider);
99
116
  const cleanMessage = cleanProviderMessage(err.message);
117
+ if (isUsageLimitError(err)) {
118
+ const resetClause = err.resetsAt ? ` It resets at ${formatResetTime(err.resetsAt)}.` : "";
119
+ return {
120
+ headline: `${name} usage limit reached.`,
121
+ source: "provider",
122
+ message: `Your ${name} usage is finished.${resetClause}`,
123
+ provider: err.provider,
124
+ statusCode: err.statusCode,
125
+ ...err.requestId ? { requestId: err.requestId } : {},
126
+ ...err.resetsAt ? { resetsAt: err.resetsAt } : {},
127
+ guidance: "Try again once it's back. Your conversation is preserved."
128
+ };
129
+ }
100
130
  return {
101
131
  headline: `${name} returned an error.`,
102
132
  source: "provider",
@@ -376,6 +406,14 @@ function normalizeRootForAnthropic(schema) {
376
406
  }
377
407
 
378
408
  // src/providers/transform.ts
409
+ function isPositionSensitiveThinking(part) {
410
+ if (part.type === "thinking") return !!part.signature;
411
+ if (part.type === "raw") {
412
+ const t = part.data.type;
413
+ return t === "thinking" || t === "redacted_thinking";
414
+ }
415
+ return false;
416
+ }
379
417
  var NON_VISION_USER_IMAGE_PLACEHOLDER = "(image omitted: model does not support images)";
380
418
  var NON_VISION_TOOL_IMAGE_PLACEHOLDER = "(tool image omitted: model does not support images)";
381
419
  function stripImages(content, placeholder) {
@@ -475,14 +513,19 @@ function toAnthropicMessages(messages, cacheControl) {
475
513
  continue;
476
514
  }
477
515
  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;
516
+ const lastThinkingIdx = typeof msg.content === "string" ? -1 : msg.content.reduce(
517
+ (last, part, idx) => isPositionSensitiveThinking(part) ? idx : last,
518
+ -1
519
+ );
520
+ const content = typeof msg.content === "string" ? msg.content : msg.content.filter((part, idx) => {
521
+ if (part.type === "thinking" && !part.signature && !part.text) return false;
522
+ if (part.type === "text" && !part.text && idx > lastThinkingIdx) return false;
481
523
  return true;
482
524
  }).map((part) => {
483
525
  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 };
526
+ if (part.type === "thinking") {
527
+ return part.signature ? { type: "thinking", thinking: part.text, signature: part.signature } : { type: "text", text: part.text };
528
+ }
486
529
  if (part.type === "tool_call")
487
530
  return {
488
531
  type: "tool_use",
@@ -1187,6 +1230,24 @@ function messageToResponse(message) {
1187
1230
  }
1188
1231
  };
1189
1232
  }
1233
+ function readUnifiedRateLimit(headers) {
1234
+ const get = (name) => {
1235
+ if (headers && typeof headers.get === "function") {
1236
+ return headers.get(name);
1237
+ }
1238
+ if (headers && typeof headers === "object") {
1239
+ const rec = headers;
1240
+ const value = rec[name] ?? rec[name.toLowerCase()];
1241
+ return typeof value === "string" ? value : null;
1242
+ }
1243
+ return null;
1244
+ };
1245
+ const status = get("anthropic-ratelimit-unified-status");
1246
+ const resetRaw = get("anthropic-ratelimit-unified-reset") ?? get("anthropic-ratelimit-unified-5h-reset") ?? get("anthropic-ratelimit-unified-7d-reset");
1247
+ const resetNum = resetRaw != null ? Number(resetRaw) : Number.NaN;
1248
+ const resetsAt = Number.isFinite(resetNum) && resetNum > 0 ? resetNum : void 0;
1249
+ return { rejected: status === "rejected", ...resetsAt ? { resetsAt } : {} };
1250
+ }
1190
1251
  function toError(err) {
1191
1252
  if (err instanceof import_sdk.default.APIError) {
1192
1253
  const errorBody = err.error;
@@ -1195,6 +1256,18 @@ function toError(err) {
1195
1256
  const bodyMessage = typeof nestedError?.message === "string" ? nestedError.message : typeof errorBody?.message === "string" ? errorBody.message : void 0;
1196
1257
  const bodyType = typeof nestedError?.type === "string" ? nestedError.type : typeof errorBody?.type === "string" ? errorBody.type : typeof err.type === "string" ? err.type : void 0;
1197
1258
  const message = bodyType && bodyMessage ? `${bodyType}: ${bodyMessage}` : bodyMessage ?? err.message;
1259
+ if (err.status === 429) {
1260
+ const limit = readUnifiedRateLimit(err.headers);
1261
+ const farOff = limit.resetsAt != null && limit.resetsAt * 1e3 - Date.now() > 6e4;
1262
+ if (limit.rejected || farOff) {
1263
+ return new ProviderError("anthropic", "Claude usage limit reached", {
1264
+ statusCode: 429,
1265
+ ...requestId ? { requestId } : {},
1266
+ ...limit.resetsAt ? { resetsAt: limit.resetsAt } : {},
1267
+ cause: err
1268
+ });
1269
+ }
1270
+ }
1198
1271
  return new ProviderError("anthropic", message, {
1199
1272
  statusCode: err.status,
1200
1273
  ...requestId ? { requestId } : {},
@@ -1632,6 +1705,8 @@ async function* runStream3(options) {
1632
1705
  const parsed = parseCodexErrorBody(text);
1633
1706
  const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
1634
1707
  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;
1708
+ const usageLimit = codexUsageLimitError(parsed.errorObj, response.status, requestId);
1709
+ if (usageLimit) throw usageLimit;
1635
1710
  let hint;
1636
1711
  if (response.status === 400 && text.includes("not supported")) {
1637
1712
  if (options.model === "gpt-5.5-pro") {
@@ -1674,6 +1749,12 @@ async function* runStream3(options) {
1674
1749
  const message = nested?.message ?? event.message ?? "Codex stream emitted an error chunk without a message.";
1675
1750
  const code = nested?.code ?? nested?.type ?? event.code ?? "server_error";
1676
1751
  const requestId = extractCodexRequestId(message) ?? event.request_id;
1752
+ const usageLimit = codexUsageLimitError(
1753
+ nested ?? event,
1754
+ void 0,
1755
+ requestId
1756
+ );
1757
+ if (usageLimit) throw usageLimit;
1677
1758
  throw new ProviderError("openai", message, {
1678
1759
  ...requestId != null ? { requestId } : {},
1679
1760
  ...code === "server_error" ? { statusCode: 500 } : {}
@@ -1996,12 +2077,34 @@ function parseCodexErrorBody(text) {
1996
2077
  const detail = parsed.detail;
1997
2078
  const message = error?.message ?? parsed.message ?? (typeof detail === "string" ? detail : void 0);
1998
2079
  const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractCodexRequestId(message) : void 0);
1999
- return { ...message ? { message } : {}, ...requestId ? { requestId } : {} };
2080
+ const errorObj = error ?? parsed;
2081
+ return {
2082
+ ...message ? { message } : {},
2083
+ ...requestId ? { requestId } : {},
2084
+ ...errorObj ? { errorObj } : {}
2085
+ };
2000
2086
  } catch {
2001
2087
  const trimmed = text.trim().slice(0, 240);
2002
2088
  return trimmed ? { message: trimmed } : {};
2003
2089
  }
2004
2090
  }
2091
+ var CODEX_USAGE_LIMIT_CODE = /usage_limit_reached|usage_not_included/i;
2092
+ var CODEX_RATE_LIMIT_CODE = /rate_limit_exceeded/i;
2093
+ function codexUsageLimitError(errorObj, statusCode, requestId) {
2094
+ const code = String(errorObj?.code ?? errorObj?.type ?? "");
2095
+ const rateLimits = errorObj?.rate_limits;
2096
+ const resetsAtRaw = (typeof errorObj?.resets_at === "number" ? errorObj.resets_at : void 0) ?? rateLimits?.primary?.resets_at ?? rateLimits?.secondary?.resets_at;
2097
+ const resetsInSeconds = typeof errorObj?.resets_in_seconds === "number" ? errorObj.resets_in_seconds : void 0;
2098
+ const resetsAt = typeof resetsAtRaw === "number" && resetsAtRaw > 0 ? resetsAtRaw : resetsInSeconds != null && resetsInSeconds > 0 ? Math.floor(Date.now() / 1e3) + resetsInSeconds : void 0;
2099
+ const isHardUsage = CODEX_USAGE_LIMIT_CODE.test(code);
2100
+ const isRateOr429 = CODEX_RATE_LIMIT_CODE.test(code) || statusCode === 429;
2101
+ if (!isHardUsage && !(isRateOr429 && resetsAt != null)) return null;
2102
+ return new ProviderError("openai", "ChatGPT usage limit reached", {
2103
+ statusCode: statusCode ?? 429,
2104
+ ...requestId ? { requestId } : {},
2105
+ ...resetsAt ? { resetsAt } : {}
2106
+ });
2107
+ }
2005
2108
 
2006
2109
  // src/providers/gemini.ts
2007
2110
  var DEFAULT_CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com";
@@ -2765,6 +2868,7 @@ function registerPalsuProvider(config) {
2765
2868
  StreamResult,
2766
2869
  formatError,
2767
2870
  formatErrorForDisplay,
2871
+ isUsageLimitError,
2768
2872
  palsuAssistantMessage,
2769
2873
  palsuText,
2770
2874
  palsuThinking,