@kenkaiiii/gg-ai 4.3.227 → 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.d.cts CHANGED
@@ -335,6 +335,8 @@ interface FormattedError {
335
335
  statusCode?: number;
336
336
  /** Provider request ID, kept for telemetry / debug — not shown by default. */
337
337
  requestId?: string;
338
+ /** Unix seconds when a usage/rate limit resets, when the provider reports it. */
339
+ resetsAt?: number;
338
340
  }
339
341
  declare class GGAIError extends Error {
340
342
  readonly source: ErrorSource;
@@ -350,11 +352,14 @@ declare class GGAIError extends Error {
350
352
  declare class ProviderError extends GGAIError {
351
353
  readonly provider: string;
352
354
  readonly statusCode?: number;
355
+ /** Unix seconds when a usage/rate limit resets, when the provider reports it. */
356
+ readonly resetsAt?: number;
353
357
  constructor(provider: string, message: string, options?: {
354
358
  statusCode?: number;
355
359
  requestId?: string;
356
360
  hint?: string;
357
361
  cause?: unknown;
362
+ resetsAt?: number;
358
363
  });
359
364
  }
360
365
  /**
@@ -362,6 +367,14 @@ declare class ProviderError extends GGAIError {
362
367
  * a non-empty `headline` and `guidance` so the UI never has to second-guess
363
368
  * what to show the user.
364
369
  */
370
+ /**
371
+ * Is this a subscription/plan usage-window exhaustion error (as opposed to a
372
+ * transient per-minute throttle)? These don't clear with a quick retry — the
373
+ * user has to wait for the window to reset — so callers must surface them as a
374
+ * hard stop, not silently retry for minutes. Detected from the canonical
375
+ * "usage limit reached" message gg-ai stamps onto the ProviderError.
376
+ */
377
+ declare function isUsageLimitError(err: unknown): boolean;
365
378
  declare function formatError(err: unknown): FormattedError;
366
379
  /**
367
380
  * Render a FormattedError as a multi-line string for terminal display.
@@ -452,4 +465,4 @@ interface PalsuProviderConfig {
452
465
  */
453
466
  declare function registerPalsuProvider(config?: PalsuProviderConfig): PalsuProviderHandle;
454
467
 
455
- export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderDiagnosticFn, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, formatError, formatErrorForDisplay, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream };
468
+ export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderDiagnosticFn, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, formatError, formatErrorForDisplay, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream };
package/dist/index.d.ts CHANGED
@@ -335,6 +335,8 @@ interface FormattedError {
335
335
  statusCode?: number;
336
336
  /** Provider request ID, kept for telemetry / debug — not shown by default. */
337
337
  requestId?: string;
338
+ /** Unix seconds when a usage/rate limit resets, when the provider reports it. */
339
+ resetsAt?: number;
338
340
  }
339
341
  declare class GGAIError extends Error {
340
342
  readonly source: ErrorSource;
@@ -350,11 +352,14 @@ declare class GGAIError extends Error {
350
352
  declare class ProviderError extends GGAIError {
351
353
  readonly provider: string;
352
354
  readonly statusCode?: number;
355
+ /** Unix seconds when a usage/rate limit resets, when the provider reports it. */
356
+ readonly resetsAt?: number;
353
357
  constructor(provider: string, message: string, options?: {
354
358
  statusCode?: number;
355
359
  requestId?: string;
356
360
  hint?: string;
357
361
  cause?: unknown;
362
+ resetsAt?: number;
358
363
  });
359
364
  }
360
365
  /**
@@ -362,6 +367,14 @@ declare class ProviderError extends GGAIError {
362
367
  * a non-empty `headline` and `guidance` so the UI never has to second-guess
363
368
  * what to show the user.
364
369
  */
370
+ /**
371
+ * Is this a subscription/plan usage-window exhaustion error (as opposed to a
372
+ * transient per-minute throttle)? These don't clear with a quick retry — the
373
+ * user has to wait for the window to reset — so callers must surface them as a
374
+ * hard stop, not silently retry for minutes. Detected from the canonical
375
+ * "usage limit reached" message gg-ai stamps onto the ProviderError.
376
+ */
377
+ declare function isUsageLimitError(err: unknown): boolean;
365
378
  declare function formatError(err: unknown): FormattedError;
366
379
  /**
367
380
  * Render a FormattedError as a multi-line string for terminal display.
@@ -452,4 +465,4 @@ interface PalsuProviderConfig {
452
465
  */
453
466
  declare function registerPalsuProvider(config?: PalsuProviderConfig): PalsuProviderHandle;
454
467
 
455
- export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderDiagnosticFn, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, formatError, formatErrorForDisplay, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream };
468
+ export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderDiagnosticFn, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, formatError, formatErrorForDisplay, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream };
package/dist/index.js CHANGED
@@ -14,6 +14,8 @@ var GGAIError = class extends Error {
14
14
  var ProviderError = class extends GGAIError {
15
15
  provider;
16
16
  statusCode;
17
+ /** Unix seconds when a usage/rate limit resets, when the provider reports it. */
18
+ resetsAt;
17
19
  constructor(provider, message, options) {
18
20
  super(message, {
19
21
  source: "provider",
@@ -24,6 +26,7 @@ var ProviderError = class extends GGAIError {
24
26
  this.name = "ProviderError";
25
27
  this.provider = provider;
26
28
  this.statusCode = options?.statusCode;
29
+ this.resetsAt = options?.resetsAt;
27
30
  }
28
31
  };
29
32
  var PROVIDER_DISPLAY = {
@@ -44,10 +47,36 @@ var PROVIDER_STATUS_URL = {
44
47
  function providerDisplayName(provider) {
45
48
  return PROVIDER_DISPLAY[provider] ?? provider;
46
49
  }
50
+ function isUsageLimitError(err) {
51
+ if (!(err instanceof Error)) return false;
52
+ return /usage limit reached/i.test(err.message);
53
+ }
54
+ function formatResetTime(resetsAt) {
55
+ const when = new Date(resetsAt * 1e3);
56
+ const sameDay = when.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
57
+ return sameDay ? when.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" }) : when.toLocaleString(void 0, {
58
+ weekday: "short",
59
+ hour: "numeric",
60
+ minute: "2-digit"
61
+ });
62
+ }
47
63
  function formatError(err) {
48
64
  if (err instanceof ProviderError) {
49
65
  const name = providerDisplayName(err.provider);
50
66
  const cleanMessage = cleanProviderMessage(err.message);
67
+ if (isUsageLimitError(err)) {
68
+ const resetClause = err.resetsAt ? ` It resets at ${formatResetTime(err.resetsAt)}.` : "";
69
+ return {
70
+ headline: `${name} usage limit reached.`,
71
+ source: "provider",
72
+ message: `Your ${name} usage is finished.${resetClause}`,
73
+ provider: err.provider,
74
+ statusCode: err.statusCode,
75
+ ...err.requestId ? { requestId: err.requestId } : {},
76
+ ...err.resetsAt ? { resetsAt: err.resetsAt } : {},
77
+ guidance: "Try again once it's back. Your conversation is preserved."
78
+ };
79
+ }
51
80
  return {
52
81
  headline: `${name} returned an error.`,
53
82
  source: "provider",
@@ -327,6 +356,14 @@ function normalizeRootForAnthropic(schema) {
327
356
  }
328
357
 
329
358
  // src/providers/transform.ts
359
+ function isPositionSensitiveThinking(part) {
360
+ if (part.type === "thinking") return !!part.signature;
361
+ if (part.type === "raw") {
362
+ const t = part.data.type;
363
+ return t === "thinking" || t === "redacted_thinking";
364
+ }
365
+ return false;
366
+ }
330
367
  var NON_VISION_USER_IMAGE_PLACEHOLDER = "(image omitted: model does not support images)";
331
368
  var NON_VISION_TOOL_IMAGE_PLACEHOLDER = "(tool image omitted: model does not support images)";
332
369
  function stripImages(content, placeholder) {
@@ -426,14 +463,19 @@ function toAnthropicMessages(messages, cacheControl) {
426
463
  continue;
427
464
  }
428
465
  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;
466
+ const lastThinkingIdx = typeof msg.content === "string" ? -1 : msg.content.reduce(
467
+ (last, part, idx) => isPositionSensitiveThinking(part) ? idx : last,
468
+ -1
469
+ );
470
+ const content = typeof msg.content === "string" ? msg.content : msg.content.filter((part, idx) => {
471
+ if (part.type === "thinking" && !part.signature && !part.text) return false;
472
+ if (part.type === "text" && !part.text && idx > lastThinkingIdx) return false;
432
473
  return true;
433
474
  }).map((part) => {
434
475
  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 };
476
+ if (part.type === "thinking") {
477
+ return part.signature ? { type: "thinking", thinking: part.text, signature: part.signature } : { type: "text", text: part.text };
478
+ }
437
479
  if (part.type === "tool_call")
438
480
  return {
439
481
  type: "tool_use",
@@ -1138,6 +1180,24 @@ function messageToResponse(message) {
1138
1180
  }
1139
1181
  };
1140
1182
  }
1183
+ function readUnifiedRateLimit(headers) {
1184
+ const get = (name) => {
1185
+ if (headers && typeof headers.get === "function") {
1186
+ return headers.get(name);
1187
+ }
1188
+ if (headers && typeof headers === "object") {
1189
+ const rec = headers;
1190
+ const value = rec[name] ?? rec[name.toLowerCase()];
1191
+ return typeof value === "string" ? value : null;
1192
+ }
1193
+ return null;
1194
+ };
1195
+ const status = get("anthropic-ratelimit-unified-status");
1196
+ const resetRaw = get("anthropic-ratelimit-unified-reset") ?? get("anthropic-ratelimit-unified-5h-reset") ?? get("anthropic-ratelimit-unified-7d-reset");
1197
+ const resetNum = resetRaw != null ? Number(resetRaw) : Number.NaN;
1198
+ const resetsAt = Number.isFinite(resetNum) && resetNum > 0 ? resetNum : void 0;
1199
+ return { rejected: status === "rejected", ...resetsAt ? { resetsAt } : {} };
1200
+ }
1141
1201
  function toError(err) {
1142
1202
  if (err instanceof Anthropic.APIError) {
1143
1203
  const errorBody = err.error;
@@ -1146,6 +1206,18 @@ function toError(err) {
1146
1206
  const bodyMessage = typeof nestedError?.message === "string" ? nestedError.message : typeof errorBody?.message === "string" ? errorBody.message : void 0;
1147
1207
  const bodyType = typeof nestedError?.type === "string" ? nestedError.type : typeof errorBody?.type === "string" ? errorBody.type : typeof err.type === "string" ? err.type : void 0;
1148
1208
  const message = bodyType && bodyMessage ? `${bodyType}: ${bodyMessage}` : bodyMessage ?? err.message;
1209
+ if (err.status === 429) {
1210
+ const limit = readUnifiedRateLimit(err.headers);
1211
+ const farOff = limit.resetsAt != null && limit.resetsAt * 1e3 - Date.now() > 6e4;
1212
+ if (limit.rejected || farOff) {
1213
+ return new ProviderError("anthropic", "Claude usage limit reached", {
1214
+ statusCode: 429,
1215
+ ...requestId ? { requestId } : {},
1216
+ ...limit.resetsAt ? { resetsAt: limit.resetsAt } : {},
1217
+ cause: err
1218
+ });
1219
+ }
1220
+ }
1149
1221
  return new ProviderError("anthropic", message, {
1150
1222
  statusCode: err.status,
1151
1223
  ...requestId ? { requestId } : {},
@@ -1583,6 +1655,8 @@ async function* runStream3(options) {
1583
1655
  const parsed = parseCodexErrorBody(text);
1584
1656
  const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
1585
1657
  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;
1658
+ const usageLimit = codexUsageLimitError(parsed.errorObj, response.status, requestId);
1659
+ if (usageLimit) throw usageLimit;
1586
1660
  let hint;
1587
1661
  if (response.status === 400 && text.includes("not supported")) {
1588
1662
  if (options.model === "gpt-5.5-pro") {
@@ -1625,6 +1699,12 @@ async function* runStream3(options) {
1625
1699
  const message = nested?.message ?? event.message ?? "Codex stream emitted an error chunk without a message.";
1626
1700
  const code = nested?.code ?? nested?.type ?? event.code ?? "server_error";
1627
1701
  const requestId = extractCodexRequestId(message) ?? event.request_id;
1702
+ const usageLimit = codexUsageLimitError(
1703
+ nested ?? event,
1704
+ void 0,
1705
+ requestId
1706
+ );
1707
+ if (usageLimit) throw usageLimit;
1628
1708
  throw new ProviderError("openai", message, {
1629
1709
  ...requestId != null ? { requestId } : {},
1630
1710
  ...code === "server_error" ? { statusCode: 500 } : {}
@@ -1947,12 +2027,34 @@ function parseCodexErrorBody(text) {
1947
2027
  const detail = parsed.detail;
1948
2028
  const message = error?.message ?? parsed.message ?? (typeof detail === "string" ? detail : void 0);
1949
2029
  const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractCodexRequestId(message) : void 0);
1950
- return { ...message ? { message } : {}, ...requestId ? { requestId } : {} };
2030
+ const errorObj = error ?? parsed;
2031
+ return {
2032
+ ...message ? { message } : {},
2033
+ ...requestId ? { requestId } : {},
2034
+ ...errorObj ? { errorObj } : {}
2035
+ };
1951
2036
  } catch {
1952
2037
  const trimmed = text.trim().slice(0, 240);
1953
2038
  return trimmed ? { message: trimmed } : {};
1954
2039
  }
1955
2040
  }
2041
+ var CODEX_USAGE_LIMIT_CODE = /usage_limit_reached|usage_not_included/i;
2042
+ var CODEX_RATE_LIMIT_CODE = /rate_limit_exceeded/i;
2043
+ function codexUsageLimitError(errorObj, statusCode, requestId) {
2044
+ const code = String(errorObj?.code ?? errorObj?.type ?? "");
2045
+ const rateLimits = errorObj?.rate_limits;
2046
+ const resetsAtRaw = (typeof errorObj?.resets_at === "number" ? errorObj.resets_at : void 0) ?? rateLimits?.primary?.resets_at ?? rateLimits?.secondary?.resets_at;
2047
+ const resetsInSeconds = typeof errorObj?.resets_in_seconds === "number" ? errorObj.resets_in_seconds : void 0;
2048
+ const resetsAt = typeof resetsAtRaw === "number" && resetsAtRaw > 0 ? resetsAtRaw : resetsInSeconds != null && resetsInSeconds > 0 ? Math.floor(Date.now() / 1e3) + resetsInSeconds : void 0;
2049
+ const isHardUsage = CODEX_USAGE_LIMIT_CODE.test(code);
2050
+ const isRateOr429 = CODEX_RATE_LIMIT_CODE.test(code) || statusCode === 429;
2051
+ if (!isHardUsage && !(isRateOr429 && resetsAt != null)) return null;
2052
+ return new ProviderError("openai", "ChatGPT usage limit reached", {
2053
+ statusCode: statusCode ?? 429,
2054
+ ...requestId ? { requestId } : {},
2055
+ ...resetsAt ? { resetsAt } : {}
2056
+ });
2057
+ }
1956
2058
 
1957
2059
  // src/providers/gemini.ts
1958
2060
  var DEFAULT_CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com";
@@ -2715,6 +2817,7 @@ export {
2715
2817
  StreamResult,
2716
2818
  formatError,
2717
2819
  formatErrorForDisplay,
2820
+ isUsageLimitError,
2718
2821
  palsuAssistantMessage,
2719
2822
  palsuText,
2720
2823
  palsuThinking,