@earendil-works/pi-ai 1.3.0 → 1.4.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 (141) hide show
  1. package/README.md +78 -3
  2. package/dist/api/anthropic-messages.d.ts.map +1 -1
  3. package/dist/api/anthropic-messages.js +7 -10
  4. package/dist/api/anthropic-messages.js.map +1 -1
  5. package/dist/api/azure-openai-responses.d.ts.map +1 -1
  6. package/dist/api/azure-openai-responses.js +8 -4
  7. package/dist/api/azure-openai-responses.js.map +1 -1
  8. package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  9. package/dist/api/bedrock-converse-stream.js +12 -0
  10. package/dist/api/bedrock-converse-stream.js.map +1 -1
  11. package/dist/api/classifier-shared.d.ts.map +1 -1
  12. package/dist/api/classifier-shared.js +4 -3
  13. package/dist/api/classifier-shared.js.map +1 -1
  14. package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
  15. package/dist/api/cloudflare-workers-ai-system-one.js +8 -0
  16. package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -1
  17. package/dist/api/google-generative-ai.js +4 -4
  18. package/dist/api/google-generative-ai.js.map +1 -1
  19. package/dist/api/google-shared.js +1 -1
  20. package/dist/api/google-shared.js.map +1 -1
  21. package/dist/api/google-vertex.d.ts.map +1 -1
  22. package/dist/api/google-vertex.js +9 -9
  23. package/dist/api/google-vertex.js.map +1 -1
  24. package/dist/api/lazy.d.ts +7 -2
  25. package/dist/api/lazy.d.ts.map +1 -1
  26. package/dist/api/lazy.js +24 -5
  27. package/dist/api/lazy.js.map +1 -1
  28. package/dist/api/llama-cpp-classify.d.ts.map +1 -1
  29. package/dist/api/llama-cpp-classify.js +2 -1
  30. package/dist/api/llama-cpp-classify.js.map +1 -1
  31. package/dist/api/mistral-conversations.js +2 -2
  32. package/dist/api/mistral-conversations.js.map +1 -1
  33. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  34. package/dist/api/openai-codex-responses.js +17 -11
  35. package/dist/api/openai-codex-responses.js.map +1 -1
  36. package/dist/api/openai-completions.d.ts.map +1 -1
  37. package/dist/api/openai-completions.js +14 -8
  38. package/dist/api/openai-completions.js.map +1 -1
  39. package/dist/api/openai-responses-shared.d.ts +16 -0
  40. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  41. package/dist/api/openai-responses-shared.js +61 -9
  42. package/dist/api/openai-responses-shared.js.map +1 -1
  43. package/dist/api/openai-responses.d.ts.map +1 -1
  44. package/dist/api/openai-responses.js +17 -9
  45. package/dist/api/openai-responses.js.map +1 -1
  46. package/dist/api/openrouter-images.d.ts.map +1 -1
  47. package/dist/api/openrouter-images.js +4 -3
  48. package/dist/api/openrouter-images.js.map +1 -1
  49. package/dist/api/pi-messages.d.ts.map +1 -1
  50. package/dist/api/pi-messages.js +3 -3
  51. package/dist/api/pi-messages.js.map +1 -1
  52. package/dist/api/simple-options.d.ts.map +1 -1
  53. package/dist/api/simple-options.js +2 -1
  54. package/dist/api/simple-options.js.map +1 -1
  55. package/dist/api/system-one-shared.d.ts +29 -2
  56. package/dist/api/system-one-shared.d.ts.map +1 -1
  57. package/dist/api/system-one-shared.js +55 -3
  58. package/dist/api/system-one-shared.js.map +1 -1
  59. package/dist/api/typesafe-system-one.d.ts.map +1 -1
  60. package/dist/api/typesafe-system-one.js +10 -0
  61. package/dist/api/typesafe-system-one.js.map +1 -1
  62. package/dist/index.d.ts +2 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +2 -0
  65. package/dist/index.js.map +1 -1
  66. package/dist/models.d.ts +4 -0
  67. package/dist/models.d.ts.map +1 -1
  68. package/dist/models.js +19 -6
  69. package/dist/models.js.map +1 -1
  70. package/dist/providers/compat-schema.d.ts +168 -0
  71. package/dist/providers/compat-schema.d.ts.map +1 -1
  72. package/dist/providers/compat-schema.js +15 -2
  73. package/dist/providers/compat-schema.js.map +1 -1
  74. package/dist/providers/data/.manifest.json +1 -1
  75. package/dist/providers/data/amazon-bedrock.json +1 -1
  76. package/dist/providers/data/azure.json +1 -1
  77. package/dist/providers/data/baseten.json +1 -1
  78. package/dist/providers/data/cerebras.json +1 -1
  79. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  80. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  81. package/dist/providers/data/fireworks.json +1 -1
  82. package/dist/providers/data/github-copilot.json +1 -1
  83. package/dist/providers/data/google.json +1 -1
  84. package/dist/providers/data/groq.json +1 -1
  85. package/dist/providers/data/huggingface.json +1 -1
  86. package/dist/providers/data/minimax-cn.json +1 -1
  87. package/dist/providers/data/minimax.json +1 -1
  88. package/dist/providers/data/mistral.json +1 -1
  89. package/dist/providers/data/moonshotai-cn.json +1 -1
  90. package/dist/providers/data/nvidia.json +1 -1
  91. package/dist/providers/data/openai-codex.json +1 -1
  92. package/dist/providers/data/openai.json +1 -1
  93. package/dist/providers/data/opencode-go.json +1 -1
  94. package/dist/providers/data/opencode.json +1 -1
  95. package/dist/providers/data/openrouter.json +1 -1
  96. package/dist/providers/data/qwen-token-plan-cn.json +1 -1
  97. package/dist/providers/data/qwen-token-plan.json +1 -1
  98. package/dist/providers/data/radius.json +1 -1
  99. package/dist/providers/data/together.json +1 -1
  100. package/dist/providers/data/typesafe.json +1 -1
  101. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  102. package/dist/providers/data/xai.json +1 -1
  103. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  104. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  105. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  106. package/dist/providers/data/xiaomi.json +1 -1
  107. package/dist/providers/data/zai-coding-cn.json +1 -1
  108. package/dist/providers/model-schema.d.ts.map +1 -1
  109. package/dist/providers/model-schema.js +4 -2
  110. package/dist/providers/model-schema.js.map +1 -1
  111. package/dist/telemetry.d.ts +288 -0
  112. package/dist/telemetry.d.ts.map +1 -0
  113. package/dist/telemetry.js +191 -0
  114. package/dist/telemetry.js.map +1 -0
  115. package/dist/types.d.ts +13 -5
  116. package/dist/types.d.ts.map +1 -1
  117. package/dist/types.js.map +1 -1
  118. package/dist/utils/identity.d.ts +65 -0
  119. package/dist/utils/identity.d.ts.map +1 -0
  120. package/dist/utils/identity.js +141 -0
  121. package/dist/utils/identity.js.map +1 -0
  122. package/dist/utils/overflow.d.ts +1 -0
  123. package/dist/utils/overflow.d.ts.map +1 -1
  124. package/dist/utils/overflow.js +6 -1
  125. package/dist/utils/overflow.js.map +1 -1
  126. package/dist/utils/retry.d.ts.map +1 -1
  127. package/dist/utils/retry.js +16 -7
  128. package/dist/utils/retry.js.map +1 -1
  129. package/dist/utils/transcript.d.ts +22 -8
  130. package/dist/utils/transcript.d.ts.map +1 -1
  131. package/dist/utils/transcript.js +29 -18
  132. package/dist/utils/transcript.js.map +1 -1
  133. package/dist/utils/version.d.ts +2 -0
  134. package/dist/utils/version.d.ts.map +1 -0
  135. package/dist/utils/version.js +3 -0
  136. package/dist/utils/version.js.map +1 -0
  137. package/package.json +2 -2
  138. package/dist/utils/pi-user-agent.d.ts +0 -2
  139. package/dist/utils/pi-user-agent.d.ts.map +0 -1
  140. package/dist/utils/pi-user-agent.js +0 -12
  141. package/dist/utils/pi-user-agent.js.map +0 -1
@@ -0,0 +1,141 @@
1
+ import { PI_AI_VERSION } from "./version.js";
2
+ /** pi's attribution, used by pi-agent-core, pi-durable, and the coding agent. */
3
+ export const PI_ATTRIBUTION = { name: "pi", url: "https://pi.dev", categories: ["cli-agent"] };
4
+ const NVIDIA_NIM_HOST = "integrate.api.nvidia.com";
5
+ const OPENCODE_HOST = "opencode.ai";
6
+ /** Merge identities field by field; later sources win and undefined fields are skipped. */
7
+ export function mergeClientIdentity(...sources) {
8
+ let merged;
9
+ for (const source of sources) {
10
+ if (!source)
11
+ continue;
12
+ merged = { ...merged };
13
+ if (source.userAgent !== undefined)
14
+ merged.userAgent = source.userAgent;
15
+ if (source.attribution !== undefined)
16
+ merged.attribution = source.attribution;
17
+ if (source.version !== undefined)
18
+ merged.version = source.version;
19
+ }
20
+ return merged;
21
+ }
22
+ /** The version in the detailed `User-Agent`. */
23
+ export function getClientVersion(identity) {
24
+ return identity?.version ?? PI_AI_VERSION;
25
+ }
26
+ function runtimeDescription() {
27
+ if (typeof process === "undefined")
28
+ return undefined;
29
+ const versions = process.versions;
30
+ if (versions?.bun)
31
+ return `bun/${versions.bun}`;
32
+ if (versions?.node)
33
+ return `node/${process.version}`;
34
+ return undefined;
35
+ }
36
+ /** The `User-Agent` value of an identity. */
37
+ export function formatUserAgent(identity) {
38
+ const mode = identity?.userAgent ?? "detailed";
39
+ if (typeof mode === "object")
40
+ return mode.custom;
41
+ if (mode === "short")
42
+ return "pi";
43
+ const product = `pi/${getClientVersion(identity)}`;
44
+ const runtime = runtimeDescription();
45
+ return runtime ? `${product} (${process.platform}; ${runtime}; ${process.arch})` : `${product} (browser)`;
46
+ }
47
+ function matchesHost(baseUrl, expectedHost) {
48
+ try {
49
+ return new URL(baseUrl).hostname === expectedHost;
50
+ }
51
+ catch {
52
+ return false;
53
+ }
54
+ }
55
+ function isOpenRouter(target) {
56
+ return target.provider === "openrouter" || target.baseUrl.includes("openrouter.ai");
57
+ }
58
+ function isNvidiaNim(target) {
59
+ return target.provider === "nvidia" || matchesHost(target.baseUrl, NVIDIA_NIM_HOST);
60
+ }
61
+ function isOpenCode(target) {
62
+ return (target.provider === "opencode" || target.provider === "opencode-go" || matchesHost(target.baseUrl, OPENCODE_HOST));
63
+ }
64
+ /** Provider attribution headers of an identity for a request target. Empty when attribution is off. */
65
+ export function getAttributionHeaders(target, identity) {
66
+ const attribution = identity?.attribution;
67
+ if (!attribution)
68
+ return {};
69
+ const { name, url, categories } = attribution;
70
+ if (isOpenRouter(target)) {
71
+ return {
72
+ "HTTP-Referer": url,
73
+ "X-OpenRouter-Title": name,
74
+ ...(categories === undefined || categories.length === 0
75
+ ? {}
76
+ : { "X-OpenRouter-Categories": categories.join(",") }),
77
+ };
78
+ }
79
+ // NVIDIA expects a capitalized origin, such as `Pi`.
80
+ if (isNvidiaNim(target))
81
+ return { "X-BILLING-INVOKE-ORIGIN": name.charAt(0).toUpperCase() + name.slice(1) };
82
+ if (isOpenCode(target))
83
+ return { "x-opencode-client": name };
84
+ return {};
85
+ }
86
+ /**
87
+ * OpenCode's per-conversation routing header for requests to OpenCode, including custom providers pointing at it.
88
+ * Undefined without a session or for other targets.
89
+ */
90
+ export function getOpenCodeSessionHeaders(target, sessionId) {
91
+ return sessionId && isOpenCode(target) ? { "x-opencode-session": sessionId } : undefined;
92
+ }
93
+ /**
94
+ * Default headers an identity adds to a request: `User-Agent` and provider attribution. APIs apply them below model,
95
+ * provider, and request headers, so configured headers override them and `null` request headers remove them.
96
+ */
97
+ export function getIdentityHeaders(target, identity) {
98
+ return { "User-Agent": formatUserAgent(identity), ...getAttributionHeaders(target, identity) };
99
+ }
100
+ function isMutableModels(models) {
101
+ return "setProvider" in models && "deleteProvider" in models && "clearProviders" in models;
102
+ }
103
+ export function withClientIdentity(models, identity) {
104
+ const withIdentity = (options) => ({ ...options, identity: mergeClientIdentity(identity, options?.identity) });
105
+ const wrapped = {
106
+ getProviders: () => models.getProviders(),
107
+ getProvider: (id) => models.getProvider(id),
108
+ getModels: (provider) => models.getModels(provider),
109
+ getModel: (provider, id) => models.getModel(provider, id),
110
+ getModelsOfType: (type, provider) => models.getModelsOfType(type, provider),
111
+ getModelOfType: (type, provider, id) => models.getModelOfType(type, provider, id),
112
+ getAllModels: (provider) => models.getAllModels(provider),
113
+ refresh: (options) => models.refresh(options),
114
+ checkAuth: (providerId, options) => models.checkAuth(providerId, options),
115
+ getAvailable: (providerId, options) => models.getAvailable(providerId, options),
116
+ getAvailableOfType: (type, providerId, options) => models.getAvailableOfType(type, providerId, options),
117
+ getAllAvailable: (providerId, options) => models.getAllAvailable(providerId, options),
118
+ getAuth: models.getAuth.bind(models),
119
+ login: (providerId, type, interaction, options) => models.login(providerId, type, interaction, options),
120
+ logout: (providerId, options) => models.logout(providerId, options),
121
+ stream: (model, context, options) => models.stream(model, context, withIdentity(options)),
122
+ complete: (model, context, options) => models.complete(model, context, withIdentity(options)),
123
+ streamSimple: (model, context, options) => models.streamSimple(model, context, withIdentity(options)),
124
+ completeSimple: (model, context, options) => models.completeSimple(model, context, withIdentity(options)),
125
+ streamDeferred: (model, handle, options) => models.streamDeferred(model, handle, withIdentity(options)),
126
+ fetchDeferred: (model, handle, options) => models.fetchDeferred(model, handle, withIdentity(options)),
127
+ cancelDeferred: (model, handle, options) => models.cancelDeferred(model, handle, withIdentity(options)),
128
+ generateImages: (model, context, options) => models.generateImages(model, context, withIdentity(options)),
129
+ classify: (model, context, options) => models.classify(model, context, withIdentity(options)),
130
+ };
131
+ if (!isMutableModels(models))
132
+ return wrapped;
133
+ const mutable = {
134
+ ...wrapped,
135
+ setProvider: (provider) => models.setProvider(provider),
136
+ deleteProvider: (id) => models.deleteProvider(id),
137
+ clearProviders: () => models.clearProviders(),
138
+ };
139
+ return mutable;
140
+ }
141
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/utils/identity.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Models, MutableModels } from \"../models.ts\";\nimport { PI_AI_VERSION } from \"./version.ts\";\n\n/**\n * The `User-Agent` sent to providers:\n * - `\"detailed\"`: `pi/<version> (<platform>; <runtime>/<runtime version>; <arch>)`, or `pi/<version> (browser)`\n * outside Node.js and Bun.\n * - `\"short\"`: `pi`.\n * - `{ custom }`: the given value.\n */\nexport type UserAgentMode = \"short\" | \"detailed\" | { custom: string };\n\n/** Who requests are attributed to. `getAttributionHeaders()` maps the fields to each provider's headers. */\nexport interface ClientAttribution {\n\t/** App name: OpenRouter app title, NVIDIA invoke origin (first letter upper-cased), OpenCode client. */\n\tname: string;\n\t/** App URL: OpenRouter `HTTP-Referer`, the app's identity in OpenRouter's rankings, grouped by origin. */\n\turl: string;\n\t/** App categories, e.g. `[\"cli-agent\"]`: OpenRouter `X-OpenRouter-Categories`, comma-separated. */\n\tcategories?: string[];\n}\n\n/** pi's attribution, used by pi-agent-core, pi-durable, and the coding agent. */\nexport const PI_ATTRIBUTION: ClientAttribution = { name: \"pi\", url: \"https://pi.dev\", categories: [\"cli-agent\"] };\n\n/**\n * How pi-ai identifies the calling app to providers. Every field is optional; absent fields use pi-ai's defaults:\n * a detailed `User-Agent` with pi-ai's version and no attribution headers. Plain JSON, so it can be persisted.\n */\nexport interface ClientIdentity {\n\t/** Default: `\"detailed\"`. */\n\tuserAgent?: UserAgentMode;\n\t/** Provider attribution headers. Absent or `null`: none; `null` also turns off attribution set by a merged identity. */\n\tattribution?: ClientAttribution | null;\n\t/** Version in the detailed `User-Agent`. Default: the pi-ai version. */\n\tversion?: string;\n}\n\n/** The provider and endpoint a request goes to. */\nexport interface ClientIdentityTarget {\n\treadonly provider: string;\n\treadonly baseUrl: string;\n}\n\nconst NVIDIA_NIM_HOST = \"integrate.api.nvidia.com\";\nconst OPENCODE_HOST = \"opencode.ai\";\n\n/** Merge identities field by field; later sources win and undefined fields are skipped. */\nexport function mergeClientIdentity(...sources: (ClientIdentity | undefined)[]): ClientIdentity | undefined {\n\tlet merged: ClientIdentity | undefined;\n\tfor (const source of sources) {\n\t\tif (!source) continue;\n\t\tmerged = { ...merged };\n\t\tif (source.userAgent !== undefined) merged.userAgent = source.userAgent;\n\t\tif (source.attribution !== undefined) merged.attribution = source.attribution;\n\t\tif (source.version !== undefined) merged.version = source.version;\n\t}\n\treturn merged;\n}\n\n/** The version in the detailed `User-Agent`. */\nexport function getClientVersion(identity?: ClientIdentity): string {\n\treturn identity?.version ?? PI_AI_VERSION;\n}\n\nfunction runtimeDescription(): string | undefined {\n\tif (typeof process === \"undefined\") return undefined;\n\tconst versions = process.versions;\n\tif (versions?.bun) return `bun/${versions.bun}`;\n\tif (versions?.node) return `node/${process.version}`;\n\treturn undefined;\n}\n\n/** The `User-Agent` value of an identity. */\nexport function formatUserAgent(identity?: ClientIdentity): string {\n\tconst mode = identity?.userAgent ?? \"detailed\";\n\tif (typeof mode === \"object\") return mode.custom;\n\tif (mode === \"short\") return \"pi\";\n\tconst product = `pi/${getClientVersion(identity)}`;\n\tconst runtime = runtimeDescription();\n\treturn runtime ? `${product} (${process.platform}; ${runtime}; ${process.arch})` : `${product} (browser)`;\n}\n\nfunction matchesHost(baseUrl: string, expectedHost: string): boolean {\n\ttry {\n\t\treturn new URL(baseUrl).hostname === expectedHost;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isOpenRouter(target: ClientIdentityTarget): boolean {\n\treturn target.provider === \"openrouter\" || target.baseUrl.includes(\"openrouter.ai\");\n}\n\nfunction isNvidiaNim(target: ClientIdentityTarget): boolean {\n\treturn target.provider === \"nvidia\" || matchesHost(target.baseUrl, NVIDIA_NIM_HOST);\n}\n\nfunction isOpenCode(target: ClientIdentityTarget): boolean {\n\treturn (\n\t\ttarget.provider === \"opencode\" || target.provider === \"opencode-go\" || matchesHost(target.baseUrl, OPENCODE_HOST)\n\t);\n}\n\n/** Provider attribution headers of an identity for a request target. Empty when attribution is off. */\nexport function getAttributionHeaders(target: ClientIdentityTarget, identity?: ClientIdentity): Record<string, string> {\n\tconst attribution = identity?.attribution;\n\tif (!attribution) return {};\n\tconst { name, url, categories } = attribution;\n\tif (isOpenRouter(target)) {\n\t\treturn {\n\t\t\t\"HTTP-Referer\": url,\n\t\t\t\"X-OpenRouter-Title\": name,\n\t\t\t...(categories === undefined || categories.length === 0\n\t\t\t\t? {}\n\t\t\t\t: { \"X-OpenRouter-Categories\": categories.join(\",\") }),\n\t\t};\n\t}\n\t// NVIDIA expects a capitalized origin, such as `Pi`.\n\tif (isNvidiaNim(target)) return { \"X-BILLING-INVOKE-ORIGIN\": name.charAt(0).toUpperCase() + name.slice(1) };\n\tif (isOpenCode(target)) return { \"x-opencode-client\": name };\n\treturn {};\n}\n\n/**\n * OpenCode's per-conversation routing header for requests to OpenCode, including custom providers pointing at it.\n * Undefined without a session or for other targets.\n */\nexport function getOpenCodeSessionHeaders(\n\ttarget: ClientIdentityTarget,\n\tsessionId: string | undefined,\n): Record<string, string> | undefined {\n\treturn sessionId && isOpenCode(target) ? { \"x-opencode-session\": sessionId } : undefined;\n}\n\n/**\n * Default headers an identity adds to a request: `User-Agent` and provider attribution. APIs apply them below model,\n * provider, and request headers, so configured headers override them and `null` request headers remove them.\n */\nexport function getIdentityHeaders(target: ClientIdentityTarget, identity?: ClientIdentity): Record<string, string> {\n\treturn { \"User-Agent\": formatUserAgent(identity), ...getAttributionHeaders(target, identity) };\n}\n\nfunction isMutableModels(models: Models): models is MutableModels {\n\treturn \"setProvider\" in models && \"deleteProvider\" in models && \"clearProviders\" in models;\n}\n\n/**\n * `models` with `identity` as the default identity of its requests: a request's `identity` overrides it field by\n * field, and it overrides the identity `models` was created with. Everything else is delegated unchanged; a\n * `MutableModels` stays mutable.\n */\nexport function withClientIdentity(models: MutableModels, identity: ClientIdentity): MutableModels;\nexport function withClientIdentity(models: Models, identity: ClientIdentity): Models;\nexport function withClientIdentity(models: Models, identity: ClientIdentity): Models {\n\tconst withIdentity = <T extends { identity?: ClientIdentity }>(options: T | undefined): T =>\n\t\t({ ...options, identity: mergeClientIdentity(identity, options?.identity) }) as T;\n\tconst wrapped: Models = {\n\t\tgetProviders: () => models.getProviders(),\n\t\tgetProvider: (id) => models.getProvider(id),\n\t\tgetModels: (provider) => models.getModels(provider),\n\t\tgetModel: (provider, id) => models.getModel(provider, id),\n\t\tgetModelsOfType: (type, provider) => models.getModelsOfType(type, provider),\n\t\tgetModelOfType: (type, provider, id) => models.getModelOfType(type, provider, id),\n\t\tgetAllModels: (provider) => models.getAllModels(provider),\n\t\trefresh: (options) => models.refresh(options),\n\t\tcheckAuth: (providerId, options) => models.checkAuth(providerId, options),\n\t\tgetAvailable: (providerId, options) => models.getAvailable(providerId, options),\n\t\tgetAvailableOfType: (type, providerId, options) => models.getAvailableOfType(type, providerId, options),\n\t\tgetAllAvailable: (providerId, options) => models.getAllAvailable(providerId, options),\n\t\tgetAuth: models.getAuth.bind(models),\n\t\tlogin: (providerId, type, interaction, options) => models.login(providerId, type, interaction, options),\n\t\tlogout: (providerId, options) => models.logout(providerId, options),\n\t\tstream: (model, context, options) => models.stream(model, context, withIdentity(options)),\n\t\tcomplete: (model, context, options) => models.complete(model, context, withIdentity(options)),\n\t\tstreamSimple: (model, context, options) => models.streamSimple(model, context, withIdentity(options)),\n\t\tcompleteSimple: (model, context, options) => models.completeSimple(model, context, withIdentity(options)),\n\t\tstreamDeferred: (model, handle, options) => models.streamDeferred(model, handle, withIdentity(options)),\n\t\tfetchDeferred: (model, handle, options) => models.fetchDeferred(model, handle, withIdentity(options)),\n\t\tcancelDeferred: (model, handle, options) => models.cancelDeferred(model, handle, withIdentity(options)),\n\t\tgenerateImages: (model, context, options) => models.generateImages(model, context, withIdentity(options)),\n\t\tclassify: (model, context, options) => models.classify(model, context, withIdentity(options)),\n\t};\n\tif (!isMutableModels(models)) return wrapped;\n\tconst mutable: MutableModels = {\n\t\t...wrapped,\n\t\tsetProvider: (provider) => models.setProvider(provider),\n\t\tdeleteProvider: (id) => models.deleteProvider(id),\n\t\tclearProviders: () => models.clearProviders(),\n\t};\n\treturn mutable;\n}\n"]}
@@ -18,6 +18,7 @@ import type { AssistantMessage } from "../types.ts";
18
18
  * - Google Gemini: "input token count exceeds the maximum"
19
19
  * - xAI (Grok): "maximum prompt length is X but request contains Y"
20
20
  * - Groq: "reduce the length of the messages"
21
+ * - GitHub Copilot: HTTP 413 "failed to parse request" (request byte-size overflow)
21
22
  * - Cerebras: 400/413 status code (no body)
22
23
  * - Mistral: "Prompt contains X tokens ... too large for model with Y maximum context length"
23
24
  * - OpenRouter (most backends): "maximum context length is X tokens"
@@ -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;AAiFpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAkC5F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAEhG;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C"}
1
+ {"version":3,"file":"overflow.d.ts","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAqFpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAkC5F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAEhG;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C"}
@@ -2,7 +2,7 @@
2
2
  * Regex patterns to detect context overflow errors from different providers.
3
3
  *
4
4
  * These patterns match error messages returned when the input exceeds
5
- * the model's context window.
5
+ * the model's context window or the provider's request byte-size limit.
6
6
  *
7
7
  * Provider-specific patterns (with example error messages):
8
8
  *
@@ -20,6 +20,7 @@
20
20
  * - llama.cpp: "the request exceeds the available context size, try increasing it"
21
21
  * - LM Studio: "tokens to keep from the initial prompt is greater than the context length"
22
22
  * - GitHub Copilot: "prompt token count of X exceeds the limit of Y"
23
+ * - GitHub Copilot: 'github-copilot API error (413): {"message":"failed to parse request","code":""}'
23
24
  * - MiniMax: "invalid params, context window exceeds limit"
24
25
  * - Kimi For Coding: "Your request exceeded model token limit: X (requested: Y)"
25
26
  * - DS4: "Prompt has X tokens, but the configured context size is Y tokens"
@@ -58,6 +59,9 @@ const OVERFLOW_PATTERNS = [
58
59
  /context[_ ]length[_ ]exceeded/i, // Generic fallback
59
60
  /too many tokens/i, // Generic fallback
60
61
  /token limit exceeded/i, // Generic fallback
62
+ // HTTP 413 means request-size overflow regardless of provider or body text.
63
+ // Match raw SDK errors and formatProviderError's prefix, not a status mentioned in the body.
64
+ /^(?:[\w -]+ API error \(413\):|413(?:\s|:|$))/i,
61
65
  ];
62
66
  const CEREBRAS_BODYLESS_OVERFLOW_PATTERN = /^4(?:00|13)\s*(?:status code)?\s*\(no body\)/i;
63
67
  /**
@@ -93,6 +97,7 @@ const NON_OVERFLOW_PATTERNS = [
93
97
  * - Google Gemini: "input token count exceeds the maximum"
94
98
  * - xAI (Grok): "maximum prompt length is X but request contains Y"
95
99
  * - Groq: "reduce the length of the messages"
100
+ * - GitHub Copilot: HTTP 413 "failed to parse request" (request byte-size overflow)
96
101
  * - Cerebras: 400/413 status code (no body)
97
102
  * - Mistral: "Prompt contains X tokens ... too large for model with Y maximum context length"
98
103
  * - OpenRouter (most backends): "maximum context length is X tokens"
@@ -1 +1 @@
1
- {"version":3,"file":"overflow.js","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"../types.ts\";\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 * - Anthropic: \"413 {\\\"error\\\":{\\\"type\\\":\\\"request_too_large\\\",\\\"message\\\":\\\"Request exceeds the maximum size\\\"}}\"\n * - OpenAI: \"Your input exceeds the context window of this model\"\n * - OpenAI/LiteLLM: \"Requested token count exceeds the model's maximum context length of 131072 tokens\"\n * - OpenAI-compatible: \"Input length (265330) exceeds model's maximum context length (262144).\"\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 * - OpenRouter/Poolside: \"Input length X exceeds the maximum allowed input length of Y tokens.\"\n * - Together AI: \"The input (X tokens) is longer than the model's context length (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 * - MiniMax: \"invalid params, context window exceeds limit\"\n * - Kimi For Coding: \"Your request exceeded model token limit: X (requested: Y)\"\n * - DS4: \"Prompt has X tokens, but the configured context size is Y tokens\"\n * - Cerebras: \"400/413 status code (no body)\"\n * - Mistral: \"Prompt contains X tokens ... too large for model with Y maximum context length\"\n * - z.ai: `{\"code\":\"1261\",\"message\":\"Prompt too long\"}`, `{\"code\":\"1261\",\"message\":\"Prompt exceeds max length\"}` (CN endpoint), or silent overflow via usage.input > contextWindow\n * - Xiaomi MiMo: Truncates input to fill contextWindow exactly, then returns finish_reason \"length\"\n * with output=0 (no room left to generate). Detected via stopReason \"length\" + zero output +\n * input filling the context window.\n * - DashScope/Qwen: \"Range of input length should be [1, X]\" (HTTP 400 invalid_parameter_error)\n * - Ollama: Some deployments truncate silently, others return errors like \"prompt too long; exceeded max context length by X tokens\"\n */\nconst OVERFLOW_PATTERNS = [\n\t/prompt (?:is )?too long/i, // Anthropic and z.ai token overflow\n\t/prompt exceeds max length/i, // z.ai CN endpoint token overflow\n\t/request_too_large/i, // Anthropic request byte-size overflow (HTTP 413)\n\t/input is too long for requested model/i, // Amazon Bedrock\n\t/exceeds the context window/i, // OpenAI (Completions & Responses API)\n\t/exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\\d,]+ tokens?|\\s*\\([\\d,]+\\))/i, // OpenAI-compatible proxies (LiteLLM)\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 (most backends)\n\t/exceeds (?:the )?maximum allowed input length of [\\d,]+ tokens?/i, // OpenRouter/Poolside\n\t/input \\(\\d+ tokens\\) is longer than the model'?s context length \\(\\d+ tokens\\)/i, // Together AI\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 window exceeds limit/i, // MiniMax\n\t/exceeded model token limit/i, // Kimi For Coding\n\t/too large for model with \\d+ maximum context length/i, // Mistral\n\t/prompt has [\\d,]+ tokens?, but the configured context size is [\\d,]+ tokens?/i, // DS4 server\n\t/model_context_window_exceeded/i, // z.ai non-standard finish_reason surfaced as error text\n\t/prompt too long; exceeded (?:max )?context length/i, // Ollama explicit overflow error\n\t/range of input length should be/i, // DashScope / Qwen Token Plan\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\nconst CEREBRAS_BODYLESS_OVERFLOW_PATTERN = /^4(?:00|13)\\s*(?:status code)?\\s*\\(no body\\)/i;\n\n/**\n * Patterns that indicate non-overflow errors (e.g. rate limiting, server errors).\n * Error messages matching any of these are excluded from overflow detection\n * even if they also match an OVERFLOW_PATTERN.\n *\n * Example: Bedrock formats throttling errors as \"ThrottlingException: Too many tokens,\n * please wait before trying again.\" which would match the /too many tokens/i overflow\n * pattern without this exclusion.\n */\nconst NON_OVERFLOW_PATTERNS = [\n\t/^(Throttling error|Service unavailable):/i, // AWS Bedrock non-overflow errors (human-readable prefixes from formatBedrockError)\n\t/rate limit/i, // Generic rate limiting\n\t/too many requests/i, // Generic HTTP 429 style\n];\n\n/**\n * Check if an assistant message represents a context overflow error.\n *\n * This handles three 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 * 3. Length-stop overflow: Xiaomi MiMo can return \"length\" with zero output when\n * the input fills 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\" or \"request_too_large\"\n * - OpenAI (Completions & Responses): \"exceeds the context window\", \"exceeds the model's maximum context length of X tokens\", or \"exceeds model's maximum context length (X)\"\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: \"Prompt contains X tokens ... too large for model with Y maximum context length\"\n * - OpenRouter (most backends): \"maximum context length is X tokens\"\n * - OpenRouter/Poolside: \"Input length X exceeds the maximum allowed input length of Y tokens.\"\n * - Together AI: \"The input (X tokens) is longer than the model's context length (Y tokens).\"\n * - llama.cpp: \"exceeds the available context size\"\n * - LM Studio: \"greater than the context length\"\n * - Kimi For Coding: \"exceeded model token limit: X (requested: Y)\"\n * - DS4: \"Prompt has X tokens, but the configured context size is Y tokens\"\n * - DashScope/Qwen: \"Range of input length should be [1, X]\"\n * - z.ai: \"Prompt too long\"\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 * - Xiaomi MiMo: Truncates input to fit contextWindow then returns stopReason \"length\" with\n * output=0. Pass contextWindow param to detect via the \"filled context + zero output\" signal.\n * - Ollama: May truncate input silently for some setups, but may also return explicit\n * overflow errors that match the patterns above. Silent truncation still cannot be\n * detected here because we do not know the expected token count.\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// Skip messages matching known non-overflow patterns (e.g. throttling / rate-limit)\n\t\tconst isNonOverflow = NON_OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!));\n\t\tif (!isNonOverflow) {\n\t\t\tif (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!))) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (message.provider === \"cerebras\" && CEREBRAS_BODYLESS_OVERFLOW_PATTERN.test(message.errorMessage)) {\n\t\t\t\treturn true;\n\t\t\t}\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\t// Case 3: Length-stop overflow (Xiaomi MiMo style) - server truncates oversized input\n\t// to fit the context window, leaving no room for output. Returns stopReason \"length\"\n\t// with output=0 and input+cacheRead filling the context window.\n\tif (contextWindow && message.stopReason === \"length\" && message.usage.output === 0) {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens >= contextWindow * 0.99) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Check whether a length stop ended below the caller or model's intended output limit.\n * Such responses may be caused by context pressure or provider-side truncation, so callers\n * can make one bounded compact-and-retry attempt. `desiredMaxOutput` must be the original\n * limit before any context-based clamping.\n */\nexport function isRecoverableLength(message: AssistantMessage, desiredMaxOutput: number): boolean {\n\treturn message.stopReason === \"length\" && desiredMaxOutput > 0 && message.usage.output < desiredMaxOutput;\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":"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","sourcesContent":["import type { AssistantMessage } from \"../types.ts\";\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 or the provider's request byte-size limit.\n *\n * Provider-specific patterns (with example error messages):\n *\n * - Anthropic: \"prompt is too long: 213462 tokens > 200000 maximum\"\n * - Anthropic: \"413 {\\\"error\\\":{\\\"type\\\":\\\"request_too_large\\\",\\\"message\\\":\\\"Request exceeds the maximum size\\\"}}\"\n * - OpenAI: \"Your input exceeds the context window of this model\"\n * - OpenAI/LiteLLM: \"Requested token count exceeds the model's maximum context length of 131072 tokens\"\n * - OpenAI-compatible: \"Input length (265330) exceeds model's maximum context length (262144).\"\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 * - OpenRouter/Poolside: \"Input length X exceeds the maximum allowed input length of Y tokens.\"\n * - Together AI: \"The input (X tokens) is longer than the model's context length (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 * - GitHub Copilot: 'github-copilot API error (413): {\"message\":\"failed to parse request\",\"code\":\"\"}'\n * - MiniMax: \"invalid params, context window exceeds limit\"\n * - Kimi For Coding: \"Your request exceeded model token limit: X (requested: Y)\"\n * - DS4: \"Prompt has X tokens, but the configured context size is Y tokens\"\n * - Cerebras: \"400/413 status code (no body)\"\n * - Mistral: \"Prompt contains X tokens ... too large for model with Y maximum context length\"\n * - z.ai: `{\"code\":\"1261\",\"message\":\"Prompt too long\"}`, `{\"code\":\"1261\",\"message\":\"Prompt exceeds max length\"}` (CN endpoint), or silent overflow via usage.input > contextWindow\n * - Xiaomi MiMo: Truncates input to fill contextWindow exactly, then returns finish_reason \"length\"\n * with output=0 (no room left to generate). Detected via stopReason \"length\" + zero output +\n * input filling the context window.\n * - DashScope/Qwen: \"Range of input length should be [1, X]\" (HTTP 400 invalid_parameter_error)\n * - Ollama: Some deployments truncate silently, others return errors like \"prompt too long; exceeded max context length by X tokens\"\n */\nconst OVERFLOW_PATTERNS = [\n\t/prompt (?:is )?too long/i, // Anthropic and z.ai token overflow\n\t/prompt exceeds max length/i, // z.ai CN endpoint token overflow\n\t/request_too_large/i, // Anthropic request byte-size overflow (HTTP 413)\n\t/input is too long for requested model/i, // Amazon Bedrock\n\t/exceeds the context window/i, // OpenAI (Completions & Responses API)\n\t/exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\\d,]+ tokens?|\\s*\\([\\d,]+\\))/i, // OpenAI-compatible proxies (LiteLLM)\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 (most backends)\n\t/exceeds (?:the )?maximum allowed input length of [\\d,]+ tokens?/i, // OpenRouter/Poolside\n\t/input \\(\\d+ tokens\\) is longer than the model'?s context length \\(\\d+ tokens\\)/i, // Together AI\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 window exceeds limit/i, // MiniMax\n\t/exceeded model token limit/i, // Kimi For Coding\n\t/too large for model with \\d+ maximum context length/i, // Mistral\n\t/prompt has [\\d,]+ tokens?, but the configured context size is [\\d,]+ tokens?/i, // DS4 server\n\t/model_context_window_exceeded/i, // z.ai non-standard finish_reason surfaced as error text\n\t/prompt too long; exceeded (?:max )?context length/i, // Ollama explicit overflow error\n\t/range of input length should be/i, // DashScope / Qwen Token Plan\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\t// HTTP 413 means request-size overflow regardless of provider or body text.\n\t// Match raw SDK errors and formatProviderError's prefix, not a status mentioned in the body.\n\t/^(?:[\\w -]+ API error \\(413\\):|413(?:\\s|:|$))/i,\n];\n\nconst CEREBRAS_BODYLESS_OVERFLOW_PATTERN = /^4(?:00|13)\\s*(?:status code)?\\s*\\(no body\\)/i;\n\n/**\n * Patterns that indicate non-overflow errors (e.g. rate limiting, server errors).\n * Error messages matching any of these are excluded from overflow detection\n * even if they also match an OVERFLOW_PATTERN.\n *\n * Example: Bedrock formats throttling errors as \"ThrottlingException: Too many tokens,\n * please wait before trying again.\" which would match the /too many tokens/i overflow\n * pattern without this exclusion.\n */\nconst NON_OVERFLOW_PATTERNS = [\n\t/^(Throttling error|Service unavailable):/i, // AWS Bedrock non-overflow errors (human-readable prefixes from formatBedrockError)\n\t/rate limit/i, // Generic rate limiting\n\t/too many requests/i, // Generic HTTP 429 style\n];\n\n/**\n * Check if an assistant message represents a context overflow error.\n *\n * This handles three 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 * 3. Length-stop overflow: Xiaomi MiMo can return \"length\" with zero output when\n * the input fills 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\" or \"request_too_large\"\n * - OpenAI (Completions & Responses): \"exceeds the context window\", \"exceeds the model's maximum context length of X tokens\", or \"exceeds model's maximum context length (X)\"\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 * - GitHub Copilot: HTTP 413 \"failed to parse request\" (request byte-size overflow)\n * - Cerebras: 400/413 status code (no body)\n * - Mistral: \"Prompt contains X tokens ... too large for model with Y maximum context length\"\n * - OpenRouter (most backends): \"maximum context length is X tokens\"\n * - OpenRouter/Poolside: \"Input length X exceeds the maximum allowed input length of Y tokens.\"\n * - Together AI: \"The input (X tokens) is longer than the model's context length (Y tokens).\"\n * - llama.cpp: \"exceeds the available context size\"\n * - LM Studio: \"greater than the context length\"\n * - Kimi For Coding: \"exceeded model token limit: X (requested: Y)\"\n * - DS4: \"Prompt has X tokens, but the configured context size is Y tokens\"\n * - DashScope/Qwen: \"Range of input length should be [1, X]\"\n * - z.ai: \"Prompt too long\"\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 * - Xiaomi MiMo: Truncates input to fit contextWindow then returns stopReason \"length\" with\n * output=0. Pass contextWindow param to detect via the \"filled context + zero output\" signal.\n * - Ollama: May truncate input silently for some setups, but may also return explicit\n * overflow errors that match the patterns above. Silent truncation still cannot be\n * detected here because we do not know the expected token count.\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// Skip messages matching known non-overflow patterns (e.g. throttling / rate-limit)\n\t\tconst isNonOverflow = NON_OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!));\n\t\tif (!isNonOverflow) {\n\t\t\tif (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!))) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (message.provider === \"cerebras\" && CEREBRAS_BODYLESS_OVERFLOW_PATTERN.test(message.errorMessage)) {\n\t\t\t\treturn true;\n\t\t\t}\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\t// Case 3: Length-stop overflow (Xiaomi MiMo style) - server truncates oversized input\n\t// to fit the context window, leaving no room for output. Returns stopReason \"length\"\n\t// with output=0 and input+cacheRead filling the context window.\n\tif (contextWindow && message.stopReason === \"length\" && message.usage.output === 0) {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens >= contextWindow * 0.99) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Check whether a length stop ended below the caller or model's intended output limit.\n * Such responses may be caused by context pressure or provider-side truncation, so callers\n * can make one bounded compact-and-retry attempt. `desiredMaxOutput` must be the original\n * limit before any context-based clamping.\n */\nexport function isRecoverableLength(message: AssistantMessage, desiredMaxOutput: number): boolean {\n\treturn message.stopReason === \"length\" && desiredMaxOutput > 0 && message.usage.output < desiredMaxOutput;\n}\n\n/**\n * Get the overflow patterns for testing purposes.\n */\nexport function getOverflowPatterns(): RegExp[] {\n\treturn [...OVERFLOW_PATTERNS];\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiHpD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAEvD,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,GAAG,iBAAiB,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIlH;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC9B,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,CAClB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,mFAAmF;IACnF,mBAAmB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,mFAAmF;IACnF,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnG;AA0BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,EACxC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,SAAS,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,gBAAgB,CAAC,CA6C3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAK5E"}
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AA0HpD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAEvD,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,GAAG,iBAAiB,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIlH;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC9B,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,CAClB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,mFAAmF;IACnF,mBAAmB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,mFAAmF;IACnF,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnG;AA0BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,EACxC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,SAAS,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,gBAAgB,CAAC,CA6C3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAK5E"}
@@ -19,6 +19,9 @@ const NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN = buildProviderErrorPattern([
19
19
  // Sign in with ChatGPT: the subscription's shared usage limit, which resets
20
20
  // after hours rather than seconds.
21
21
  "subscription_sharing_usage_limit_exceeded",
22
+ // Sign in with ChatGPT on a ChatGPT Business plan: a usage limit set by the
23
+ // workspace admin. Only the admin can raise it.
24
+ "subscription_sharing_workplace_individual_usage_limit_exceeded",
22
25
  ]);
23
26
  const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
24
27
  // Generic provider load, HTTP status, and server-side transient failures.
@@ -29,13 +32,8 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
29
32
  "model is at capacity",
30
33
  "rate.?limit",
31
34
  "too many requests",
32
- "429",
33
- "500",
34
- "502",
35
- "503",
36
- "504",
37
- "520",
38
- "524",
35
+ // Status codes must not be part of a longer word or number, e.g. a request id or byte count (#11057).
36
+ "(?<![a-z0-9])(?:429|500|502|503|504|520|524)(?![a-z0-9])",
39
37
  // Also matches "The service is temporarily unavailable" (#10965).
40
38
  "service.?(?:is.?)?(?:temporarily.?)?unavailable",
41
39
  "server.?error",
@@ -48,6 +46,11 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
48
46
  // Gateway failure relaying the upstream response stream, sent as an OpenAI
49
47
  // Responses `response.failed` event without an error code (#10920).
50
48
  "unable to forward upstream",
49
+ // Other gateway upstream failures relayed as `response.failed` error codes (#10920).
50
+ "upstream.?request.?failed",
51
+ "upstream_stream_read_error",
52
+ "stream was interrupted",
53
+ "http/?2 stream failed",
51
54
  // Network, proxy, and fetch transport failures. This includes OpenAI Codex
52
55
  // raw-fetch failures such as "upstream connect", "connection refused", and
53
56
  // "reset before headers" (#733), plus OpenRouter connection drops (#3317).
@@ -57,6 +60,9 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
57
60
  "connection.?lost",
58
61
  "other side closed",
59
62
  "fetch failed",
63
+ // node-fetch system errors, e.g. the Google ADC OAuth token refresh used by
64
+ // google-vertex. The reason is often empty (#10996).
65
+ "request to \\S+ failed, reason:",
60
66
  "getaddrinfo",
61
67
  "ENOTFOUND",
62
68
  "EAI_AGAIN",
@@ -77,6 +83,9 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
77
83
  "stream ended before message_stop",
78
84
  "stream ended before a terminal response event",
79
85
  "http2 request did not get a response",
86
+ // Codex CLI mid-stream disconnect, relayed by proxies that wrap the Codex CLI,
87
+ // e.g. "stream disconnected before completion: stream closed before response.completed" (#11057).
88
+ "stream disconnected before completion",
80
89
  // Node ERR_HTTP2_STREAM_CANCEL: the HTTP/2 session died before the request was
81
90
  // sent, e.g. after the Bedrock SDK's 5-minute session timeout (#10379).
82
91
  "pending stream has been canceled",
@@ -1 +1 @@
1
- {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"../types.ts\";\n\nfunction buildProviderErrorPattern(patterns: readonly string[]): RegExp {\n\treturn new RegExp(patterns.join(\"|\"), \"i\");\n}\n\nconst NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN = buildProviderErrorPattern([\n\t// OpenCode Go/free-tier limits returned as 429 JSON error types by OpenCode's\n\t// Zen API. These are subscription/account limits, not transient throttles.\n\t\"GoUsageLimitError\",\n\t\"FreeUsageLimitError\",\n\n\t// OpenCode Go subscription-limit text asks users to enable available-balance\n\t// usage after rolling/weekly/monthly limits are reached.\n\t\"Monthly usage limit reached\",\n\t\"available balance\",\n\n\t// Generic quota/budget/billing exhaustion. `insufficient_quota` is OpenAI's\n\t// quota/billing error code; the other strings cover common gateway wording.\n\t\"insufficient_quota\",\n\t\"out of budget\",\n\t\"quota exceeded\",\n\t\"billing\",\n\n\t// Sign in with ChatGPT: the subscription's shared usage limit, which resets\n\t// after hours rather than seconds.\n\t\"subscription_sharing_usage_limit_exceeded\",\n]);\n\nconst RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([\n\t// Generic provider load, HTTP status, and server-side transient failures.\n\t\"overloaded\",\n\t\"server_busy\",\n\t\"servers are currently busy\",\n\t\"currently experiencing high demand\",\n\t\"model is at capacity\",\n\t\"rate.?limit\",\n\t\"too many requests\",\n\t\"429\",\n\t\"500\",\n\t\"502\",\n\t\"503\",\n\t\"504\",\n\t\"520\",\n\t\"524\",\n\t// Also matches \"The service is temporarily unavailable\" (#10965).\n\t\"service.?(?:is.?)?(?:temporarily.?)?unavailable\",\n\t\"server.?error\",\n\t\"internal.?error\",\n\n\t// Wrapper/provider text for transient upstream failures, including OpenRouter\n\t// \"Provider returned error\" responses (#2264) and gateway wording such as\n\t// \"The upstream provider returned an error\" (#10842).\n\t\"provider.?returned.?(?:an.?)?error\",\n\t\"exceeded request buffer limit while retrying upstream\",\n\t// Gateway failure relaying the upstream response stream, sent as an OpenAI\n\t// Responses `response.failed` event without an error code (#10920).\n\t\"unable to forward upstream\",\n\n\t// Network, proxy, and fetch transport failures. This includes OpenAI Codex\n\t// raw-fetch failures such as \"upstream connect\", \"connection refused\", and\n\t// \"reset before headers\" (#733), plus OpenRouter connection drops (#3317).\n\t\"network.?error\",\n\t\"connection.?error\",\n\t\"connection.?refused\",\n\t\"connection.?lost\",\n\t\"other side closed\",\n\t\"fetch failed\",\n\t\"getaddrinfo\",\n\t\"ENOTFOUND\",\n\t\"EAI_AGAIN\",\n\t\"upstream.?connect\",\n\t\"reset before headers\",\n\t\"socket hang up\",\n\t\"socket connection was closed\",\n\t\"timed? out\",\n\t\"timeout\",\n\t\"terminated\",\n\n\t// WebSocket transports can report close/error text instead of HTTP/fetch text.\n\t\"websocket.?closed\",\n\t\"websocket.?error\",\n\n\t// Premature stream endings from SDKs and transports. Anthropic can throw\n\t// \"stream ended without ...\" and \"Anthropic stream ended before message_stop\"\n\t// (#4433); Bedrock/Smithy can throw an HTTP/2 no-response error (#3594).\n\t\"ended without\",\n\t\"stream ended before message_stop\",\n\t\"stream ended before a terminal response event\",\n\t\"http2 request did not get a response\",\n\t// Node ERR_HTTP2_STREAM_CANCEL: the HTTP/2 session died before the request was\n\t// sent, e.g. after the Bedrock SDK's 5-minute session timeout (#10379).\n\t\"pending stream has been canceled\",\n\n\t// Provider-requested retry delay cap failures should flow through the outer\n\t// retry policy so callers can surface/abort the backoff (#1123).\n\t\"retry delay\",\n\n\t// Explicit retry guidance emitted mid-stream by OpenAI Responses and Bedrock\n\t// stream exceptions (#6019).\n\t\"you can retry your request\",\n\t\"try your request again\",\n\t\"please retry your request\",\n\n\t// gRPC based providers (e.g. NVIDIA NIM)\n\t\"ResourceExhausted\",\n\n\t// Sign in with ChatGPT: usage or user data temporarily unavailable. Usage\n\t// failures can arrive mid-stream without an HTTP 503 in the message.\n\t\"subscription_sharing_usage_unavailable\",\n\t\"subscription_sharing_user_unavailable\",\n]);\n\n/**\n * Retry policy: bounded attempts with exponential backoff (`baseDelayMs * 2^(attempt-1)`).\n * `maxAgentDelayMs` caps each computed delay and defaults to 60 seconds.\n * Matches `settings.retry` (`enabled`, `maxRetries`, `baseDelayMs`, `maxAgentDelayMs`) in coding-agent; kept\n * here so the classifier and the policy-driven retry loop live together and stay reusable\n * by the SDK and other callers.\n */\nexport interface RetryPolicy {\n\tenabled: boolean;\n\t/** Max retry attempts (0 = no retries). The initial call never counts as a retry. */\n\tmaxRetries: number;\n\t/** Base delay in ms. Per-attempt delay is `baseDelayMs * 2^(attempt-1)` before jitter. */\n\tbaseDelayMs: number;\n\t/** Optional cap for agent-level retry delays in ms. Defaults to 60 seconds. */\n\tmaxAgentDelayMs?: number;\n}\n\nexport const DEFAULT_MAX_AGENT_RETRY_DELAY_MS = 60_000;\n\nexport function retryDelayMs(policy: Pick<RetryPolicy, \"baseDelayMs\" | \"maxAgentDelayMs\">, attempt: number): number {\n\tconst delay = policy.baseDelayMs * 2 ** Math.max(0, attempt - 1);\n\tconst safeDelay = Number.isSafeInteger(delay) ? delay : Number.MAX_SAFE_INTEGER;\n\treturn Math.min(safeDelay, policy.maxAgentDelayMs ?? DEFAULT_MAX_AGENT_RETRY_DELAY_MS);\n}\n\n/** Optional callbacks emitted by {@link retryAssistantCall} around each retry. */\nexport interface RetryCallbacks {\n\t/** Emitted before the backoff sleep of each retry attempt (1-indexed). */\n\tonRetryScheduled?: (\n\t\tattempt: number,\n\t\tmaxAttempts: number,\n\t\tdelayMs: number,\n\t\terrorMessage: string,\n\t) => void | Promise<void>;\n\t/** Emitted after the backoff sleep, immediately before the retried call starts. */\n\tonRetryAttemptStart?: () => void | Promise<void>;\n\t/** Emitted once when the loop ends: success if a later call completed normally. */\n\tonRetryFinished?: (success: boolean, attempt: number, finalError?: string) => void | Promise<void>;\n}\n\nclass RetrySleepAbortError extends Error {\n\tconstructor() {\n\t\tsuper(\"Aborted\");\n\t}\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new RetrySleepAbortError());\n\t\t\treturn;\n\t\t}\n\t\tconst timeout = setTimeout(resolve, ms);\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\treject(new RetrySleepAbortError());\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\n/**\n * Run a single assistant-producing call with bounded retry on transient errors.\n *\n * Behavior:\n * - A successful response is returned immediately. Aborts are terminal and never\n * retried, but reported as unsuccessful if they happen after a retry was scheduled.\n * Aborts during the backoff sleep are normalized to an aborted `AssistantMessage`\n * too, so callers do not need to care when cancellation happened.\n * - A non-retryable error (per {@link isRetryableAssistantError}, including quota/\n * billing exhaustion) is returned immediately so deterministic errors fail fast.\n * - Otherwise retries up to `maxRetries` times with exponential backoff, emitting\n * `onRetryScheduled` before each sleep, `onRetryAttemptStart` after each sleep before\n * the retried call starts, and `onRetryFinished` once at the end (whether the loop\n * ends in success, exhausted retries, or an aborted backoff).\n *\n * When `policy` is undefined or disabled, the first response is returned unchanged\n * (equivalent to calling `produce()` directly).\n */\nexport async function retryAssistantCall(\n\tproduce: () => Promise<AssistantMessage>,\n\tpolicy: RetryPolicy | undefined,\n\tsignal: AbortSignal | undefined,\n\tcallbacks?: RetryCallbacks,\n): Promise<AssistantMessage> {\n\tconst maxAttempts = policy?.enabled ? policy.maxRetries : 0;\n\n\tlet attempt = 0;\n\tlet lastRetry: { attempt: number; errorMessage: string } | undefined;\n\tfor (;;) {\n\t\tconst response = await produce();\n\n\t\t// Abort: terminal but not successful. Never retry an aborted message.\n\t\tif (response.stopReason === \"aborted\") {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt);\n\t\t\treturn response;\n\t\t}\n\n\t\t// Success: non-error, non-abort responses return as-is.\n\t\tif (response.stopReason !== \"error\") {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(true, lastRetry.attempt);\n\t\t\treturn response;\n\t\t}\n\n\t\t// Non-retryable, or budget exhausted: return the final error message.\n\t\tif (attempt >= maxAttempts || !isRetryableAssistantError(response)) {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt, response.errorMessage);\n\t\t\treturn response;\n\t\t}\n\n\t\tattempt++;\n\t\tlastRetry = { attempt, errorMessage: response.errorMessage || \"Unknown error\" };\n\t\tconst delayMs = retryDelayMs(policy!, attempt);\n\t\tawait callbacks?.onRetryScheduled?.(attempt, maxAttempts, delayMs, lastRetry.errorMessage);\n\n\t\t// Normalize aborts during retry backoff to the same AssistantMessage shape as\n\t\t// provider stream aborts, so callers do not need to care when cancellation happened.\n\t\ttry {\n\t\t\tawait sleep(delayMs, signal);\n\t\t} catch (error) {\n\t\t\tawait callbacks?.onRetryFinished?.(false, attempt, lastRetry.errorMessage);\n\t\t\tif (error instanceof RetrySleepAbortError) {\n\t\t\t\tconst { errorMessage: _errorMessage, ...rest } = response;\n\t\t\t\treturn { ...rest, stopReason: \"aborted\" };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tawait callbacks?.onRetryAttemptStart?.();\n\t}\n}\n\n/**\n * Classifies whether a failed assistant message looks like a transient provider\n * or transport error, so callers can decide if the last assistant turn should be\n * restarted.\n *\n * This does not implement retry policy. Callers should first handle context\n * overflow separately, then apply their own retry budget, backoff, and reporting\n * before restarting the assistant turn.\n */\nexport function isRetryableAssistantError(message: AssistantMessage): boolean {\n\tif (message.stopReason !== \"error\" || !message.errorMessage) return false;\n\tconst errorMessage = message.errorMessage;\n\tif (NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN.test(errorMessage)) return false;\n\treturn RETRYABLE_PROVIDER_ERROR_PATTERN.test(errorMessage);\n}\n"]}
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"../types.ts\";\n\nfunction buildProviderErrorPattern(patterns: readonly string[]): RegExp {\n\treturn new RegExp(patterns.join(\"|\"), \"i\");\n}\n\nconst NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN = buildProviderErrorPattern([\n\t// OpenCode Go/free-tier limits returned as 429 JSON error types by OpenCode's\n\t// Zen API. These are subscription/account limits, not transient throttles.\n\t\"GoUsageLimitError\",\n\t\"FreeUsageLimitError\",\n\n\t// OpenCode Go subscription-limit text asks users to enable available-balance\n\t// usage after rolling/weekly/monthly limits are reached.\n\t\"Monthly usage limit reached\",\n\t\"available balance\",\n\n\t// Generic quota/budget/billing exhaustion. `insufficient_quota` is OpenAI's\n\t// quota/billing error code; the other strings cover common gateway wording.\n\t\"insufficient_quota\",\n\t\"out of budget\",\n\t\"quota exceeded\",\n\t\"billing\",\n\n\t// Sign in with ChatGPT: the subscription's shared usage limit, which resets\n\t// after hours rather than seconds.\n\t\"subscription_sharing_usage_limit_exceeded\",\n\t// Sign in with ChatGPT on a ChatGPT Business plan: a usage limit set by the\n\t// workspace admin. Only the admin can raise it.\n\t\"subscription_sharing_workplace_individual_usage_limit_exceeded\",\n]);\n\nconst RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([\n\t// Generic provider load, HTTP status, and server-side transient failures.\n\t\"overloaded\",\n\t\"server_busy\",\n\t\"servers are currently busy\",\n\t\"currently experiencing high demand\",\n\t\"model is at capacity\",\n\t\"rate.?limit\",\n\t\"too many requests\",\n\t// Status codes must not be part of a longer word or number, e.g. a request id or byte count (#11057).\n\t\"(?<![a-z0-9])(?:429|500|502|503|504|520|524)(?![a-z0-9])\",\n\t// Also matches \"The service is temporarily unavailable\" (#10965).\n\t\"service.?(?:is.?)?(?:temporarily.?)?unavailable\",\n\t\"server.?error\",\n\t\"internal.?error\",\n\n\t// Wrapper/provider text for transient upstream failures, including OpenRouter\n\t// \"Provider returned error\" responses (#2264) and gateway wording such as\n\t// \"The upstream provider returned an error\" (#10842).\n\t\"provider.?returned.?(?:an.?)?error\",\n\t\"exceeded request buffer limit while retrying upstream\",\n\t// Gateway failure relaying the upstream response stream, sent as an OpenAI\n\t// Responses `response.failed` event without an error code (#10920).\n\t\"unable to forward upstream\",\n\t// Other gateway upstream failures relayed as `response.failed` error codes (#10920).\n\t\"upstream.?request.?failed\",\n\t\"upstream_stream_read_error\",\n\t\"stream was interrupted\",\n\t\"http/?2 stream failed\",\n\n\t// Network, proxy, and fetch transport failures. This includes OpenAI Codex\n\t// raw-fetch failures such as \"upstream connect\", \"connection refused\", and\n\t// \"reset before headers\" (#733), plus OpenRouter connection drops (#3317).\n\t\"network.?error\",\n\t\"connection.?error\",\n\t\"connection.?refused\",\n\t\"connection.?lost\",\n\t\"other side closed\",\n\t\"fetch failed\",\n\t// node-fetch system errors, e.g. the Google ADC OAuth token refresh used by\n\t// google-vertex. The reason is often empty (#10996).\n\t\"request to \\\\S+ failed, reason:\",\n\t\"getaddrinfo\",\n\t\"ENOTFOUND\",\n\t\"EAI_AGAIN\",\n\t\"upstream.?connect\",\n\t\"reset before headers\",\n\t\"socket hang up\",\n\t\"socket connection was closed\",\n\t\"timed? out\",\n\t\"timeout\",\n\t\"terminated\",\n\n\t// WebSocket transports can report close/error text instead of HTTP/fetch text.\n\t\"websocket.?closed\",\n\t\"websocket.?error\",\n\n\t// Premature stream endings from SDKs and transports. Anthropic can throw\n\t// \"stream ended without ...\" and \"Anthropic stream ended before message_stop\"\n\t// (#4433); Bedrock/Smithy can throw an HTTP/2 no-response error (#3594).\n\t\"ended without\",\n\t\"stream ended before message_stop\",\n\t\"stream ended before a terminal response event\",\n\t\"http2 request did not get a response\",\n\t// Codex CLI mid-stream disconnect, relayed by proxies that wrap the Codex CLI,\n\t// e.g. \"stream disconnected before completion: stream closed before response.completed\" (#11057).\n\t\"stream disconnected before completion\",\n\t// Node ERR_HTTP2_STREAM_CANCEL: the HTTP/2 session died before the request was\n\t// sent, e.g. after the Bedrock SDK's 5-minute session timeout (#10379).\n\t\"pending stream has been canceled\",\n\n\t// Provider-requested retry delay cap failures should flow through the outer\n\t// retry policy so callers can surface/abort the backoff (#1123).\n\t\"retry delay\",\n\n\t// Explicit retry guidance emitted mid-stream by OpenAI Responses and Bedrock\n\t// stream exceptions (#6019).\n\t\"you can retry your request\",\n\t\"try your request again\",\n\t\"please retry your request\",\n\n\t// gRPC based providers (e.g. NVIDIA NIM)\n\t\"ResourceExhausted\",\n\n\t// Sign in with ChatGPT: usage or user data temporarily unavailable. Usage\n\t// failures can arrive mid-stream without an HTTP 503 in the message.\n\t\"subscription_sharing_usage_unavailable\",\n\t\"subscription_sharing_user_unavailable\",\n]);\n\n/**\n * Retry policy: bounded attempts with exponential backoff (`baseDelayMs * 2^(attempt-1)`).\n * `maxAgentDelayMs` caps each computed delay and defaults to 60 seconds.\n * Matches `settings.retry` (`enabled`, `maxRetries`, `baseDelayMs`, `maxAgentDelayMs`) in coding-agent; kept\n * here so the classifier and the policy-driven retry loop live together and stay reusable\n * by the SDK and other callers.\n */\nexport interface RetryPolicy {\n\tenabled: boolean;\n\t/** Max retry attempts (0 = no retries). The initial call never counts as a retry. */\n\tmaxRetries: number;\n\t/** Base delay in ms. Per-attempt delay is `baseDelayMs * 2^(attempt-1)` before jitter. */\n\tbaseDelayMs: number;\n\t/** Optional cap for agent-level retry delays in ms. Defaults to 60 seconds. */\n\tmaxAgentDelayMs?: number;\n}\n\nexport const DEFAULT_MAX_AGENT_RETRY_DELAY_MS = 60_000;\n\nexport function retryDelayMs(policy: Pick<RetryPolicy, \"baseDelayMs\" | \"maxAgentDelayMs\">, attempt: number): number {\n\tconst delay = policy.baseDelayMs * 2 ** Math.max(0, attempt - 1);\n\tconst safeDelay = Number.isSafeInteger(delay) ? delay : Number.MAX_SAFE_INTEGER;\n\treturn Math.min(safeDelay, policy.maxAgentDelayMs ?? DEFAULT_MAX_AGENT_RETRY_DELAY_MS);\n}\n\n/** Optional callbacks emitted by {@link retryAssistantCall} around each retry. */\nexport interface RetryCallbacks {\n\t/** Emitted before the backoff sleep of each retry attempt (1-indexed). */\n\tonRetryScheduled?: (\n\t\tattempt: number,\n\t\tmaxAttempts: number,\n\t\tdelayMs: number,\n\t\terrorMessage: string,\n\t) => void | Promise<void>;\n\t/** Emitted after the backoff sleep, immediately before the retried call starts. */\n\tonRetryAttemptStart?: () => void | Promise<void>;\n\t/** Emitted once when the loop ends: success if a later call completed normally. */\n\tonRetryFinished?: (success: boolean, attempt: number, finalError?: string) => void | Promise<void>;\n}\n\nclass RetrySleepAbortError extends Error {\n\tconstructor() {\n\t\tsuper(\"Aborted\");\n\t}\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new RetrySleepAbortError());\n\t\t\treturn;\n\t\t}\n\t\tconst timeout = setTimeout(resolve, ms);\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\treject(new RetrySleepAbortError());\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\n/**\n * Run a single assistant-producing call with bounded retry on transient errors.\n *\n * Behavior:\n * - A successful response is returned immediately. Aborts are terminal and never\n * retried, but reported as unsuccessful if they happen after a retry was scheduled.\n * Aborts during the backoff sleep are normalized to an aborted `AssistantMessage`\n * too, so callers do not need to care when cancellation happened.\n * - A non-retryable error (per {@link isRetryableAssistantError}, including quota/\n * billing exhaustion) is returned immediately so deterministic errors fail fast.\n * - Otherwise retries up to `maxRetries` times with exponential backoff, emitting\n * `onRetryScheduled` before each sleep, `onRetryAttemptStart` after each sleep before\n * the retried call starts, and `onRetryFinished` once at the end (whether the loop\n * ends in success, exhausted retries, or an aborted backoff).\n *\n * When `policy` is undefined or disabled, the first response is returned unchanged\n * (equivalent to calling `produce()` directly).\n */\nexport async function retryAssistantCall(\n\tproduce: () => Promise<AssistantMessage>,\n\tpolicy: RetryPolicy | undefined,\n\tsignal: AbortSignal | undefined,\n\tcallbacks?: RetryCallbacks,\n): Promise<AssistantMessage> {\n\tconst maxAttempts = policy?.enabled ? policy.maxRetries : 0;\n\n\tlet attempt = 0;\n\tlet lastRetry: { attempt: number; errorMessage: string } | undefined;\n\tfor (;;) {\n\t\tconst response = await produce();\n\n\t\t// Abort: terminal but not successful. Never retry an aborted message.\n\t\tif (response.stopReason === \"aborted\") {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt);\n\t\t\treturn response;\n\t\t}\n\n\t\t// Success: non-error, non-abort responses return as-is.\n\t\tif (response.stopReason !== \"error\") {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(true, lastRetry.attempt);\n\t\t\treturn response;\n\t\t}\n\n\t\t// Non-retryable, or budget exhausted: return the final error message.\n\t\tif (attempt >= maxAttempts || !isRetryableAssistantError(response)) {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt, response.errorMessage);\n\t\t\treturn response;\n\t\t}\n\n\t\tattempt++;\n\t\tlastRetry = { attempt, errorMessage: response.errorMessage || \"Unknown error\" };\n\t\tconst delayMs = retryDelayMs(policy!, attempt);\n\t\tawait callbacks?.onRetryScheduled?.(attempt, maxAttempts, delayMs, lastRetry.errorMessage);\n\n\t\t// Normalize aborts during retry backoff to the same AssistantMessage shape as\n\t\t// provider stream aborts, so callers do not need to care when cancellation happened.\n\t\ttry {\n\t\t\tawait sleep(delayMs, signal);\n\t\t} catch (error) {\n\t\t\tawait callbacks?.onRetryFinished?.(false, attempt, lastRetry.errorMessage);\n\t\t\tif (error instanceof RetrySleepAbortError) {\n\t\t\t\tconst { errorMessage: _errorMessage, ...rest } = response;\n\t\t\t\treturn { ...rest, stopReason: \"aborted\" };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tawait callbacks?.onRetryAttemptStart?.();\n\t}\n}\n\n/**\n * Classifies whether a failed assistant message looks like a transient provider\n * or transport error, so callers can decide if the last assistant turn should be\n * restarted.\n *\n * This does not implement retry policy. Callers should first handle context\n * overflow separately, then apply their own retry budget, backoff, and reporting\n * before restarting the assistant turn.\n */\nexport function isRetryableAssistantError(message: AssistantMessage): boolean {\n\tif (message.stopReason !== \"error\" || !message.errorMessage) return false;\n\tconst errorMessage = message.errorMessage;\n\tif (NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN.test(errorMessage)) return false;\n\treturn RETRYABLE_PROVIDER_ERROR_PATTERN.test(errorMessage);\n}\n"]}
@@ -68,20 +68,34 @@ export declare function getDeclaredTools(messages: TranscriptMessages): Tool[];
68
68
  export declare function hasToolRedefinitions(messages: TranscriptMessages): boolean;
69
69
  /** Whether tool history contains a removal or same-name redeclaration that an addition-only transport cannot replay. */
70
70
  export declare function hasNonAdditiveToolChanges(messages: TranscriptMessages): boolean;
71
+ /**
72
+ * How a transport can express tool changes after the first request.
73
+ *
74
+ * - `"none"`: only the top-level tool list exists; it always holds the current tools.
75
+ * - `"additions"`: tools can be added at a later message, but not removed or redefined.
76
+ * - `"changes"`: every tool, including the initial ones, is declared in place, and a later
77
+ * declaration under the same name replaces the earlier one. Removals are not expressible on
78
+ * the wire, so the transport announces them as text and the removed tools stay declared.
79
+ */
80
+ export type TranscriptToolSupport = "none" | "additions" | "changes";
71
81
  export interface TranscriptTools {
72
82
  /** Tools sent in the top-level request field. */
73
83
  requestTools: Tool[];
74
84
  /**
75
- * Whether later system messages carry their own `toolsAdded` as in-place additions.
76
- * When false, `requestTools` already holds the complete current tool set.
85
+ * Number of leading system messages whose tool changes `requestTools` already holds. Every
86
+ * later system message carries its own tool changes in place.
77
87
  */
78
- anchorsAdditions: boolean;
88
+ foldedSystemMessages: number;
79
89
  }
80
90
  /**
81
- * Split tool declarations between the top-level request field and in-place additions.
82
- * Transports that can anchor additions at a system message keep the initial tools at the
83
- * top and load later ones where they appear; that only works when no tool was removed or
84
- * redeclared, so everything else sends the current tool list.
91
+ * Split tool declarations between the top-level request field and in-place changes.
92
+ *
93
+ * With `"changes"`, the top-level list stays empty and every system message, including the
94
+ * leading one, declares its tools in place: an in-place declaration can replace an earlier
95
+ * in-place one, but not a top-level one. With `"additions"`, a removal or redefinition cannot be
96
+ * expressed in place, so the top-level list becomes the tool state after the last such change;
97
+ * later additions stay anchored, and the cached prefix is invalidated once per removal instead
98
+ * of on every later addition. With `"none"`, the top-level list is the current tool set.
85
99
  */
86
- export declare function resolveTranscriptTools(messages: TranscriptMessages, supportsToolAdditions: boolean): TranscriptTools;
100
+ export declare function resolveTranscriptTools(messages: TranscriptMessages, support: TranscriptToolSupport): TranscriptTools;
87
101
  //# sourceMappingURL=transcript.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../../src/utils/transcript.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAG3G,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;;GAGG;AACH,wBAAgB,0BAA0B,CACzC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,KAAK,EAAE,IAAI,EAAE,GAAG,SAAS,GACvB,aAAa,GAAG,SAAS,CAU3B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,CAIpE;AAED;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC;AAM7D,4EAA4E;AAC5E,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,aAAa,GAAG,SAAS,CAG/F;AAED,+FAA+F;AAC/F,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAE1E;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,EAAE,CAQpE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,aAAa,GAAG,SAAS,CAuB/F;AAED,kFAAkF;AAClF,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAG3E;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,iBAAiB,GAAG,iBAAiB,CAIpF;AAED,gGAAgG;AAChG,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,iBAAiB,EAC1B,8BAA8B,EAAE,OAAO,GAAG,SAAS,GACjD,iBAAiB,CAEnB;AAED,wGAAwG;AACxG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAOlD;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAElE;AAED,MAAM,WAAW,gBAAgB;IAChC,UAAU,EAAE,IAAI,EAAE,CAAC;IACnB,YAAY,EAAE,aAAa,EAAE,CAAC;CAC9B;AAED,mGAAmG;AACnG,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,SAAS,IAAI,EAAE,EAAE,OAAO,EAAE,SAAS,IAAI,EAAE,GAAG,gBAAgB,CAiBzG;AAED,wFAAwF;AACxF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,EAAE,CAOrE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAW1E;AAED,wHAAwH;AACxH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAW/E;AAED,MAAM,WAAW,eAAe;IAC/B,iDAAiD;IACjD,YAAY,EAAE,IAAI,EAAE,CAAC;IACrB;;;OAGG;IACH,gBAAgB,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,OAAO,GAAG,eAAe,CAQpH"}
1
+ {"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../../src/utils/transcript.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAG3G,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;;GAGG;AACH,wBAAgB,0BAA0B,CACzC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,KAAK,EAAE,IAAI,EAAE,GAAG,SAAS,GACvB,aAAa,GAAG,SAAS,CAU3B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,CAIpE;AAED;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC;AAM7D,4EAA4E;AAC5E,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,aAAa,GAAG,SAAS,CAG/F;AAED,+FAA+F;AAC/F,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAE1E;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,EAAE,CAQpE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,aAAa,GAAG,SAAS,CAuB/F;AAED,kFAAkF;AAClF,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAG3E;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,iBAAiB,GAAG,iBAAiB,CAIpF;AAED,gGAAgG;AAChG,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,iBAAiB,EAC1B,8BAA8B,EAAE,OAAO,GAAG,SAAS,GACjD,iBAAiB,CAEnB;AAED,wGAAwG;AACxG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAOlD;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAElE;AAED,MAAM,WAAW,gBAAgB;IAChC,UAAU,EAAE,IAAI,EAAE,CAAC;IACnB,YAAY,EAAE,aAAa,EAAE,CAAC;CAC9B;AAED,mGAAmG;AACnG,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,SAAS,IAAI,EAAE,EAAE,OAAO,EAAE,SAAS,IAAI,EAAE,GAAG,gBAAgB,CAiBzG;AAED,wFAAwF;AACxF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,EAAE,CAOrE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAW1E;AAED,wHAAwH;AACxH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAE/E;AAmBD;;;;;;;;GAQG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;AAErE,MAAM,WAAW,eAAe;IAC/B,iDAAiD;IACjD,YAAY,EAAE,IAAI,EAAE,CAAC;IACrB;;;OAGG;IACH,oBAAoB,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,qBAAqB,GAAG,eAAe,CAWpH"}
@@ -175,33 +175,44 @@ export function hasToolRedefinitions(messages) {
175
175
  }
176
176
  /** Whether tool history contains a removal or same-name redeclaration that an addition-only transport cannot replay. */
177
177
  export function hasNonAdditiveToolChanges(messages) {
178
+ return findLastNonAdditiveToolChange(messages.filter(isSystemMessage)) >= 0;
179
+ }
180
+ /** Index of the last system message with a removal or same-name redeclaration, or -1. */
181
+ function findLastNonAdditiveToolChange(systemMessages) {
182
+ // A name declared before counts as a redeclaration even after its removal: Kimi K3 ignores a
183
+ // removed tool that a later tool message declares again (verified October 2026).
178
184
  const declared = new Set();
179
- for (const message of messages) {
180
- if (!isSystemMessage(message))
181
- continue;
185
+ let last = -1;
186
+ for (let index = 0; index < systemMessages.length; index++) {
187
+ const message = systemMessages[index];
182
188
  if ((message.toolsRemoved?.length ?? 0) > 0)
183
- return true;
189
+ last = index;
184
190
  for (const tool of message.toolsAdded ?? []) {
185
191
  if (declared.has(tool.name))
186
- return true;
192
+ last = index;
187
193
  declared.add(tool.name);
188
194
  }
189
195
  }
190
- return false;
196
+ return last;
191
197
  }
192
198
  /**
193
- * Split tool declarations between the top-level request field and in-place additions.
194
- * Transports that can anchor additions at a system message keep the initial tools at the
195
- * top and load later ones where they appear; that only works when no tool was removed or
196
- * redeclared, so everything else sends the current tool list.
199
+ * Split tool declarations between the top-level request field and in-place changes.
200
+ *
201
+ * With `"changes"`, the top-level list stays empty and every system message, including the
202
+ * leading one, declares its tools in place: an in-place declaration can replace an earlier
203
+ * in-place one, but not a top-level one. With `"additions"`, a removal or redefinition cannot be
204
+ * expressed in place, so the top-level list becomes the tool state after the last such change;
205
+ * later additions stay anchored, and the cached prefix is invalidated once per removal instead
206
+ * of on every later addition. With `"none"`, the top-level list is the current tool set.
197
207
  */
198
- export function resolveTranscriptTools(messages, supportsToolAdditions) {
199
- const anchorsAdditions = supportsToolAdditions && !hasNonAdditiveToolChanges(messages);
200
- return {
201
- requestTools: anchorsAdditions
202
- ? (getInitialSystemMessage(messages)?.toolsAdded ?? [])
203
- : getCurrentTools(messages),
204
- anchorsAdditions,
205
- };
208
+ export function resolveTranscriptTools(messages, support) {
209
+ const systemMessages = messages.filter(isSystemMessage);
210
+ if (support === "none") {
211
+ return { requestTools: getCurrentTools(systemMessages), foldedSystemMessages: systemMessages.length };
212
+ }
213
+ if (support === "changes")
214
+ return { requestTools: [], foldedSystemMessages: 0 };
215
+ const folded = Math.max(getInitialSystemMessage(messages) ? 1 : 0, findLastNonAdditiveToolChange(systemMessages) + 1);
216
+ return { requestTools: getCurrentTools(systemMessages.slice(0, folded)), foldedSystemMessages: folded };
206
217
  }
207
218
  //# sourceMappingURL=transcript.js.map