@mariozechner/pi-ai 0.24.5 → 0.25.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.
Files changed (71) hide show
  1. package/README.md +120 -10
  2. package/dist/agent/agent-loop.d.ts.map +1 -1
  3. package/dist/agent/agent-loop.js +58 -5
  4. package/dist/agent/agent-loop.js.map +1 -1
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +2 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/models.generated.d.ts +179 -22
  10. package/dist/models.generated.d.ts.map +1 -1
  11. package/dist/models.generated.js +205 -48
  12. package/dist/models.generated.js.map +1 -1
  13. package/dist/providers/anthropic.d.ts.map +1 -1
  14. package/dist/providers/anthropic.js +0 -2
  15. package/dist/providers/anthropic.js.map +1 -1
  16. package/dist/providers/google-gemini-cli.d.ts +16 -0
  17. package/dist/providers/google-gemini-cli.d.ts.map +1 -0
  18. package/dist/providers/google-gemini-cli.js +347 -0
  19. package/dist/providers/google-gemini-cli.js.map +1 -0
  20. package/dist/providers/google-shared.d.ts +34 -0
  21. package/dist/providers/google-shared.d.ts.map +1 -0
  22. package/dist/providers/google-shared.js +211 -0
  23. package/dist/providers/google-shared.js.map +1 -0
  24. package/dist/providers/google.d.ts.map +1 -1
  25. package/dist/providers/google.js +3 -162
  26. package/dist/providers/google.js.map +1 -1
  27. package/dist/providers/openai-completions.d.ts.map +1 -1
  28. package/dist/providers/openai-completions.js +25 -0
  29. package/dist/providers/openai-completions.js.map +1 -1
  30. package/dist/providers/openai-responses.d.ts.map +1 -1
  31. package/dist/providers/openai-responses.js +24 -1
  32. package/dist/providers/openai-responses.js.map +1 -1
  33. package/dist/providers/transorm-messages.d.ts.map +1 -1
  34. package/dist/providers/transorm-messages.js +62 -43
  35. package/dist/providers/transorm-messages.js.map +1 -1
  36. package/dist/stream.d.ts +15 -0
  37. package/dist/stream.d.ts.map +1 -1
  38. package/dist/stream.js +39 -0
  39. package/dist/stream.js.map +1 -1
  40. package/dist/types.d.ts +4 -2
  41. package/dist/types.d.ts.map +1 -1
  42. package/dist/types.js.map +1 -1
  43. package/dist/utils/oauth/anthropic.d.ts +16 -0
  44. package/dist/utils/oauth/anthropic.d.ts.map +1 -0
  45. package/dist/utils/oauth/anthropic.js +102 -0
  46. package/dist/utils/oauth/anthropic.js.map +1 -0
  47. package/dist/utils/oauth/github-copilot.d.ts +43 -0
  48. package/dist/utils/oauth/github-copilot.d.ts.map +1 -0
  49. package/dist/utils/oauth/github-copilot.js +236 -0
  50. package/dist/utils/oauth/github-copilot.js.map +1 -0
  51. package/dist/utils/oauth/google-antigravity.d.ts +24 -0
  52. package/dist/utils/oauth/google-antigravity.d.ts.map +1 -0
  53. package/dist/utils/oauth/google-antigravity.js +272 -0
  54. package/dist/utils/oauth/google-antigravity.js.map +1 -0
  55. package/dist/utils/oauth/google-gemini-cli.d.ts +24 -0
  56. package/dist/utils/oauth/google-gemini-cli.d.ts.map +1 -0
  57. package/dist/utils/oauth/google-gemini-cli.js +285 -0
  58. package/dist/utils/oauth/google-gemini-cli.js.map +1 -0
  59. package/dist/utils/oauth/index.d.ts +54 -0
  60. package/dist/utils/oauth/index.d.ts.map +1 -0
  61. package/dist/utils/oauth/index.js +147 -0
  62. package/dist/utils/oauth/index.js.map +1 -0
  63. package/dist/utils/oauth/storage.d.ts +81 -0
  64. package/dist/utils/oauth/storage.d.ts.map +1 -0
  65. package/dist/utils/oauth/storage.js +119 -0
  66. package/dist/utils/oauth/storage.js.map +1 -0
  67. package/dist/utils/overflow.d.ts +2 -2
  68. package/dist/utils/overflow.d.ts.map +1 -1
  69. package/dist/utils/overflow.js +7 -4
  70. package/dist/utils/overflow.js.map +1 -1
  71. package/package.json +1 -1
@@ -0,0 +1,81 @@
1
+ /**
2
+ * OAuth credential storage with configurable backend.
3
+ *
4
+ * Default: ~/.pi/agent/oauth.json
5
+ * Override with setOAuthStorage() for custom storage locations or backends.
6
+ */
7
+ export interface OAuthCredentials {
8
+ type: "oauth";
9
+ refresh: string;
10
+ access: string;
11
+ expires: number;
12
+ enterpriseUrl?: string;
13
+ projectId?: string;
14
+ email?: string;
15
+ }
16
+ export interface OAuthStorage {
17
+ [provider: string]: OAuthCredentials;
18
+ }
19
+ export type OAuthProvider = "anthropic" | "github-copilot" | "google-gemini-cli" | "google-antigravity";
20
+ /**
21
+ * Storage backend interface.
22
+ * Implement this to use a custom storage location or backend.
23
+ */
24
+ export interface OAuthStorageBackend {
25
+ /** Load all OAuth credentials. Return empty object if none exist. */
26
+ load(): OAuthStorage;
27
+ /** Save all OAuth credentials. */
28
+ save(storage: OAuthStorage): void;
29
+ }
30
+ /**
31
+ * Configure the OAuth storage backend.
32
+ *
33
+ * @example
34
+ * // Custom file path
35
+ * setOAuthStorage({
36
+ * load: () => JSON.parse(readFileSync('/custom/path/oauth.json', 'utf-8')),
37
+ * save: (storage) => writeFileSync('/custom/path/oauth.json', JSON.stringify(storage))
38
+ * });
39
+ *
40
+ * @example
41
+ * // In-memory storage (for testing)
42
+ * let memoryStorage = {};
43
+ * setOAuthStorage({
44
+ * load: () => memoryStorage,
45
+ * save: (storage) => { memoryStorage = storage; }
46
+ * });
47
+ */
48
+ export declare function setOAuthStorage(backend: OAuthStorageBackend): void;
49
+ /**
50
+ * Reset to default filesystem storage (~/.pi/agent/oauth.json)
51
+ */
52
+ export declare function resetOAuthStorage(): void;
53
+ /**
54
+ * Get the default OAuth path (for reference, may not be used if custom backend is set)
55
+ */
56
+ export declare function getOAuthPath(): string;
57
+ /**
58
+ * Load all OAuth credentials
59
+ */
60
+ export declare function loadOAuthStorage(): OAuthStorage;
61
+ /**
62
+ * Load OAuth credentials for a specific provider
63
+ */
64
+ export declare function loadOAuthCredentials(provider: string): OAuthCredentials | null;
65
+ /**
66
+ * Save OAuth credentials for a specific provider
67
+ */
68
+ export declare function saveOAuthCredentials(provider: string, creds: OAuthCredentials): void;
69
+ /**
70
+ * Remove OAuth credentials for a specific provider
71
+ */
72
+ export declare function removeOAuthCredentials(provider: string): void;
73
+ /**
74
+ * Check if OAuth credentials exist for a provider
75
+ */
76
+ export declare function hasOAuthCredentials(provider: string): boolean;
77
+ /**
78
+ * List all providers with OAuth credentials
79
+ */
80
+ export declare function listOAuthProviders(): string[];
81
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC5B,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAAC;CACrC;AAED,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,oBAAoB,CAAC;AAExG;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IACnC,qEAAqE;IACrE,IAAI,IAAI,YAAY,CAAC;IACrB,kCAAkC;IAClC,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;CAClC;AAsCD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAElE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAMD;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,YAAY,CAE/C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAG9E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAIpF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAI7D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAG7C","sourcesContent":["/**\n * OAuth credential storage with configurable backend.\n *\n * Default: ~/.pi/agent/oauth.json\n * Override with setOAuthStorage() for custom storage locations or backends.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join } from \"path\";\n\nexport interface OAuthCredentials {\n\ttype: \"oauth\";\n\trefresh: string;\n\taccess: string;\n\texpires: number;\n\tenterpriseUrl?: string;\n\tprojectId?: string;\n\temail?: string;\n}\n\nexport interface OAuthStorage {\n\t[provider: string]: OAuthCredentials;\n}\n\nexport type OAuthProvider = \"anthropic\" | \"github-copilot\" | \"google-gemini-cli\" | \"google-antigravity\";\n\n/**\n * Storage backend interface.\n * Implement this to use a custom storage location or backend.\n */\nexport interface OAuthStorageBackend {\n\t/** Load all OAuth credentials. Return empty object if none exist. */\n\tload(): OAuthStorage;\n\t/** Save all OAuth credentials. */\n\tsave(storage: OAuthStorage): void;\n}\n\n// ============================================================================\n// Default filesystem backend\n// ============================================================================\n\nconst DEFAULT_PATH = join(homedir(), \".pi\", \"agent\", \"oauth.json\");\n\nfunction defaultLoad(): OAuthStorage {\n\tif (!existsSync(DEFAULT_PATH)) {\n\t\treturn {};\n\t}\n\ttry {\n\t\tconst content = readFileSync(DEFAULT_PATH, \"utf-8\");\n\t\treturn JSON.parse(content);\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction defaultSave(storage: OAuthStorage): void {\n\tconst configDir = dirname(DEFAULT_PATH);\n\tif (!existsSync(configDir)) {\n\t\tmkdirSync(configDir, { recursive: true, mode: 0o700 });\n\t}\n\twriteFileSync(DEFAULT_PATH, JSON.stringify(storage, null, 2), \"utf-8\");\n\tchmodSync(DEFAULT_PATH, 0o600);\n}\n\n// ============================================================================\n// Configurable backend\n// ============================================================================\n\nlet currentBackend: OAuthStorageBackend = {\n\tload: defaultLoad,\n\tsave: defaultSave,\n};\n\n/**\n * Configure the OAuth storage backend.\n *\n * @example\n * // Custom file path\n * setOAuthStorage({\n * load: () => JSON.parse(readFileSync('/custom/path/oauth.json', 'utf-8')),\n * save: (storage) => writeFileSync('/custom/path/oauth.json', JSON.stringify(storage))\n * });\n *\n * @example\n * // In-memory storage (for testing)\n * let memoryStorage = {};\n * setOAuthStorage({\n * load: () => memoryStorage,\n * save: (storage) => { memoryStorage = storage; }\n * });\n */\nexport function setOAuthStorage(backend: OAuthStorageBackend): void {\n\tcurrentBackend = backend;\n}\n\n/**\n * Reset to default filesystem storage (~/.pi/agent/oauth.json)\n */\nexport function resetOAuthStorage(): void {\n\tcurrentBackend = { load: defaultLoad, save: defaultSave };\n}\n\n/**\n * Get the default OAuth path (for reference, may not be used if custom backend is set)\n */\nexport function getOAuthPath(): string {\n\treturn DEFAULT_PATH;\n}\n\n// ============================================================================\n// Public API (uses current backend)\n// ============================================================================\n\n/**\n * Load all OAuth credentials\n */\nexport function loadOAuthStorage(): OAuthStorage {\n\treturn currentBackend.load();\n}\n\n/**\n * Load OAuth credentials for a specific provider\n */\nexport function loadOAuthCredentials(provider: string): OAuthCredentials | null {\n\tconst storage = currentBackend.load();\n\treturn storage[provider] || null;\n}\n\n/**\n * Save OAuth credentials for a specific provider\n */\nexport function saveOAuthCredentials(provider: string, creds: OAuthCredentials): void {\n\tconst storage = currentBackend.load();\n\tstorage[provider] = creds;\n\tcurrentBackend.save(storage);\n}\n\n/**\n * Remove OAuth credentials for a specific provider\n */\nexport function removeOAuthCredentials(provider: string): void {\n\tconst storage = currentBackend.load();\n\tdelete storage[provider];\n\tcurrentBackend.save(storage);\n}\n\n/**\n * Check if OAuth credentials exist for a provider\n */\nexport function hasOAuthCredentials(provider: string): boolean {\n\treturn loadOAuthCredentials(provider) !== null;\n}\n\n/**\n * List all providers with OAuth credentials\n */\nexport function listOAuthProviders(): string[] {\n\tconst storage = currentBackend.load();\n\treturn Object.keys(storage);\n}\n"]}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * OAuth credential storage with configurable backend.
3
+ *
4
+ * Default: ~/.pi/agent/oauth.json
5
+ * Override with setOAuthStorage() for custom storage locations or backends.
6
+ */
7
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
8
+ import { homedir } from "os";
9
+ import { dirname, join } from "path";
10
+ // ============================================================================
11
+ // Default filesystem backend
12
+ // ============================================================================
13
+ const DEFAULT_PATH = join(homedir(), ".pi", "agent", "oauth.json");
14
+ function defaultLoad() {
15
+ if (!existsSync(DEFAULT_PATH)) {
16
+ return {};
17
+ }
18
+ try {
19
+ const content = readFileSync(DEFAULT_PATH, "utf-8");
20
+ return JSON.parse(content);
21
+ }
22
+ catch {
23
+ return {};
24
+ }
25
+ }
26
+ function defaultSave(storage) {
27
+ const configDir = dirname(DEFAULT_PATH);
28
+ if (!existsSync(configDir)) {
29
+ mkdirSync(configDir, { recursive: true, mode: 0o700 });
30
+ }
31
+ writeFileSync(DEFAULT_PATH, JSON.stringify(storage, null, 2), "utf-8");
32
+ chmodSync(DEFAULT_PATH, 0o600);
33
+ }
34
+ // ============================================================================
35
+ // Configurable backend
36
+ // ============================================================================
37
+ let currentBackend = {
38
+ load: defaultLoad,
39
+ save: defaultSave,
40
+ };
41
+ /**
42
+ * Configure the OAuth storage backend.
43
+ *
44
+ * @example
45
+ * // Custom file path
46
+ * setOAuthStorage({
47
+ * load: () => JSON.parse(readFileSync('/custom/path/oauth.json', 'utf-8')),
48
+ * save: (storage) => writeFileSync('/custom/path/oauth.json', JSON.stringify(storage))
49
+ * });
50
+ *
51
+ * @example
52
+ * // In-memory storage (for testing)
53
+ * let memoryStorage = {};
54
+ * setOAuthStorage({
55
+ * load: () => memoryStorage,
56
+ * save: (storage) => { memoryStorage = storage; }
57
+ * });
58
+ */
59
+ export function setOAuthStorage(backend) {
60
+ currentBackend = backend;
61
+ }
62
+ /**
63
+ * Reset to default filesystem storage (~/.pi/agent/oauth.json)
64
+ */
65
+ export function resetOAuthStorage() {
66
+ currentBackend = { load: defaultLoad, save: defaultSave };
67
+ }
68
+ /**
69
+ * Get the default OAuth path (for reference, may not be used if custom backend is set)
70
+ */
71
+ export function getOAuthPath() {
72
+ return DEFAULT_PATH;
73
+ }
74
+ // ============================================================================
75
+ // Public API (uses current backend)
76
+ // ============================================================================
77
+ /**
78
+ * Load all OAuth credentials
79
+ */
80
+ export function loadOAuthStorage() {
81
+ return currentBackend.load();
82
+ }
83
+ /**
84
+ * Load OAuth credentials for a specific provider
85
+ */
86
+ export function loadOAuthCredentials(provider) {
87
+ const storage = currentBackend.load();
88
+ return storage[provider] || null;
89
+ }
90
+ /**
91
+ * Save OAuth credentials for a specific provider
92
+ */
93
+ export function saveOAuthCredentials(provider, creds) {
94
+ const storage = currentBackend.load();
95
+ storage[provider] = creds;
96
+ currentBackend.save(storage);
97
+ }
98
+ /**
99
+ * Remove OAuth credentials for a specific provider
100
+ */
101
+ export function removeOAuthCredentials(provider) {
102
+ const storage = currentBackend.load();
103
+ delete storage[provider];
104
+ currentBackend.save(storage);
105
+ }
106
+ /**
107
+ * Check if OAuth credentials exist for a provider
108
+ */
109
+ export function hasOAuthCredentials(provider) {
110
+ return loadOAuthCredentials(provider) !== null;
111
+ }
112
+ /**
113
+ * List all providers with OAuth credentials
114
+ */
115
+ export function listOAuthProviders() {
116
+ const storage = currentBackend.load();
117
+ return Object.keys(storage);
118
+ }
119
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/utils/oauth/storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OAuth credential storage with configurable backend.\n *\n * Default: ~/.pi/agent/oauth.json\n * Override with setOAuthStorage() for custom storage locations or backends.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join } from \"path\";\n\nexport interface OAuthCredentials {\n\ttype: \"oauth\";\n\trefresh: string;\n\taccess: string;\n\texpires: number;\n\tenterpriseUrl?: string;\n\tprojectId?: string;\n\temail?: string;\n}\n\nexport interface OAuthStorage {\n\t[provider: string]: OAuthCredentials;\n}\n\nexport type OAuthProvider = \"anthropic\" | \"github-copilot\" | \"google-gemini-cli\" | \"google-antigravity\";\n\n/**\n * Storage backend interface.\n * Implement this to use a custom storage location or backend.\n */\nexport interface OAuthStorageBackend {\n\t/** Load all OAuth credentials. Return empty object if none exist. */\n\tload(): OAuthStorage;\n\t/** Save all OAuth credentials. */\n\tsave(storage: OAuthStorage): void;\n}\n\n// ============================================================================\n// Default filesystem backend\n// ============================================================================\n\nconst DEFAULT_PATH = join(homedir(), \".pi\", \"agent\", \"oauth.json\");\n\nfunction defaultLoad(): OAuthStorage {\n\tif (!existsSync(DEFAULT_PATH)) {\n\t\treturn {};\n\t}\n\ttry {\n\t\tconst content = readFileSync(DEFAULT_PATH, \"utf-8\");\n\t\treturn JSON.parse(content);\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction defaultSave(storage: OAuthStorage): void {\n\tconst configDir = dirname(DEFAULT_PATH);\n\tif (!existsSync(configDir)) {\n\t\tmkdirSync(configDir, { recursive: true, mode: 0o700 });\n\t}\n\twriteFileSync(DEFAULT_PATH, JSON.stringify(storage, null, 2), \"utf-8\");\n\tchmodSync(DEFAULT_PATH, 0o600);\n}\n\n// ============================================================================\n// Configurable backend\n// ============================================================================\n\nlet currentBackend: OAuthStorageBackend = {\n\tload: defaultLoad,\n\tsave: defaultSave,\n};\n\n/**\n * Configure the OAuth storage backend.\n *\n * @example\n * // Custom file path\n * setOAuthStorage({\n * load: () => JSON.parse(readFileSync('/custom/path/oauth.json', 'utf-8')),\n * save: (storage) => writeFileSync('/custom/path/oauth.json', JSON.stringify(storage))\n * });\n *\n * @example\n * // In-memory storage (for testing)\n * let memoryStorage = {};\n * setOAuthStorage({\n * load: () => memoryStorage,\n * save: (storage) => { memoryStorage = storage; }\n * });\n */\nexport function setOAuthStorage(backend: OAuthStorageBackend): void {\n\tcurrentBackend = backend;\n}\n\n/**\n * Reset to default filesystem storage (~/.pi/agent/oauth.json)\n */\nexport function resetOAuthStorage(): void {\n\tcurrentBackend = { load: defaultLoad, save: defaultSave };\n}\n\n/**\n * Get the default OAuth path (for reference, may not be used if custom backend is set)\n */\nexport function getOAuthPath(): string {\n\treturn DEFAULT_PATH;\n}\n\n// ============================================================================\n// Public API (uses current backend)\n// ============================================================================\n\n/**\n * Load all OAuth credentials\n */\nexport function loadOAuthStorage(): OAuthStorage {\n\treturn currentBackend.load();\n}\n\n/**\n * Load OAuth credentials for a specific provider\n */\nexport function loadOAuthCredentials(provider: string): OAuthCredentials | null {\n\tconst storage = currentBackend.load();\n\treturn storage[provider] || null;\n}\n\n/**\n * Save OAuth credentials for a specific provider\n */\nexport function saveOAuthCredentials(provider: string, creds: OAuthCredentials): void {\n\tconst storage = currentBackend.load();\n\tstorage[provider] = creds;\n\tcurrentBackend.save(storage);\n}\n\n/**\n * Remove OAuth credentials for a specific provider\n */\nexport function removeOAuthCredentials(provider: string): void {\n\tconst storage = currentBackend.load();\n\tdelete storage[provider];\n\tcurrentBackend.save(storage);\n}\n\n/**\n * Check if OAuth credentials exist for a provider\n */\nexport function hasOAuthCredentials(provider: string): boolean {\n\treturn loadOAuthCredentials(provider) !== null;\n}\n\n/**\n * List all providers with OAuth credentials\n */\nexport function listOAuthProviders(): string[] {\n\tconst storage = currentBackend.load();\n\treturn Object.keys(storage);\n}\n"]}
@@ -16,8 +16,8 @@ import type { AssistantMessage } from "../types.js";
16
16
  * - Google Gemini: "input token count exceeds the maximum"
17
17
  * - xAI (Grok): "maximum prompt length is X but request contains Y"
18
18
  * - Groq: "reduce the length of the messages"
19
- * - Cerebras: 400/413 status code (no body)
20
- * - Mistral: 400/413 status code (no body)
19
+ * - Cerebras: 400/413/429 status code (no body)
20
+ * - Mistral: 400/413/429 status code (no body)
21
21
  * - OpenRouter (all backends): "maximum context length is X tokens"
22
22
  * - llama.cpp: "exceeds the available context size"
23
23
  * - LM Studio: "greater than the context length"
@@ -1 +1 @@
1
- {"version":3,"file":"overflow.d.ts","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAqCpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAuB5F;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C","sourcesContent":["import type { AssistantMessage } from \"../types.js\";\n\n/**\n * Regex patterns to detect context overflow errors from different providers.\n *\n * These patterns match error messages returned when the input exceeds\n * the model's context window.\n *\n * Provider-specific patterns (with example error messages):\n *\n * - Anthropic: \"prompt is too long: 213462 tokens > 200000 maximum\"\n * - OpenAI: \"Your input exceeds the context window of this model\"\n * - Google: \"The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)\"\n * - xAI: \"This model's maximum prompt length is 131072 but the request contains 537812 tokens\"\n * - Groq: \"Please reduce the length of the messages or completion\"\n * - OpenRouter: \"This endpoint's maximum context length is X tokens. However, you requested about Y tokens\"\n * - llama.cpp: \"the request exceeds the available context size, try increasing it\"\n * - LM Studio: \"tokens to keep from the initial prompt is greater than the context length\"\n * - Cerebras: Returns \"400 status code (no body)\" - handled separately below\n * - Mistral: Returns \"400 status code (no body)\" - handled separately below\n * - z.ai: Does NOT error, accepts overflow silently - handled via usage.input > contextWindow\n * - Ollama: Silently truncates input - not detectable via error message\n */\nconst OVERFLOW_PATTERNS = [\n\t/prompt is too long/i, // Anthropic\n\t/exceeds the context window/i, // OpenAI (Completions & Responses API)\n\t/input token count.*exceeds the maximum/i, // Google (Gemini)\n\t/maximum prompt length is \\d+/i, // xAI (Grok)\n\t/reduce the length of the messages/i, // Groq\n\t/maximum context length is \\d+ tokens/i, // OpenRouter (all backends)\n\t/exceeds the available context size/i, // llama.cpp server\n\t/greater than the context length/i, // LM Studio\n\t/context length exceeded/i, // Generic fallback\n\t/too many tokens/i, // Generic fallback\n\t/token limit exceeded/i, // Generic fallback\n];\n\n/**\n * Check if an assistant message represents a context overflow error.\n *\n * This handles two cases:\n * 1. Error-based overflow: Most providers return stopReason \"error\" with a\n * specific error message pattern.\n * 2. Silent overflow: Some providers accept overflow requests and return\n * successfully. For these, we check if usage.input exceeds the context window.\n *\n * ## Reliability by Provider\n *\n * **Reliable detection (returns error with detectable message):**\n * - Anthropic: \"prompt is too long: X tokens > Y maximum\"\n * - OpenAI (Completions & Responses): \"exceeds the context window\"\n * - Google Gemini: \"input token count exceeds the maximum\"\n * - xAI (Grok): \"maximum prompt length is X but request contains Y\"\n * - Groq: \"reduce the length of the messages\"\n * - Cerebras: 400/413 status code (no body)\n * - Mistral: 400/413 status code (no body)\n * - OpenRouter (all backends): \"maximum context length is X tokens\"\n * - llama.cpp: \"exceeds the available context size\"\n * - LM Studio: \"greater than the context length\"\n *\n * **Unreliable detection:**\n * - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),\n * sometimes returns rate limit errors. Pass contextWindow param to detect silent overflow.\n * - Ollama: Silently truncates input without error. Cannot be detected via this function.\n * The response will have usage.input < expected, but we don't know the expected value.\n *\n * ## Custom Providers\n *\n * If you've added custom models via settings.json, this function may not detect\n * overflow errors from those providers. To add support:\n *\n * 1. Send a request that exceeds the model's context window\n * 2. Check the errorMessage in the response\n * 3. Create a regex pattern that matches the error\n * 4. The pattern should be added to OVERFLOW_PATTERNS in this file, or\n * check the errorMessage yourself before calling this function\n *\n * @param message - The assistant message to check\n * @param contextWindow - Optional context window size for detecting silent overflow (z.ai)\n * @returns true if the message indicates a context overflow\n */\nexport function isContextOverflow(message: AssistantMessage, contextWindow?: number): boolean {\n\t// Case 1: Check error message patterns\n\tif (message.stopReason === \"error\" && message.errorMessage) {\n\t\t// Check known patterns\n\t\tif (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!))) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Cerebras and Mistral return 400/413 with no body - check for status code pattern\n\t\tif (/^4(00|13)\\s*(status code)?\\s*\\(no body\\)/i.test(message.errorMessage)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// Case 2: Silent overflow (z.ai style) - successful but usage exceeds context\n\tif (contextWindow && message.stopReason === \"stop\") {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens > contextWindow) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Get the overflow patterns for testing purposes.\n */\nexport function getOverflowPatterns(): RegExp[] {\n\treturn [...OVERFLOW_PATTERNS];\n}\n"]}
1
+ {"version":3,"file":"overflow.d.ts","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAuCpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAwB5F;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C","sourcesContent":["import type { AssistantMessage } from \"../types.js\";\n\n/**\n * Regex patterns to detect context overflow errors from different providers.\n *\n * These patterns match error messages returned when the input exceeds\n * the model's context window.\n *\n * Provider-specific patterns (with example error messages):\n *\n * - Anthropic: \"prompt is too long: 213462 tokens > 200000 maximum\"\n * - OpenAI: \"Your input exceeds the context window of this model\"\n * - Google: \"The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)\"\n * - xAI: \"This model's maximum prompt length is 131072 but the request contains 537812 tokens\"\n * - Groq: \"Please reduce the length of the messages or completion\"\n * - OpenRouter: \"This endpoint's maximum context length is X tokens. However, you requested about Y tokens\"\n * - llama.cpp: \"the request exceeds the available context size, try increasing it\"\n * - LM Studio: \"tokens to keep from the initial prompt is greater than the context length\"\n * - GitHub Copilot: \"prompt token count of X exceeds the limit of Y\"\n * - Cerebras: Returns \"400 status code (no body)\" - handled separately below\n * - Mistral: Returns \"400 status code (no body)\" - handled separately below\n * - z.ai: Does NOT error, accepts overflow silently - handled via usage.input > contextWindow\n * - Ollama: Silently truncates input - not detectable via error message\n */\nconst OVERFLOW_PATTERNS = [\n\t/prompt is too long/i, // Anthropic\n\t/exceeds the context window/i, // OpenAI (Completions & Responses API)\n\t/input token count.*exceeds the maximum/i, // Google (Gemini)\n\t/maximum prompt length is \\d+/i, // xAI (Grok)\n\t/reduce the length of the messages/i, // Groq\n\t/maximum context length is \\d+ tokens/i, // OpenRouter (all backends)\n\t/exceeds the limit of \\d+/i, // GitHub Copilot\n\t/exceeds the available context size/i, // llama.cpp server\n\t/greater than the context length/i, // LM Studio\n\t/context length exceeded/i, // Generic fallback\n\t/too many tokens/i, // Generic fallback\n\t/token limit exceeded/i, // Generic fallback\n];\n\n/**\n * Check if an assistant message represents a context overflow error.\n *\n * This handles two cases:\n * 1. Error-based overflow: Most providers return stopReason \"error\" with a\n * specific error message pattern.\n * 2. Silent overflow: Some providers accept overflow requests and return\n * successfully. For these, we check if usage.input exceeds the context window.\n *\n * ## Reliability by Provider\n *\n * **Reliable detection (returns error with detectable message):**\n * - Anthropic: \"prompt is too long: X tokens > Y maximum\"\n * - OpenAI (Completions & Responses): \"exceeds the context window\"\n * - Google Gemini: \"input token count exceeds the maximum\"\n * - xAI (Grok): \"maximum prompt length is X but request contains Y\"\n * - Groq: \"reduce the length of the messages\"\n * - Cerebras: 400/413/429 status code (no body)\n * - Mistral: 400/413/429 status code (no body)\n * - OpenRouter (all backends): \"maximum context length is X tokens\"\n * - llama.cpp: \"exceeds the available context size\"\n * - LM Studio: \"greater than the context length\"\n *\n * **Unreliable detection:**\n * - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),\n * sometimes returns rate limit errors. Pass contextWindow param to detect silent overflow.\n * - Ollama: Silently truncates input without error. Cannot be detected via this function.\n * The response will have usage.input < expected, but we don't know the expected value.\n *\n * ## Custom Providers\n *\n * If you've added custom models via settings.json, this function may not detect\n * overflow errors from those providers. To add support:\n *\n * 1. Send a request that exceeds the model's context window\n * 2. Check the errorMessage in the response\n * 3. Create a regex pattern that matches the error\n * 4. The pattern should be added to OVERFLOW_PATTERNS in this file, or\n * check the errorMessage yourself before calling this function\n *\n * @param message - The assistant message to check\n * @param contextWindow - Optional context window size for detecting silent overflow (z.ai)\n * @returns true if the message indicates a context overflow\n */\nexport function isContextOverflow(message: AssistantMessage, contextWindow?: number): boolean {\n\t// Case 1: Check error message patterns\n\tif (message.stopReason === \"error\" && message.errorMessage) {\n\t\t// Check known patterns\n\t\tif (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!))) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Cerebras and Mistral return 400/413/429 with no body - check for status code pattern\n\t\t// 429 can indicate token-based rate limiting which correlates with context overflow\n\t\tif (/^4(00|13|29)\\s*(status code)?\\s*\\(no body\\)/i.test(message.errorMessage)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// Case 2: Silent overflow (z.ai style) - successful but usage exceeds context\n\tif (contextWindow && message.stopReason === \"stop\") {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens > contextWindow) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Get the overflow patterns for testing purposes.\n */\nexport function getOverflowPatterns(): RegExp[] {\n\treturn [...OVERFLOW_PATTERNS];\n}\n"]}
@@ -14,6 +14,7 @@
14
14
  * - OpenRouter: "This endpoint's maximum context length is X tokens. However, you requested about Y tokens"
15
15
  * - llama.cpp: "the request exceeds the available context size, try increasing it"
16
16
  * - LM Studio: "tokens to keep from the initial prompt is greater than the context length"
17
+ * - GitHub Copilot: "prompt token count of X exceeds the limit of Y"
17
18
  * - Cerebras: Returns "400 status code (no body)" - handled separately below
18
19
  * - Mistral: Returns "400 status code (no body)" - handled separately below
19
20
  * - z.ai: Does NOT error, accepts overflow silently - handled via usage.input > contextWindow
@@ -26,6 +27,7 @@ const OVERFLOW_PATTERNS = [
26
27
  /maximum prompt length is \d+/i, // xAI (Grok)
27
28
  /reduce the length of the messages/i, // Groq
28
29
  /maximum context length is \d+ tokens/i, // OpenRouter (all backends)
30
+ /exceeds the limit of \d+/i, // GitHub Copilot
29
31
  /exceeds the available context size/i, // llama.cpp server
30
32
  /greater than the context length/i, // LM Studio
31
33
  /context length exceeded/i, // Generic fallback
@@ -49,8 +51,8 @@ const OVERFLOW_PATTERNS = [
49
51
  * - Google Gemini: "input token count exceeds the maximum"
50
52
  * - xAI (Grok): "maximum prompt length is X but request contains Y"
51
53
  * - Groq: "reduce the length of the messages"
52
- * - Cerebras: 400/413 status code (no body)
53
- * - Mistral: 400/413 status code (no body)
54
+ * - Cerebras: 400/413/429 status code (no body)
55
+ * - Mistral: 400/413/429 status code (no body)
54
56
  * - OpenRouter (all backends): "maximum context length is X tokens"
55
57
  * - llama.cpp: "exceeds the available context size"
56
58
  * - LM Studio: "greater than the context length"
@@ -83,8 +85,9 @@ export function isContextOverflow(message, contextWindow) {
83
85
  if (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage))) {
84
86
  return true;
85
87
  }
86
- // Cerebras and Mistral return 400/413 with no body - check for status code pattern
87
- if (/^4(00|13)\s*(status code)?\s*\(no body\)/i.test(message.errorMessage)) {
88
+ // Cerebras and Mistral return 400/413/429 with no body - check for status code pattern
89
+ // 429 can indicate token-based rate limiting which correlates with context overflow
90
+ if (/^4(00|13|29)\s*(status code)?\s*\(no body\)/i.test(message.errorMessage)) {
88
91
  return true;
89
92
  }
90
93
  }
@@ -1 +1 @@
1
- {"version":3,"file":"overflow.js","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,iBAAiB,GAAG;IACzB,qBAAqB,EAAE,YAAY;IACnC,6BAA6B,EAAE,uCAAuC;IACtE,yCAAyC,EAAE,kBAAkB;IAC7D,+BAA+B,EAAE,aAAa;IAC9C,oCAAoC,EAAE,OAAO;IAC7C,uCAAuC,EAAE,4BAA4B;IACrE,qCAAqC,EAAE,mBAAmB;IAC1D,kCAAkC,EAAE,YAAY;IAChD,0BAA0B,EAAE,mBAAmB;IAC/C,kBAAkB,EAAE,mBAAmB;IACvC,uBAAuB,EAAE,mBAAmB;CAC5C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAyB,EAAE,aAAsB,EAAW;IAC7F,uCAAuC;IACvC,IAAI,OAAO,CAAC,UAAU,KAAK,OAAO,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;QAC5D,uBAAuB;QACvB,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,YAAa,CAAC,CAAC,EAAE,CAAC;YAClE,OAAO,IAAI,CAAC;QACb,CAAC;QAED,mFAAmF;QACnF,IAAI,2CAA2C,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC5E,OAAO,IAAI,CAAC;QACb,CAAC;IACF,CAAC;IAED,8EAA8E;IAC9E,IAAI,aAAa,IAAI,OAAO,CAAC,UAAU,KAAK,MAAM,EAAE,CAAC;QACpD,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;QAClE,IAAI,WAAW,GAAG,aAAa,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC;QACb,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAC;AAAA,CACb;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,GAAa;IAC/C,OAAO,CAAC,GAAG,iBAAiB,CAAC,CAAC;AAAA,CAC9B","sourcesContent":["import type { AssistantMessage } from \"../types.js\";\n\n/**\n * Regex patterns to detect context overflow errors from different providers.\n *\n * These patterns match error messages returned when the input exceeds\n * the model's context window.\n *\n * Provider-specific patterns (with example error messages):\n *\n * - Anthropic: \"prompt is too long: 213462 tokens > 200000 maximum\"\n * - OpenAI: \"Your input exceeds the context window of this model\"\n * - Google: \"The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)\"\n * - xAI: \"This model's maximum prompt length is 131072 but the request contains 537812 tokens\"\n * - Groq: \"Please reduce the length of the messages or completion\"\n * - OpenRouter: \"This endpoint's maximum context length is X tokens. However, you requested about Y tokens\"\n * - llama.cpp: \"the request exceeds the available context size, try increasing it\"\n * - LM Studio: \"tokens to keep from the initial prompt is greater than the context length\"\n * - Cerebras: Returns \"400 status code (no body)\" - handled separately below\n * - Mistral: Returns \"400 status code (no body)\" - handled separately below\n * - z.ai: Does NOT error, accepts overflow silently - handled via usage.input > contextWindow\n * - Ollama: Silently truncates input - not detectable via error message\n */\nconst OVERFLOW_PATTERNS = [\n\t/prompt is too long/i, // Anthropic\n\t/exceeds the context window/i, // OpenAI (Completions & Responses API)\n\t/input token count.*exceeds the maximum/i, // Google (Gemini)\n\t/maximum prompt length is \\d+/i, // xAI (Grok)\n\t/reduce the length of the messages/i, // Groq\n\t/maximum context length is \\d+ tokens/i, // OpenRouter (all backends)\n\t/exceeds the available context size/i, // llama.cpp server\n\t/greater than the context length/i, // LM Studio\n\t/context length exceeded/i, // Generic fallback\n\t/too many tokens/i, // Generic fallback\n\t/token limit exceeded/i, // Generic fallback\n];\n\n/**\n * Check if an assistant message represents a context overflow error.\n *\n * This handles two cases:\n * 1. Error-based overflow: Most providers return stopReason \"error\" with a\n * specific error message pattern.\n * 2. Silent overflow: Some providers accept overflow requests and return\n * successfully. For these, we check if usage.input exceeds the context window.\n *\n * ## Reliability by Provider\n *\n * **Reliable detection (returns error with detectable message):**\n * - Anthropic: \"prompt is too long: X tokens > Y maximum\"\n * - OpenAI (Completions & Responses): \"exceeds the context window\"\n * - Google Gemini: \"input token count exceeds the maximum\"\n * - xAI (Grok): \"maximum prompt length is X but request contains Y\"\n * - Groq: \"reduce the length of the messages\"\n * - Cerebras: 400/413 status code (no body)\n * - Mistral: 400/413 status code (no body)\n * - OpenRouter (all backends): \"maximum context length is X tokens\"\n * - llama.cpp: \"exceeds the available context size\"\n * - LM Studio: \"greater than the context length\"\n *\n * **Unreliable detection:**\n * - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),\n * sometimes returns rate limit errors. Pass contextWindow param to detect silent overflow.\n * - Ollama: Silently truncates input without error. Cannot be detected via this function.\n * The response will have usage.input < expected, but we don't know the expected value.\n *\n * ## Custom Providers\n *\n * If you've added custom models via settings.json, this function may not detect\n * overflow errors from those providers. To add support:\n *\n * 1. Send a request that exceeds the model's context window\n * 2. Check the errorMessage in the response\n * 3. Create a regex pattern that matches the error\n * 4. The pattern should be added to OVERFLOW_PATTERNS in this file, or\n * check the errorMessage yourself before calling this function\n *\n * @param message - The assistant message to check\n * @param contextWindow - Optional context window size for detecting silent overflow (z.ai)\n * @returns true if the message indicates a context overflow\n */\nexport function isContextOverflow(message: AssistantMessage, contextWindow?: number): boolean {\n\t// Case 1: Check error message patterns\n\tif (message.stopReason === \"error\" && message.errorMessage) {\n\t\t// Check known patterns\n\t\tif (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!))) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Cerebras and Mistral return 400/413 with no body - check for status code pattern\n\t\tif (/^4(00|13)\\s*(status code)?\\s*\\(no body\\)/i.test(message.errorMessage)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// Case 2: Silent overflow (z.ai style) - successful but usage exceeds context\n\tif (contextWindow && message.stopReason === \"stop\") {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens > contextWindow) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Get the overflow patterns for testing purposes.\n */\nexport function getOverflowPatterns(): RegExp[] {\n\treturn [...OVERFLOW_PATTERNS];\n}\n"]}
1
+ {"version":3,"file":"overflow.js","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,iBAAiB,GAAG;IACzB,qBAAqB,EAAE,YAAY;IACnC,6BAA6B,EAAE,uCAAuC;IACtE,yCAAyC,EAAE,kBAAkB;IAC7D,+BAA+B,EAAE,aAAa;IAC9C,oCAAoC,EAAE,OAAO;IAC7C,uCAAuC,EAAE,4BAA4B;IACrE,2BAA2B,EAAE,iBAAiB;IAC9C,qCAAqC,EAAE,mBAAmB;IAC1D,kCAAkC,EAAE,YAAY;IAChD,0BAA0B,EAAE,mBAAmB;IAC/C,kBAAkB,EAAE,mBAAmB;IACvC,uBAAuB,EAAE,mBAAmB;CAC5C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAyB,EAAE,aAAsB,EAAW;IAC7F,uCAAuC;IACvC,IAAI,OAAO,CAAC,UAAU,KAAK,OAAO,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;QAC5D,uBAAuB;QACvB,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,YAAa,CAAC,CAAC,EAAE,CAAC;YAClE,OAAO,IAAI,CAAC;QACb,CAAC;QAED,uFAAuF;QACvF,oFAAoF;QACpF,IAAI,8CAA8C,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC/E,OAAO,IAAI,CAAC;QACb,CAAC;IACF,CAAC;IAED,8EAA8E;IAC9E,IAAI,aAAa,IAAI,OAAO,CAAC,UAAU,KAAK,MAAM,EAAE,CAAC;QACpD,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;QAClE,IAAI,WAAW,GAAG,aAAa,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC;QACb,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAC;AAAA,CACb;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,GAAa;IAC/C,OAAO,CAAC,GAAG,iBAAiB,CAAC,CAAC;AAAA,CAC9B","sourcesContent":["import type { AssistantMessage } from \"../types.js\";\n\n/**\n * Regex patterns to detect context overflow errors from different providers.\n *\n * These patterns match error messages returned when the input exceeds\n * the model's context window.\n *\n * Provider-specific patterns (with example error messages):\n *\n * - Anthropic: \"prompt is too long: 213462 tokens > 200000 maximum\"\n * - OpenAI: \"Your input exceeds the context window of this model\"\n * - Google: \"The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)\"\n * - xAI: \"This model's maximum prompt length is 131072 but the request contains 537812 tokens\"\n * - Groq: \"Please reduce the length of the messages or completion\"\n * - OpenRouter: \"This endpoint's maximum context length is X tokens. However, you requested about Y tokens\"\n * - llama.cpp: \"the request exceeds the available context size, try increasing it\"\n * - LM Studio: \"tokens to keep from the initial prompt is greater than the context length\"\n * - GitHub Copilot: \"prompt token count of X exceeds the limit of Y\"\n * - Cerebras: Returns \"400 status code (no body)\" - handled separately below\n * - Mistral: Returns \"400 status code (no body)\" - handled separately below\n * - z.ai: Does NOT error, accepts overflow silently - handled via usage.input > contextWindow\n * - Ollama: Silently truncates input - not detectable via error message\n */\nconst OVERFLOW_PATTERNS = [\n\t/prompt is too long/i, // Anthropic\n\t/exceeds the context window/i, // OpenAI (Completions & Responses API)\n\t/input token count.*exceeds the maximum/i, // Google (Gemini)\n\t/maximum prompt length is \\d+/i, // xAI (Grok)\n\t/reduce the length of the messages/i, // Groq\n\t/maximum context length is \\d+ tokens/i, // OpenRouter (all backends)\n\t/exceeds the limit of \\d+/i, // GitHub Copilot\n\t/exceeds the available context size/i, // llama.cpp server\n\t/greater than the context length/i, // LM Studio\n\t/context length exceeded/i, // Generic fallback\n\t/too many tokens/i, // Generic fallback\n\t/token limit exceeded/i, // Generic fallback\n];\n\n/**\n * Check if an assistant message represents a context overflow error.\n *\n * This handles two cases:\n * 1. Error-based overflow: Most providers return stopReason \"error\" with a\n * specific error message pattern.\n * 2. Silent overflow: Some providers accept overflow requests and return\n * successfully. For these, we check if usage.input exceeds the context window.\n *\n * ## Reliability by Provider\n *\n * **Reliable detection (returns error with detectable message):**\n * - Anthropic: \"prompt is too long: X tokens > Y maximum\"\n * - OpenAI (Completions & Responses): \"exceeds the context window\"\n * - Google Gemini: \"input token count exceeds the maximum\"\n * - xAI (Grok): \"maximum prompt length is X but request contains Y\"\n * - Groq: \"reduce the length of the messages\"\n * - Cerebras: 400/413/429 status code (no body)\n * - Mistral: 400/413/429 status code (no body)\n * - OpenRouter (all backends): \"maximum context length is X tokens\"\n * - llama.cpp: \"exceeds the available context size\"\n * - LM Studio: \"greater than the context length\"\n *\n * **Unreliable detection:**\n * - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),\n * sometimes returns rate limit errors. Pass contextWindow param to detect silent overflow.\n * - Ollama: Silently truncates input without error. Cannot be detected via this function.\n * The response will have usage.input < expected, but we don't know the expected value.\n *\n * ## Custom Providers\n *\n * If you've added custom models via settings.json, this function may not detect\n * overflow errors from those providers. To add support:\n *\n * 1. Send a request that exceeds the model's context window\n * 2. Check the errorMessage in the response\n * 3. Create a regex pattern that matches the error\n * 4. The pattern should be added to OVERFLOW_PATTERNS in this file, or\n * check the errorMessage yourself before calling this function\n *\n * @param message - The assistant message to check\n * @param contextWindow - Optional context window size for detecting silent overflow (z.ai)\n * @returns true if the message indicates a context overflow\n */\nexport function isContextOverflow(message: AssistantMessage, contextWindow?: number): boolean {\n\t// Case 1: Check error message patterns\n\tif (message.stopReason === \"error\" && message.errorMessage) {\n\t\t// Check known patterns\n\t\tif (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!))) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Cerebras and Mistral return 400/413/429 with no body - check for status code pattern\n\t\t// 429 can indicate token-based rate limiting which correlates with context overflow\n\t\tif (/^4(00|13|29)\\s*(status code)?\\s*\\(no body\\)/i.test(message.errorMessage)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// Case 2: Silent overflow (z.ai style) - successful but usage exceeds context\n\tif (contextWindow && message.stopReason === \"stop\") {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens > contextWindow) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Get the overflow patterns for testing purposes.\n */\nexport function getOverflowPatterns(): RegExp[] {\n\treturn [...OVERFLOW_PATTERNS];\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mariozechner/pi-ai",
3
- "version": "0.24.5",
3
+ "version": "0.25.0",
4
4
  "description": "Unified LLM API with automatic model discovery and provider configuration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",