@kenkaiiii/gg-ai 5.82.0 → 5.83.0

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
@@ -454,6 +454,23 @@ interface FastModeResolution {
454
454
  * whose base doesn't support fast mode for `provider`, pass through unchanged.
455
455
  */
456
456
  declare function resolveFastModeModel(provider: Provider, model: string): FastModeResolution;
457
+ interface FastModeRejection {
458
+ /** Anthropic's reason code (`unknown` when it sent none). */
459
+ reason: string;
460
+ /** Short, factual headline for the error card. */
461
+ headline: string;
462
+ /** What the user can do about it. */
463
+ guidance: string;
464
+ /** "billing": credits ran out or hit a cap. "access": credits are off or not offered. */
465
+ kind: "billing" | "access";
466
+ }
467
+ /** Describe a fast-mode rejection reason in user terms. */
468
+ declare function describeFastModeRejection(reason: string | undefined): FastModeRejection;
469
+ /**
470
+ * Recover the rejection from an error message: gg-ai's canonical message, or
471
+ * Anthropic's raw fast-mode wording (when it reaches a caller unnormalized).
472
+ */
473
+ declare function parseFastModeRejection(message: string): FastModeRejection | undefined;
457
474
 
458
475
  /** What the embedded text will be used for. See `EmbedOptions.inputType`. */
459
476
  type EmbedInputType = "query" | "document";
@@ -1054,4 +1071,4 @@ declare function palsuEmbedding(text: string, dimensions?: number): number[];
1054
1071
  /** Deterministic fake relevance: the share of distinct query words found in the document (0–1). */
1055
1072
  declare function palsuRerankScore(query: string, document: string): number;
1056
1073
 
1057
- export { type AssistantMessage, type CacheRetention, type ChatErrorReason, type ContentPart, type DoneEvent, type EmbedInputType, type EmbedOptions, type EmbedResult, type EmbedUsage, type ErrorEvent, type ErrorSource, EventStream, FAST_MODE_SUFFIX, type FastModeResolution, type FormattedChatError, type FormattedError, GGAIError, type ImageContent, type Message, type MessageProvenance, type MessageProvenanceKind, type MessageProvenanceSource, type MessageProvenanceVisibility, type OpenRouterProviderPreferences, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type PreparedContext, type Provider, type ProviderCapability, type ProviderDiagnosticFn, type ProviderEmbedFn, type ProviderEmbedRequest, type ProviderEmbedResponse, type ProviderEntry, ProviderError, type ProviderOptions, type ProviderRerankFn, type ProviderRerankRequest, type ProviderRerankResponse, type ProviderStreamFn, REDACTED as REDACTION_MARKER, type RawContent, type RedactionOptions, type RerankOptions, type RerankResponse, type RerankResult, type RerankUsage, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, TOOL_CALL_NAME_RULES, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolCallNameRule, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, type VideoContent, clampProviderContextImages, classifyProviderError, continuesTurn, dropInvalidToolCalls, embed, environmentSecrets, formatChatError, formatError, formatErrorForDisplay, hasLoneSurrogate, isHardBillingMessage, isUsageLimitError, isValidToolCallName, localWireModelId, opensTurn, palsuAssistantMessage, palsuEmbedding, palsuRerankScore, palsuText, palsuThinking, palsuToolCall, prewarmAnthropicCache, providerRegistry, redactText, redactValue, registerPalsuProvider, rerank, resolveFastModeModel, resolveToolSchema, sanitizeMessagesForWire, setProviderDiagnostic, sliceHead, sliceTail, stream, toAnthropicMessages, toOpenAIMessages, toWellFormedText, toolCallNameRuleFor, usesResponsesLite };
1074
+ export { type AssistantMessage, type CacheRetention, type ChatErrorReason, type ContentPart, type DoneEvent, type EmbedInputType, type EmbedOptions, type EmbedResult, type EmbedUsage, type ErrorEvent, type ErrorSource, EventStream, FAST_MODE_SUFFIX, type FastModeRejection, type FastModeResolution, type FormattedChatError, type FormattedError, GGAIError, type ImageContent, type Message, type MessageProvenance, type MessageProvenanceKind, type MessageProvenanceSource, type MessageProvenanceVisibility, type OpenRouterProviderPreferences, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type PreparedContext, type Provider, type ProviderCapability, type ProviderDiagnosticFn, type ProviderEmbedFn, type ProviderEmbedRequest, type ProviderEmbedResponse, type ProviderEntry, ProviderError, type ProviderOptions, type ProviderRerankFn, type ProviderRerankRequest, type ProviderRerankResponse, type ProviderStreamFn, REDACTED as REDACTION_MARKER, type RawContent, type RedactionOptions, type RerankOptions, type RerankResponse, type RerankResult, type RerankUsage, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, TOOL_CALL_NAME_RULES, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolCallNameRule, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, type VideoContent, clampProviderContextImages, classifyProviderError, continuesTurn, describeFastModeRejection, dropInvalidToolCalls, embed, environmentSecrets, formatChatError, formatError, formatErrorForDisplay, hasLoneSurrogate, isHardBillingMessage, isUsageLimitError, isValidToolCallName, localWireModelId, opensTurn, palsuAssistantMessage, palsuEmbedding, palsuRerankScore, palsuText, palsuThinking, palsuToolCall, parseFastModeRejection, prewarmAnthropicCache, providerRegistry, redactText, redactValue, registerPalsuProvider, rerank, resolveFastModeModel, resolveToolSchema, sanitizeMessagesForWire, setProviderDiagnostic, sliceHead, sliceTail, stream, toAnthropicMessages, toOpenAIMessages, toWellFormedText, toolCallNameRuleFor, usesResponsesLite };
package/dist/index.d.ts CHANGED
@@ -454,6 +454,23 @@ interface FastModeResolution {
454
454
  * whose base doesn't support fast mode for `provider`, pass through unchanged.
455
455
  */
456
456
  declare function resolveFastModeModel(provider: Provider, model: string): FastModeResolution;
457
+ interface FastModeRejection {
458
+ /** Anthropic's reason code (`unknown` when it sent none). */
459
+ reason: string;
460
+ /** Short, factual headline for the error card. */
461
+ headline: string;
462
+ /** What the user can do about it. */
463
+ guidance: string;
464
+ /** "billing": credits ran out or hit a cap. "access": credits are off or not offered. */
465
+ kind: "billing" | "access";
466
+ }
467
+ /** Describe a fast-mode rejection reason in user terms. */
468
+ declare function describeFastModeRejection(reason: string | undefined): FastModeRejection;
469
+ /**
470
+ * Recover the rejection from an error message: gg-ai's canonical message, or
471
+ * Anthropic's raw fast-mode wording (when it reaches a caller unnormalized).
472
+ */
473
+ declare function parseFastModeRejection(message: string): FastModeRejection | undefined;
457
474
 
458
475
  /** What the embedded text will be used for. See `EmbedOptions.inputType`. */
459
476
  type EmbedInputType = "query" | "document";
@@ -1054,4 +1071,4 @@ declare function palsuEmbedding(text: string, dimensions?: number): number[];
1054
1071
  /** Deterministic fake relevance: the share of distinct query words found in the document (0–1). */
1055
1072
  declare function palsuRerankScore(query: string, document: string): number;
1056
1073
 
1057
- export { type AssistantMessage, type CacheRetention, type ChatErrorReason, type ContentPart, type DoneEvent, type EmbedInputType, type EmbedOptions, type EmbedResult, type EmbedUsage, type ErrorEvent, type ErrorSource, EventStream, FAST_MODE_SUFFIX, type FastModeResolution, type FormattedChatError, type FormattedError, GGAIError, type ImageContent, type Message, type MessageProvenance, type MessageProvenanceKind, type MessageProvenanceSource, type MessageProvenanceVisibility, type OpenRouterProviderPreferences, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type PreparedContext, type Provider, type ProviderCapability, type ProviderDiagnosticFn, type ProviderEmbedFn, type ProviderEmbedRequest, type ProviderEmbedResponse, type ProviderEntry, ProviderError, type ProviderOptions, type ProviderRerankFn, type ProviderRerankRequest, type ProviderRerankResponse, type ProviderStreamFn, REDACTED as REDACTION_MARKER, type RawContent, type RedactionOptions, type RerankOptions, type RerankResponse, type RerankResult, type RerankUsage, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, TOOL_CALL_NAME_RULES, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolCallNameRule, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, type VideoContent, clampProviderContextImages, classifyProviderError, continuesTurn, dropInvalidToolCalls, embed, environmentSecrets, formatChatError, formatError, formatErrorForDisplay, hasLoneSurrogate, isHardBillingMessage, isUsageLimitError, isValidToolCallName, localWireModelId, opensTurn, palsuAssistantMessage, palsuEmbedding, palsuRerankScore, palsuText, palsuThinking, palsuToolCall, prewarmAnthropicCache, providerRegistry, redactText, redactValue, registerPalsuProvider, rerank, resolveFastModeModel, resolveToolSchema, sanitizeMessagesForWire, setProviderDiagnostic, sliceHead, sliceTail, stream, toAnthropicMessages, toOpenAIMessages, toWellFormedText, toolCallNameRuleFor, usesResponsesLite };
1074
+ export { type AssistantMessage, type CacheRetention, type ChatErrorReason, type ContentPart, type DoneEvent, type EmbedInputType, type EmbedOptions, type EmbedResult, type EmbedUsage, type ErrorEvent, type ErrorSource, EventStream, FAST_MODE_SUFFIX, type FastModeRejection, type FastModeResolution, type FormattedChatError, type FormattedError, GGAIError, type ImageContent, type Message, type MessageProvenance, type MessageProvenanceKind, type MessageProvenanceSource, type MessageProvenanceVisibility, type OpenRouterProviderPreferences, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type PreparedContext, type Provider, type ProviderCapability, type ProviderDiagnosticFn, type ProviderEmbedFn, type ProviderEmbedRequest, type ProviderEmbedResponse, type ProviderEntry, ProviderError, type ProviderOptions, type ProviderRerankFn, type ProviderRerankRequest, type ProviderRerankResponse, type ProviderStreamFn, REDACTED as REDACTION_MARKER, type RawContent, type RedactionOptions, type RerankOptions, type RerankResponse, type RerankResult, type RerankUsage, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, TOOL_CALL_NAME_RULES, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolCallNameRule, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, type VideoContent, clampProviderContextImages, classifyProviderError, continuesTurn, describeFastModeRejection, dropInvalidToolCalls, embed, environmentSecrets, formatChatError, formatError, formatErrorForDisplay, hasLoneSurrogate, isHardBillingMessage, isUsageLimitError, isValidToolCallName, localWireModelId, opensTurn, palsuAssistantMessage, palsuEmbedding, palsuRerankScore, palsuText, palsuThinking, palsuToolCall, parseFastModeRejection, prewarmAnthropicCache, providerRegistry, redactText, redactValue, registerPalsuProvider, rerank, resolveFastModeModel, resolveToolSchema, sanitizeMessagesForWire, setProviderDiagnostic, sliceHead, sliceTail, stream, toAnthropicMessages, toOpenAIMessages, toWellFormedText, toolCallNameRuleFor, usesResponsesLite };
package/dist/index.js CHANGED
@@ -1,3 +1,93 @@
1
+ // src/fast-mode.ts
2
+ var FAST_MODE_SUFFIX = "-fast";
3
+ var FAST_MODE_BASE_MODELS = {
4
+ anthropic: ["claude-opus-5-5"],
5
+ openai: ["gpt-6-astra", "gpt-6.1-sol", "gpt-6-luna"]
6
+ };
7
+ function resolveFastModeModel(provider, model) {
8
+ if (!model.endsWith(FAST_MODE_SUFFIX)) return { model, fast: false };
9
+ const base = model.slice(0, -FAST_MODE_SUFFIX.length);
10
+ return FAST_MODE_BASE_MODELS[provider]?.includes(base) ? { model: base, fast: true } : { model, fast: false };
11
+ }
12
+ var FAST_MODE_REJECTION_HEADER = "anthropic-ratelimit-unified-overage-disabled-reason";
13
+ var FAST_MODE_REJECTION_PREFIX = "Fast mode unavailable";
14
+ var USAGE_PAGE = "claude.ai/settings/usage";
15
+ var STANDARD_SPEED_FALLBACK = "or switch to Claude Opus 5.5 (standard speed), which uses your plan";
16
+ function describeFastModeRejection(reason) {
17
+ const code = reason?.trim() || "unknown";
18
+ const of = (headline, guidance, kind) => ({ reason: code, headline, guidance, kind });
19
+ switch (code) {
20
+ case "overage_not_provisioned":
21
+ case "no_limits_configured":
22
+ return of(
23
+ "Claude usage credits are turned off",
24
+ `Fast mode bills your Claude usage credits, never your plan. Turn usage credits on at ${USAGE_PAGE}, ${STANDARD_SPEED_FALLBACK}.`,
25
+ "access"
26
+ );
27
+ case "out_of_credits":
28
+ return of(
29
+ "Your Claude usage credits have run out",
30
+ `Fast mode bills your Claude usage credits, never your plan, and the balance is empty. Add funds or turn on auto-reload at ${USAGE_PAGE}, ${STANDARD_SPEED_FALLBACK}.`,
31
+ "billing"
32
+ );
33
+ case "org_level_disabled_until":
34
+ case "org_spend_cap_reached":
35
+ return of(
36
+ "Claude usage credit limit reached",
37
+ `Fast mode bills your Claude usage credits and this period's spend limit is used up. Raise the limit at ${USAGE_PAGE}, ${STANDARD_SPEED_FALLBACK}.`,
38
+ "billing"
39
+ );
40
+ case "org_level_disabled":
41
+ case "org_service_level_disabled":
42
+ return of(
43
+ "Your organization turned Claude usage credits off",
44
+ `Fast mode needs usage credits. Ask an admin to turn them on, ${STANDARD_SPEED_FALLBACK}.`,
45
+ "access"
46
+ );
47
+ case "member_level_disabled":
48
+ return of(
49
+ "Claude usage credits are off for your account",
50
+ `Fast mode needs usage credits. Ask your organization's admin to turn them on for you, ${STANDARD_SPEED_FALLBACK}.`,
51
+ "access"
52
+ );
53
+ case "seat_tier_level_disabled":
54
+ case "seat_tier_zero_credit_limit":
55
+ case "member_zero_credit_limit":
56
+ case "group_zero_credit_limit":
57
+ return of(
58
+ "Claude usage credits aren't available on your plan",
59
+ `Fast mode needs usage credits, which your seat doesn't include. Ask your admin about access, ${STANDARD_SPEED_FALLBACK}.`,
60
+ "access"
61
+ );
62
+ case "fast_mode_org_disabled":
63
+ return of(
64
+ "Your organization turned fast mode off",
65
+ `An Owner can enable fast mode for the organization, ${STANDARD_SPEED_FALLBACK}.`,
66
+ "access"
67
+ );
68
+ default:
69
+ return of(
70
+ "Fast mode needs Claude usage credits",
71
+ `Fast mode bills your Claude usage credits, never your plan, and Anthropic couldn't charge them. Check usage credits at ${USAGE_PAGE}, ${STANDARD_SPEED_FALLBACK}.`,
72
+ "billing"
73
+ );
74
+ }
75
+ }
76
+ function fastModeRejectionMessage(rejection) {
77
+ return `${FAST_MODE_REJECTION_PREFIX} (${rejection.reason}): ${rejection.headline}.`;
78
+ }
79
+ function parseFastModeRejection(message) {
80
+ const canonical = /fast mode unavailable \(([a-z_]+)\)/i.exec(message);
81
+ if (canonical) return describeFastModeRejection(canonical[1]);
82
+ if (/usage credits are required for fast mode/i.test(message)) {
83
+ return describeFastModeRejection(void 0);
84
+ }
85
+ if (/fast mode (?:has been |is )?disabled by your organization/i.test(message)) {
86
+ return describeFastModeRejection("fast_mode_org_disabled");
87
+ }
88
+ return void 0;
89
+ }
90
+
1
91
  // src/errors.ts
2
92
  function readHeader(headers, ...names) {
3
93
  if (!headers) return void 0;
@@ -77,8 +167,8 @@ function isUsageLimitError(err) {
77
167
  }
78
168
  function isHardBillingMessage(message) {
79
169
  const lower = message.toLowerCase();
80
- return lower.includes("insufficient balance") || lower.includes("insufficient credits") || lower.includes("more credits") || lower.includes("insufficient_quota") || lower.includes("exceeded your current quota") || lower.includes("quota exceeded") || lower.includes("no resource package") || lower.includes("recharge") || lower.includes("balance is too low") || lower.includes("out of credits") || // Anthropic fast mode on a subscription without usage credits turned on.
81
- lower.includes("usage credits are required") || lower.includes("arrears") || lower.includes("arrearage") || lower.includes("token quota") || lower.includes("exceeded_current_quota_error") || lower.includes("check your account balance") || lower.includes("does not yet include access") || lower.includes("subscription plan") || lower.includes("billing");
170
+ return lower.includes("insufficient balance") || lower.includes("insufficient credits") || lower.includes("more credits") || lower.includes("insufficient_quota") || lower.includes("exceeded your current quota") || lower.includes("quota exceeded") || lower.includes("no resource package") || lower.includes("recharge") || lower.includes("balance is too low") || lower.includes("out of credits") || // Anthropic fast mode when usage credits can't pay (see fast-mode.ts).
171
+ lower.includes("usage credits are required") || lower.includes("fast mode unavailable") || lower.includes("arrears") || lower.includes("arrearage") || lower.includes("token quota") || lower.includes("exceeded_current_quota_error") || lower.includes("check your account balance") || lower.includes("does not yet include access") || lower.includes("subscription plan") || lower.includes("billing");
82
172
  }
83
173
  function formatResetTime(resetsAt) {
84
174
  const when = new Date(resetsAt * 1e3);
@@ -131,6 +221,18 @@ function formatError(err) {
131
221
  guidance: "Request access via your Anthropic account team (see platform.claude.com/docs/en/about-claude/models/overview), or switch to Claude Fable 5.1 via the model selector \u2014 same underlying model, generally available."
132
222
  };
133
223
  }
224
+ const fastRejection = err.provider === "anthropic" ? parseFastModeRejection(err.message) : void 0;
225
+ if (fastRejection) {
226
+ return {
227
+ headline: `${fastRejection.headline}.`,
228
+ source: "provider",
229
+ message: cleanMessage,
230
+ provider: err.provider,
231
+ statusCode: err.statusCode,
232
+ ...err.requestId ? { requestId: err.requestId } : {},
233
+ guidance: fastRejection.guidance
234
+ };
235
+ }
134
236
  if (isUsageLimitError(err)) {
135
237
  const resetClause = err.resetsAt ? ` It resets at ${formatResetTime(err.resetsAt)}.` : "";
136
238
  return {
@@ -1307,7 +1409,7 @@ function isAdaptiveThinkingModel(model) {
1307
1409
  function toAnthropicThinking(level, maxTokens, model) {
1308
1410
  if (isAdaptiveThinkingModel(model)) {
1309
1411
  let effort = level;
1310
- if (effort === "xhigh" && !/opus-5|opus-4-8|opus-4-7|sonnet-5[-.]5|haiku-5[-.]5/.test(model)) {
1412
+ if (effort === "xhigh" && !/fable-5|mythos-5|opus-5|opus-4-8|opus-4-7|sonnet-5[-.]5|haiku-5[-.]5/.test(model)) {
1311
1413
  effort = "high";
1312
1414
  }
1313
1415
  return {
@@ -1847,7 +1949,7 @@ async function* runStream(options) {
1847
1949
  yield* synthesizeEventsFromMessage(message);
1848
1950
  return messageToResponse(message);
1849
1951
  } catch (err) {
1850
- throw toError(err);
1952
+ throw toError(err, { fastMode: useFastMode });
1851
1953
  }
1852
1954
  }
1853
1955
  const contentParts = [];
@@ -2064,7 +2166,7 @@ async function* runStream(options) {
2064
2166
  }
2065
2167
  }
2066
2168
  } catch (err) {
2067
- throw toError(err);
2169
+ throw toError(err, { fastMode: useFastMode });
2068
2170
  }
2069
2171
  if (!receivedAnyEvent) {
2070
2172
  throw new ProviderError("anthropic", "Stream ended without producing any events.", {
@@ -2194,8 +2296,12 @@ function messageToResponse(message) {
2194
2296
  }
2195
2297
  };
2196
2298
  }
2197
- function isFastModeCreditsMessage(message) {
2198
- return /usage credits are required for fast mode/i.test(message);
2299
+ function readFastModeRejection(err, message, fastMode) {
2300
+ const reason = readHeader(err.headers, FAST_MODE_REJECTION_HEADER);
2301
+ if (fastMode && err.status === 429 && reason) return describeFastModeRejection(reason);
2302
+ const fromBody = parseFastModeRejection(message);
2303
+ if (!fromBody) return void 0;
2304
+ return reason ? describeFastModeRejection(reason) : fromBody;
2199
2305
  }
2200
2306
  function readUnifiedRateLimit(headers) {
2201
2307
  const status = readHeader(headers, "anthropic-ratelimit-unified-status");
@@ -2209,7 +2315,7 @@ function readUnifiedRateLimit(headers) {
2209
2315
  const resetsAt = Number.isFinite(resetNum) && resetNum > 0 ? resetNum : void 0;
2210
2316
  return { rejected: status === "rejected", ...resetsAt ? { resetsAt } : {} };
2211
2317
  }
2212
- function toError(err) {
2318
+ function toError(err, context) {
2213
2319
  if (err instanceof ProviderError) return err;
2214
2320
  if (err instanceof Anthropic.APIError) {
2215
2321
  const errorBody = err.error;
@@ -2220,11 +2326,12 @@ function toError(err) {
2220
2326
  const fallbackMessage = isRawJsonErrorEcho(err.message) ? emptyProviderErrorMessage(err.status) : err.message;
2221
2327
  const messageCandidate = bodyMessage ?? err.message;
2222
2328
  const message = isRawHtmlErrorEcho(messageCandidate) ? providerHtmlErrorMessage(err.status) : bodyType && bodyMessage ? `${bodyType}: ${bodyMessage}` : bodyMessage ?? fallbackMessage;
2223
- if (isFastModeCreditsMessage(message)) {
2224
- return new ProviderError("anthropic", message, {
2329
+ const fastRejection = readFastModeRejection(err, message, context?.fastMode === true);
2330
+ if (fastRejection) {
2331
+ return new ProviderError("anthropic", fastModeRejectionMessage(fastRejection), {
2225
2332
  statusCode: err.status,
2226
2333
  ...requestId ? { requestId } : {},
2227
- hint: "Fast mode bills Claude usage credits, not your plan. Turn them on at claude.ai/settings/usage, or switch to Claude Opus 5.5 (standard speed) in the model selector.",
2334
+ hint: fastRejection.guidance,
2228
2335
  cause: err
2229
2336
  });
2230
2337
  }
@@ -2523,10 +2630,16 @@ async function* runStream2(options) {
2523
2630
  `
2524
2631
  );
2525
2632
  }
2633
+ const requestHeaders = options.provider === "xai" ? {
2634
+ headers: {
2635
+ "x-grok-conv-id": normalizePromptCacheKey(options.promptCacheKey ?? "ggcoder")
2636
+ }
2637
+ } : {};
2526
2638
  if (!useStreaming) {
2527
2639
  try {
2528
2640
  const completion = await client.chat.completions.create(requestParams, {
2529
- signal: options.signal ?? void 0
2641
+ signal: options.signal ?? void 0,
2642
+ ...requestHeaders
2530
2643
  });
2531
2644
  yield* synthesizeEventsFromCompletion(completion, !!options.thinking, endpointKey);
2532
2645
  return completionToResponse(completion, endpointKey);
@@ -2537,7 +2650,8 @@ async function* runStream2(options) {
2537
2650
  let stream2;
2538
2651
  try {
2539
2652
  stream2 = await client.chat.completions.create(requestParams, {
2540
- signal: options.signal ?? void 0
2653
+ signal: options.signal ?? void 0,
2654
+ ...requestHeaders
2541
2655
  });
2542
2656
  } catch (err) {
2543
2657
  throw toError2(err, providerName);
@@ -3580,6 +3694,9 @@ var CODE_ASSIST_SUPPORTED_MODELS = /* @__PURE__ */ new Set([
3580
3694
  "gemma-4-31b-it",
3581
3695
  "gemma-4-26b-a4b-it"
3582
3696
  ]);
3697
+ function isGeminiApiKeyRoute(options) {
3698
+ return options.baseUrl?.includes("generativelanguage.googleapis.com") === true;
3699
+ }
3583
3700
  function getGoogleProject(options) {
3584
3701
  const env = getEnvironment();
3585
3702
  return options.projectId ?? env?.GOOGLE_CLOUD_PROJECT ?? env?.GOOGLE_CLOUD_PROJECT_ID;
@@ -3612,8 +3729,28 @@ function accountGatedHint(model) {
3612
3729
  if (model === "gemini-3.8-flash" || model === "gemini-3.5-flash-lite") {
3613
3730
  return "Use /model to select Gemini 3.1 Flash Lite, or retry once Google enables this model through Code Assist for your account.";
3614
3731
  }
3615
- return `Newer Gemini models (3.7 Flash, 3.5 Flash, 3.1 Pro Preview) are available only to Code Assist Standard/Enterprise accounts with preview/GA access enabled by a cloud admin \u2014 free/personal accounts usually can't call them. Switch to Gemini 3.1 Flash Lite (it works on this account) with /model, or sign in with a Code Assist Standard/Enterprise account that has preview access.`;
3732
+ return `Newer Gemini models (3.5 Flash, 3.1 Pro Preview) are available only to Code Assist Standard/Enterprise accounts with preview/GA access enabled by a cloud admin. Try Gemini 3.1 Flash Lite with /model, or sign in with a Code Assist Standard/Enterprise account that has preview access.`;
3616
3733
  }
3734
+ function isCodeAssistLicenseRequired(status, body) {
3735
+ if (status !== 403) return false;
3736
+ try {
3737
+ const parsed = JSON.parse(body);
3738
+ const details = typeof parsed === "object" && parsed !== null && "error" in parsed ? parsed.error?.details : void 0;
3739
+ return Array.isArray(details) && details.some(
3740
+ (detail) => typeof detail === "object" && detail !== null && detail.reason === "SUBSCRIPTION_REQUIRED"
3741
+ );
3742
+ } catch {
3743
+ return false;
3744
+ }
3745
+ }
3746
+ function isInvalidGeminiApiKey(status, body) {
3747
+ if (status !== 400 && status !== 401 && status !== 403) return false;
3748
+ return /"reason"\s*:\s*"API_KEY_INVALID"/.test(body) || /API key not valid/i.test(body);
3749
+ }
3750
+ var INVALID_API_KEY_MESSAGE = "Google rejected the Gemini API key: it is not valid.";
3751
+ var INVALID_API_KEY_HINT = "Create a key at aistudio.google.com/apikey and add it again under Gemini in Providers (or `ggcoder login`).";
3752
+ var LICENSE_REQUIRED_MESSAGE = "This Google account has no Gemini Code Assist licence. Google stopped serving Gemini through Code Assist to personal accounts (Google AI Pro/Ultra and the free individual tier) on June 18, 2026, so every Gemini model is refused, not just this one.";
3753
+ var LICENSE_REQUIRED_HINT = "Sign in to Gemini with a Google account that has a Gemini Code Assist Standard or Enterprise licence, or pick a model from another provider with /model.";
3617
3754
  function formatErrorMessage(status, body, model) {
3618
3755
  if (status === 404 && !CODE_ASSIST_SUPPORTED_MODELS.has(model)) {
3619
3756
  return `Gemini API error (404): ${body}
@@ -3837,7 +3974,29 @@ function buildCodeAssistRequest(options, request, projectId) {
3837
3974
  request
3838
3975
  };
3839
3976
  }
3977
+ var API_KEY_MODEL_IDS = {
3978
+ "gemini-3-flash": "gemini-3.5-flash"
3979
+ };
3980
+ function buildApiKeyRequestPlan(options, method) {
3981
+ const { session_id: _sessionId, ...request } = mergeProviderOptions(
3982
+ buildGenerateRequest(options),
3983
+ options.providerOptions
3984
+ );
3985
+ const base = (options.baseUrl ?? "").replace(/\/+$/, "");
3986
+ return {
3987
+ url: new URL(
3988
+ `${base}/models/${encodeURIComponent(API_KEY_MODEL_IDS[options.model] ?? options.model)}:${method}`
3989
+ ),
3990
+ headers: {
3991
+ "Content-Type": "application/json",
3992
+ "x-goog-api-key": options.apiKey ?? ""
3993
+ },
3994
+ body: request,
3995
+ route: "api-key"
3996
+ };
3997
+ }
3840
3998
  function buildRequestPlan(options, method) {
3999
+ if (isGeminiApiKeyRoute(options)) return buildApiKeyRequestPlan(options, method);
3841
4000
  if (!CODE_ASSIST_SUPPORTED_MODELS.has(options.model)) {
3842
4001
  throw new ProviderError("gemini", formatUnsupportedModelMessage(options.model));
3843
4002
  }
@@ -3851,7 +4010,8 @@ function buildRequestPlan(options, method) {
3851
4010
  "User-Agent": GEMINI_CLI_USER_AGENT,
3852
4011
  "X-Goog-Api-Client": GEMINI_CLI_API_CLIENT
3853
4012
  },
3854
- body: buildCodeAssistRequest(options, request, projectId)
4013
+ body: buildCodeAssistRequest(options, request, projectId),
4014
+ route: "code-assist"
3855
4015
  };
3856
4016
  }
3857
4017
  function normalizeGeminiStopReason(reason) {
@@ -3937,8 +4097,11 @@ async function fetchCodeAssist(plan, options) {
3937
4097
  if (!response.ok) {
3938
4098
  const text = await response.text().catch(() => "");
3939
4099
  const quota = parseGeminiQuota(response.status, text);
3940
- const accountGated = response.status === 404 && ACCOUNT_GATED_MODELS.has(options.model);
3941
- let message = accountGated ? accountGatedMessage(options.model) : formatErrorMessage(response.status, text, options.model);
4100
+ const codeAssist = plan.route === "code-assist";
4101
+ const accountGated = codeAssist && response.status === 404 && ACCOUNT_GATED_MODELS.has(options.model);
4102
+ const licenseRequired = codeAssist && isCodeAssistLicenseRequired(response.status, text);
4103
+ const invalidKey = !codeAssist && isInvalidGeminiApiKey(response.status, text);
4104
+ let message = invalidKey ? INVALID_API_KEY_MESSAGE : licenseRequired ? LICENSE_REQUIRED_MESSAGE : accountGated ? accountGatedMessage(options.model) : formatErrorMessage(response.status, text, options.model);
3942
4105
  let resetsAt;
3943
4106
  if (quota?.exhausted) {
3944
4107
  message = `Gemini quota exhausted \u2014 usage limit reached. ${message}`;
@@ -3948,7 +4111,7 @@ async function fetchCodeAssist(plan, options) {
3948
4111
  throw new ProviderError("gemini", message, {
3949
4112
  statusCode: response.status,
3950
4113
  ...resetsAt !== void 0 ? { resetsAt } : {},
3951
- ...accountGated ? { hint: accountGatedHint(options.model) } : {}
4114
+ ...invalidKey ? { hint: INVALID_API_KEY_HINT, source: "auth" } : licenseRequired ? { hint: LICENSE_REQUIRED_HINT, source: "auth" } : accountGated ? { hint: accountGatedHint(options.model) } : {}
3952
4115
  });
3953
4116
  }
3954
4117
  return response;
@@ -4733,18 +4896,6 @@ function observePreparedContext(before, after, tools) {
4733
4896
  };
4734
4897
  }
4735
4898
 
4736
- // src/fast-mode.ts
4737
- var FAST_MODE_SUFFIX = "-fast";
4738
- var FAST_MODE_BASE_MODELS = {
4739
- anthropic: ["claude-opus-5-5"],
4740
- openai: ["gpt-6-astra", "gpt-6.1-sol", "gpt-6-luna"]
4741
- };
4742
- function resolveFastModeModel(provider, model) {
4743
- if (!model.endsWith(FAST_MODE_SUFFIX)) return { model, fast: false };
4744
- const base = model.slice(0, -FAST_MODE_SUFFIX.length);
4745
- return FAST_MODE_BASE_MODELS[provider]?.includes(base) ? { model: base, fast: true } : { model, fast: false };
4746
- }
4747
-
4748
4899
  // src/stream.ts
4749
4900
  var OPENAI_EMBED_BATCH_SIZE = 2048;
4750
4901
  var OPENROUTER_EMBED_BATCH_SIZE = 64;
@@ -4847,7 +4998,7 @@ providerRegistry.register("sakana", {
4847
4998
  providerRegistry.register("xai", {
4848
4999
  // xAI's public API (console.x.ai key) is OpenAI-compatible — ride the Chat
4849
5000
  // Completions transport like Moonshot/DeepSeek. Grok reasoning models take
4850
- // top-level `reasoning_effort` (low/medium/high), which the shared thinking
5001
+ // top-level `reasoning_effort` (low/medium/high/xhigh), which the shared thinking
4851
5002
  // path already sends.
4852
5003
  //
4853
5004
  // Subscription OAuth (SuperGrok / X Premium) routes to the Grok CLI chat proxy
@@ -5136,6 +5287,14 @@ function formatChatError(error) {
5136
5287
  "Update the app, or choose another model."
5137
5288
  );
5138
5289
  }
5290
+ const fastRejection = base.provider === "anthropic" ? parseFastModeRejection(raw) : void 0;
5291
+ if (fastRejection) {
5292
+ return present(
5293
+ fastRejection.kind === "billing" ? "billing" : "model_access",
5294
+ fastRejection.headline,
5295
+ fastRejection.guidance
5296
+ );
5297
+ }
5139
5298
  if (/insufficient.*(?:balance|credits?)|(?:balance|credits?).*(?:insufficient|exhausted|depleted)|credit balance is too low|payment_required/.test(
5140
5299
  text
5141
5300
  )) {
@@ -5561,6 +5720,7 @@ export {
5561
5720
  clampProviderContextImages,
5562
5721
  classifyProviderError,
5563
5722
  continuesTurn,
5723
+ describeFastModeRejection,
5564
5724
  dropInvalidToolCalls,
5565
5725
  embed,
5566
5726
  environmentSecrets,
@@ -5579,6 +5739,7 @@ export {
5579
5739
  palsuText,
5580
5740
  palsuThinking,
5581
5741
  palsuToolCall,
5742
+ parseFastModeRejection,
5582
5743
  prewarmAnthropicCache,
5583
5744
  providerRegistry,
5584
5745
  redactText,