@kenkaiiii/gg-ai 4.3.243 → 4.5.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
@@ -23,6 +23,11 @@ interface VideoContent {
23
23
  type: "video";
24
24
  mediaType: string;
25
25
  data: string;
26
+ /** Moonshot/Kimi file id (e.g. "d4f0…") after uploading via the files API.
27
+ * Moonshot rejects inline base64 video; the provider uploads the clip once
28
+ * and caches the id here so later turns reference `ms://<fileId>` instead of
29
+ * re-sending the bytes. */
30
+ fileId?: string;
26
31
  }
27
32
  interface ToolCall {
28
33
  type: "tool_call";
@@ -30,7 +35,7 @@ interface ToolCall {
30
35
  name: string;
31
36
  args: Record<string, unknown>;
32
37
  }
33
- type ToolResultContent = string | (TextContent | ImageContent)[];
38
+ type ToolResultContent = string | (TextContent | ImageContent | VideoContent)[];
34
39
  interface ToolResult {
35
40
  type: "tool_result";
36
41
  toolCallId: string;
@@ -208,6 +213,11 @@ interface StreamOptions {
208
213
  * version should pass it here. Ignored for non-Anthropic providers and for
209
214
  * Anthropic requests using a regular API key. */
210
215
  userAgent?: string;
216
+ /** Extra HTTP headers attached to every model request. Used by providers
217
+ * whose endpoint gates on client identity (e.g. Kimi For Coding requires a
218
+ * `User-Agent: kimi-code-cli/...` and `X-Msh-*` device headers). Merged
219
+ * into the underlying SDK's default headers. */
220
+ defaultHeaders?: Record<string, string>;
211
221
  }
212
222
 
213
223
  /**
@@ -330,7 +340,7 @@ declare const providerRegistry: ProviderRegistryImpl;
330
340
  * Cannot read property 'foo' of undefined
331
341
  * → This is a ggcoder bug — please report it.
332
342
  */
333
- type ErrorSource = "provider" | "ggcoder" | "network" | "auth";
343
+ type ErrorSource = "provider" | "ggcoder" | "network" | "auth" | "capability";
334
344
  interface FormattedError {
335
345
  /** Plain-English headline, e.g. "OpenAI returned an error." */
336
346
  headline: string;
@@ -404,6 +414,19 @@ declare function formatError(err: unknown): FormattedError;
404
414
  */
405
415
  declare function formatErrorForDisplay(err: unknown): string;
406
416
 
417
+ /**
418
+ * Inspect a raw provider error message and tag it with a clearer, actionable
419
+ * prefix so a worker orchestrator can route on intent instead of regexing JSON.
420
+ * Preserves the original message verbatim after the prefix — helpful for
421
+ * debugging.
422
+ *
423
+ * Order matters: context-overflow is checked first because some providers wrap
424
+ * overflow errors in HTTP 429 envelopes; we want the structural meaning, not
425
+ * the transport status. Billing comes before auth/rate-limit because "402
426
+ * Payment Required" must not be mis-routed as a rate-limit retry.
427
+ */
428
+ declare function classifyProviderError(message: string): string;
429
+
407
430
  /**
408
431
  * Provider-level diagnostic hook. Mirrors the pattern used by gg-agent's
409
432
  * setStreamDiagnostic — the host app wires a callback (typically writing to
@@ -496,4 +519,4 @@ interface PalsuProviderConfig {
496
519
  */
497
520
  declare function registerPalsuProvider(config?: PalsuProviderConfig): PalsuProviderHandle;
498
521
 
499
- 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, type VideoContent, formatError, formatErrorForDisplay, isHardBillingMessage, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream, toAnthropicMessages, toOpenAIMessages };
522
+ 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, type VideoContent, classifyProviderError, formatError, formatErrorForDisplay, isHardBillingMessage, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream, toAnthropicMessages, toOpenAIMessages };
package/dist/index.d.ts CHANGED
@@ -23,6 +23,11 @@ interface VideoContent {
23
23
  type: "video";
24
24
  mediaType: string;
25
25
  data: string;
26
+ /** Moonshot/Kimi file id (e.g. "d4f0…") after uploading via the files API.
27
+ * Moonshot rejects inline base64 video; the provider uploads the clip once
28
+ * and caches the id here so later turns reference `ms://<fileId>` instead of
29
+ * re-sending the bytes. */
30
+ fileId?: string;
26
31
  }
27
32
  interface ToolCall {
28
33
  type: "tool_call";
@@ -30,7 +35,7 @@ interface ToolCall {
30
35
  name: string;
31
36
  args: Record<string, unknown>;
32
37
  }
33
- type ToolResultContent = string | (TextContent | ImageContent)[];
38
+ type ToolResultContent = string | (TextContent | ImageContent | VideoContent)[];
34
39
  interface ToolResult {
35
40
  type: "tool_result";
36
41
  toolCallId: string;
@@ -208,6 +213,11 @@ interface StreamOptions {
208
213
  * version should pass it here. Ignored for non-Anthropic providers and for
209
214
  * Anthropic requests using a regular API key. */
210
215
  userAgent?: string;
216
+ /** Extra HTTP headers attached to every model request. Used by providers
217
+ * whose endpoint gates on client identity (e.g. Kimi For Coding requires a
218
+ * `User-Agent: kimi-code-cli/...` and `X-Msh-*` device headers). Merged
219
+ * into the underlying SDK's default headers. */
220
+ defaultHeaders?: Record<string, string>;
211
221
  }
212
222
 
213
223
  /**
@@ -330,7 +340,7 @@ declare const providerRegistry: ProviderRegistryImpl;
330
340
  * Cannot read property 'foo' of undefined
331
341
  * → This is a ggcoder bug — please report it.
332
342
  */
333
- type ErrorSource = "provider" | "ggcoder" | "network" | "auth";
343
+ type ErrorSource = "provider" | "ggcoder" | "network" | "auth" | "capability";
334
344
  interface FormattedError {
335
345
  /** Plain-English headline, e.g. "OpenAI returned an error." */
336
346
  headline: string;
@@ -404,6 +414,19 @@ declare function formatError(err: unknown): FormattedError;
404
414
  */
405
415
  declare function formatErrorForDisplay(err: unknown): string;
406
416
 
417
+ /**
418
+ * Inspect a raw provider error message and tag it with a clearer, actionable
419
+ * prefix so a worker orchestrator can route on intent instead of regexing JSON.
420
+ * Preserves the original message verbatim after the prefix — helpful for
421
+ * debugging.
422
+ *
423
+ * Order matters: context-overflow is checked first because some providers wrap
424
+ * overflow errors in HTTP 429 envelopes; we want the structural meaning, not
425
+ * the transport status. Billing comes before auth/rate-limit because "402
426
+ * Payment Required" must not be mis-routed as a rate-limit retry.
427
+ */
428
+ declare function classifyProviderError(message: string): string;
429
+
407
430
  /**
408
431
  * Provider-level diagnostic hook. Mirrors the pattern used by gg-agent's
409
432
  * setStreamDiagnostic — the host app wires a callback (typically writing to
@@ -496,4 +519,4 @@ interface PalsuProviderConfig {
496
519
  */
497
520
  declare function registerPalsuProvider(config?: PalsuProviderConfig): PalsuProviderHandle;
498
521
 
499
- 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, type VideoContent, formatError, formatErrorForDisplay, isHardBillingMessage, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream, toAnthropicMessages, toOpenAIMessages };
522
+ 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, type VideoContent, classifyProviderError, formatError, formatErrorForDisplay, isHardBillingMessage, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream, toAnthropicMessages, toOpenAIMessages };
package/dist/index.js CHANGED
@@ -25,6 +25,12 @@ var GGAIError = class extends Error {
25
25
  this.hint = options?.hint;
26
26
  }
27
27
  };
28
+ var VideoUnsupportedError = class extends GGAIError {
29
+ constructor() {
30
+ super("This model can't analyze video.", { source: "capability" });
31
+ this.name = "VideoUnsupportedError";
32
+ }
33
+ };
28
34
  var ProviderError = class extends GGAIError {
29
35
  provider;
30
36
  statusCode;
@@ -140,6 +146,14 @@ function finaliseBySource(source, message, requestId, hint) {
140
146
  guidance: hint ?? providerGuidance(void 0, message, void 0),
141
147
  ...requestId ? { requestId } : {}
142
148
  };
149
+ case "capability":
150
+ return {
151
+ headline: message,
152
+ source,
153
+ message: "",
154
+ guidance: hint ?? "Only Kimi, Gemini, and MiniMax can analyze video. Switch with /model.",
155
+ ...requestId ? { requestId } : {}
156
+ };
143
157
  case "ggcoder":
144
158
  return {
145
159
  headline: "ggcoder hit an unexpected error.",
@@ -500,6 +514,10 @@ function toolResultImages(content) {
500
514
  if (typeof content === "string") return [];
501
515
  return content.filter((b) => b.type === "image");
502
516
  }
517
+ function toolResultVideos(content) {
518
+ if (typeof content === "string") return [];
519
+ return content.filter((b) => b.type === "video");
520
+ }
503
521
  function toAnthropicCacheControl(retention, baseUrl) {
504
522
  const resolved = retention ?? "short";
505
523
  if (resolved === "none") return void 0;
@@ -510,6 +528,12 @@ function toAnthropicToolResultContent(content) {
510
528
  if (typeof content === "string") return content;
511
529
  return content.map((block) => {
512
530
  if (block.type === "text") return { type: "text", text: block.text };
531
+ if (block.type === "video") {
532
+ return {
533
+ type: "video",
534
+ source: { type: "base64", media_type: block.mediaType, data: block.data }
535
+ };
536
+ }
513
537
  return {
514
538
  type: "image",
515
539
  source: {
@@ -579,6 +603,8 @@ function toAnthropicMessages(messages, cacheControl) {
579
603
  if (msg.role === "tool") {
580
604
  out.push({
581
605
  role: "user",
606
+ // Cast covers the video block (used by the Anthropic-compatible MiniMax
607
+ // API), which isn't in the first-party Anthropic tool_result types.
582
608
  content: msg.content.map((result) => ({
583
609
  type: "tool_result",
584
610
  tool_use_id: remapAnthropicToolCallId(result.toolCallId, idMap),
@@ -719,11 +745,10 @@ function toOpenAIMessages(messages, options) {
719
745
  (part) => {
720
746
  if (part.type === "text") return { type: "text", text: part.text };
721
747
  if (part.type === "video") {
748
+ const videoUrl = part.fileId ? { url: `ms://${part.fileId}`, id: part.fileId } : { url: `data:${part.mediaType};base64,${part.data}` };
722
749
  return {
723
750
  type: "video_url",
724
- video_url: {
725
- url: `data:${part.mediaType};base64,${part.data}`
726
- }
751
+ video_url: videoUrl
727
752
  };
728
753
  }
729
754
  return {
@@ -768,11 +793,27 @@ function toOpenAIMessages(messages, options) {
768
793
  continue;
769
794
  }
770
795
  if (msg.role === "tool") {
796
+ const isMoonshot = options?.provider === "moonshot";
771
797
  const imageBlocks = [];
772
798
  for (const result of msg.content) {
773
799
  const text = toolResultText(result.content);
774
800
  const images = toolResultImages(result.content);
801
+ const videos = isMoonshot ? toolResultVideos(result.content) : [];
775
802
  const hasText = text.length > 0;
803
+ if (videos.length > 0) {
804
+ const parts = [];
805
+ if (hasText) parts.push({ type: "text", text });
806
+ const videoParts = videos.map((v) => {
807
+ const videoUrl = v.fileId ? { url: `ms://${v.fileId}`, id: v.fileId } : { url: `data:${v.mediaType};base64,${v.data}` };
808
+ return { type: "video_url", video_url: videoUrl };
809
+ });
810
+ out.push({
811
+ role: "tool",
812
+ tool_call_id: remapToolCallId(result.toolCallId, idMap),
813
+ content: [...parts, ...videoParts]
814
+ });
815
+ continue;
816
+ }
776
817
  out.push({
777
818
  role: "tool",
778
819
  tool_call_id: remapToolCallId(result.toolCallId, idMap),
@@ -1357,6 +1398,75 @@ function fnv1aHash(value) {
1357
1398
  return (hash >>> 0).toString(16).padStart(8, "0");
1358
1399
  }
1359
1400
 
1401
+ // src/utils/diag.ts
1402
+ var _diagFn = null;
1403
+ function setProviderDiagnostic(fn) {
1404
+ _diagFn = fn;
1405
+ }
1406
+ function providerDiag(phase, data) {
1407
+ _diagFn?.(phase, data);
1408
+ }
1409
+
1410
+ // src/providers/moonshot-video.ts
1411
+ async function uploadMoonshotVideos(client, messages, signal) {
1412
+ for (const msg of messages) {
1413
+ if (typeof msg.content === "string") continue;
1414
+ for (const part of msg.content) {
1415
+ if (part.type === "video") {
1416
+ await ensureUploaded(client, part, signal);
1417
+ continue;
1418
+ }
1419
+ if (part.type === "tool_result" && Array.isArray(part.content)) {
1420
+ for (const inner of part.content) {
1421
+ if (inner.type === "video") {
1422
+ await ensureUploaded(client, inner, signal);
1423
+ }
1424
+ }
1425
+ }
1426
+ }
1427
+ }
1428
+ }
1429
+ async function ensureUploaded(client, video, signal) {
1430
+ if (video.fileId) {
1431
+ providerDiag("moonshot_video_cached", { fileId: video.fileId });
1432
+ return;
1433
+ }
1434
+ if (!video.data) {
1435
+ providerDiag("moonshot_video_skipped_no_data", {});
1436
+ return;
1437
+ }
1438
+ providerDiag("moonshot_video_upload_start", {
1439
+ mediaType: video.mediaType,
1440
+ bytes: Math.floor(video.data.length * 3 / 4)
1441
+ });
1442
+ video.fileId = await uploadOne(client, video, signal);
1443
+ providerDiag("moonshot_video_upload_done", { fileId: video.fileId });
1444
+ }
1445
+ async function uploadOne(client, video, signal) {
1446
+ const bytes = Buffer.from(video.data, "base64");
1447
+ const mediaType = video.mediaType || "video/mp4";
1448
+ const filename = `upload.${extForMime(mediaType)}`;
1449
+ const file = new File([new Uint8Array(bytes)], filename, { type: mediaType });
1450
+ const uploaded = await client.files.create(
1451
+ { file, purpose: "video" },
1452
+ signal ? { signal } : void 0
1453
+ );
1454
+ return uploaded.id;
1455
+ }
1456
+ var MIME_TO_EXT = {
1457
+ "video/mp4": "mp4",
1458
+ "video/mpeg": "mpeg",
1459
+ "video/quicktime": "mov",
1460
+ "video/webm": "webm",
1461
+ "video/x-matroska": "mkv",
1462
+ "video/x-msvideo": "avi",
1463
+ "video/x-flv": "flv",
1464
+ "video/3gpp": "3gp"
1465
+ };
1466
+ function extForMime(mediaType) {
1467
+ return MIME_TO_EXT[mediaType.toLowerCase()] ?? "mp4";
1468
+ }
1469
+
1360
1470
  // src/utils/env.ts
1361
1471
  function getEnvironment() {
1362
1472
  return globalThis.process?.env;
@@ -1386,7 +1496,8 @@ function createClient2(options) {
1386
1496
  return new OpenAI({
1387
1497
  apiKey: options.apiKey,
1388
1498
  ...options.baseUrl ? { baseURL: options.baseUrl } : {},
1389
- ...options.fetch ? { fetch: options.fetch } : {}
1499
+ ...options.fetch ? { fetch: options.fetch } : {},
1500
+ ...options.defaultHeaders ? { defaultHeaders: options.defaultHeaders } : {}
1390
1501
  });
1391
1502
  }
1392
1503
  function streamOpenAI(options) {
@@ -1399,6 +1510,13 @@ async function* runStream2(options) {
1399
1510
  const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" || options.provider === "xiaomi";
1400
1511
  const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
1401
1512
  const downgradedMessages = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
1513
+ if (options.provider === "moonshot") {
1514
+ try {
1515
+ await uploadMoonshotVideos(client, downgradedMessages, options.signal);
1516
+ } catch (err) {
1517
+ throw toError2(err, providerName);
1518
+ }
1519
+ }
1402
1520
  const messages = toOpenAIMessages(downgradedMessages, {
1403
1521
  provider: options.provider,
1404
1522
  thinking: !!options.thinking,
@@ -1702,15 +1820,6 @@ function toError2(err, provider = "openai") {
1702
1820
  // src/providers/openai-codex.ts
1703
1821
  import os from "os";
1704
1822
 
1705
- // src/utils/diag.ts
1706
- var _diagFn = null;
1707
- function setProviderDiagnostic(fn) {
1708
- _diagFn = fn;
1709
- }
1710
- function providerDiag(phase, data) {
1711
- _diagFn?.(phase, data);
1712
- }
1713
-
1714
1823
  // src/utils/sse.ts
1715
1824
  function parseSseBuffer(buffer) {
1716
1825
  const events = [];
@@ -2359,6 +2468,13 @@ ${msg.content}` : msg.content;
2359
2468
  }
2360
2469
  }
2361
2470
  });
2471
+ if (typeof result.content !== "string") {
2472
+ for (const block of result.content) {
2473
+ if (block.type === "video") {
2474
+ parts.push({ inlineData: { mimeType: block.mediaType, data: block.data } });
2475
+ }
2476
+ }
2477
+ }
2362
2478
  }
2363
2479
  if (parts.length > 0) contents.push({ role: "user", parts });
2364
2480
  }
@@ -2756,6 +2872,7 @@ var providerRegistry = new ProviderRegistryImpl();
2756
2872
 
2757
2873
  // src/stream.ts
2758
2874
  var GLM_CODING_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
2875
+ var KIMI_CODE_USER_AGENT = `kimi-code-cli/${process.env.KIMI_CODE_VERSION ?? "1.0.11"}`;
2759
2876
  providerRegistry.register("anthropic", {
2760
2877
  stream: (options) => streamAnthropic(options)
2761
2878
  });
@@ -2784,10 +2901,11 @@ providerRegistry.register("glm", {
2784
2901
  })
2785
2902
  });
2786
2903
  providerRegistry.register("moonshot", {
2787
- stream: (options) => streamOpenAI({
2788
- ...options,
2789
- baseUrl: options.baseUrl ?? "https://api.moonshot.ai/v1"
2790
- })
2904
+ stream: (options) => {
2905
+ const baseUrl = options.baseUrl ?? "https://api.moonshot.ai/v1";
2906
+ const defaultHeaders = baseUrl.includes("api.kimi.com") ? { "User-Agent": KIMI_CODE_USER_AGENT, ...options.defaultHeaders } : options.defaultHeaders;
2907
+ return streamOpenAI({ ...options, baseUrl, defaultHeaders });
2908
+ }
2791
2909
  });
2792
2910
  providerRegistry.register("deepseek", {
2793
2911
  stream: (options) => streamOpenAI({
@@ -2820,8 +2938,113 @@ function stream(options) {
2820
2938
  `Unknown provider: "${options.provider}". Registered: ${providerRegistry.list().join(", ")}`
2821
2939
  );
2822
2940
  }
2941
+ if (options.supportsVideo !== true && messagesContainVideo(options.messages)) {
2942
+ throw new VideoUnsupportedError();
2943
+ }
2823
2944
  return entry.stream(options);
2824
2945
  }
2946
+ function messagesContainVideo(messages) {
2947
+ for (const msg of messages) {
2948
+ if (typeof msg.content === "string" || !Array.isArray(msg.content)) continue;
2949
+ for (const part of msg.content) {
2950
+ if (part.type === "video") return true;
2951
+ if (part.type === "tool_result" && Array.isArray(part.content)) {
2952
+ if (part.content.some((block) => block.type === "video")) return true;
2953
+ }
2954
+ }
2955
+ }
2956
+ return false;
2957
+ }
2958
+
2959
+ // src/error-classification.ts
2960
+ var CONTEXT_OVERFLOW_PATTERNS = [
2961
+ /context_length_exceeded/i,
2962
+ /context length exceeded/i,
2963
+ /context window/i,
2964
+ // OpenAI Codex / Responses
2965
+ /maximum context length/i,
2966
+ // OpenAI / OpenRouter / Mistral
2967
+ /prompt is too long/i,
2968
+ // Anthropic
2969
+ /request_too_large/i,
2970
+ // Anthropic HTTP 413
2971
+ /input is too long/i,
2972
+ // Bedrock
2973
+ /input token count.*exceeds the maximum/i,
2974
+ // Gemini
2975
+ /maximum prompt length/i,
2976
+ // xAI / Grok
2977
+ /reduce the length of the messages/i,
2978
+ // Groq
2979
+ /too large for model/i,
2980
+ // Mistral
2981
+ /token limit/i
2982
+ // generic
2983
+ ];
2984
+ var RATE_LIMIT_PATTERNS = [
2985
+ /rate[ _-]?limit/i,
2986
+ /\b429\b/,
2987
+ /too many requests/i,
2988
+ /tokens per minute/i,
2989
+ /requests per minute/i
2990
+ ];
2991
+ var PROVIDER_TRANSIENT_PATTERNS = [
2992
+ /\b5\d\d\b/,
2993
+ /api_error/i,
2994
+ /server_error/i,
2995
+ /internal server error/i,
2996
+ /bad gateway/i,
2997
+ /service unavailable/i,
2998
+ /gateway timeout/i,
2999
+ /overloaded/i,
3000
+ /\b529\b/
3001
+ ];
3002
+ var BILLING_PATTERNS = [
3003
+ /payment required/i,
3004
+ /\b402\b/,
3005
+ /quota_exceeded/i,
3006
+ // underscore variant not in isHardBillingMessage
3007
+ /credit balance/i
3008
+ ];
3009
+ var AUTH_PATTERNS = [
3010
+ /invalid[ _]api[ _]key/i,
3011
+ /unauthorized/i,
3012
+ /\b401\b/,
3013
+ /authentication[ _]failed/i,
3014
+ /please run \/login/i
3015
+ // Anthropic Claude Code-style hint
3016
+ ];
3017
+ function matchesAny(message, patterns) {
3018
+ return patterns.some((p) => p.test(message));
3019
+ }
3020
+ function classifyProviderError(message) {
3021
+ if (matchesAny(message, CONTEXT_OVERFLOW_PATTERNS)) {
3022
+ return `[context_overflow] Worker context window exceeded \u2014 the conversation is too large to continue. Recovery: call reset_worker(project) to wipe history, then re-prompt with the task. Re-prompting WITHOUT reset will fail the same way.
3023
+
3024
+ Original: ${message}`;
3025
+ }
3026
+ if (isHardBillingMessage(message) || matchesAny(message, BILLING_PATTERNS)) {
3027
+ return `[billing] Provider billing/quota issue. Recovery: surface to the user \u2014 they need to top up or switch providers. Do NOT retry.
3028
+
3029
+ Original: ${message}`;
3030
+ }
3031
+ if (matchesAny(message, AUTH_PATTERNS)) {
3032
+ return `[auth] Provider authentication failed. Recovery: surface to the user \u2014 they need to re-login. Do NOT retry.
3033
+
3034
+ Original: ${message}`;
3035
+ }
3036
+ if (matchesAny(message, RATE_LIMIT_PATTERNS)) {
3037
+ return `[rate_limited] Provider rate limit hit. Recovery: wait ~30s, then re-prompt the same worker (no reset needed).
3038
+
3039
+ Original: ${message}`;
3040
+ }
3041
+ if (matchesAny(message, PROVIDER_TRANSIENT_PATTERNS)) {
3042
+ return `[provider_transient] Provider server-side/transient error. Recovery: wait briefly, then re-prompt the same worker (no reset needed). If it keeps happening, switch models/providers or check provider status.
3043
+
3044
+ Original: ${message}`;
3045
+ }
3046
+ return message;
3047
+ }
2825
3048
 
2826
3049
  // src/providers/palsu.ts
2827
3050
  function palsuText(text) {
@@ -2979,6 +3202,7 @@ export {
2979
3202
  GGAIError,
2980
3203
  ProviderError,
2981
3204
  StreamResult,
3205
+ classifyProviderError,
2982
3206
  formatError,
2983
3207
  formatErrorForDisplay,
2984
3208
  isHardBillingMessage,