@earendil-works/pi-ai 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/README.md +113 -38
  2. package/dist/api/anthropic-messages.d.ts.map +1 -1
  3. package/dist/api/anthropic-messages.js +66 -81
  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 -10
  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 +137 -103
  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/constrained-sampling.d.ts +15 -1
  18. package/dist/api/constrained-sampling.d.ts.map +1 -1
  19. package/dist/api/constrained-sampling.js +51 -18
  20. package/dist/api/constrained-sampling.js.map +1 -1
  21. package/dist/api/google-generative-ai.d.ts.map +1 -1
  22. package/dist/api/google-generative-ai.js +4 -10
  23. package/dist/api/google-generative-ai.js.map +1 -1
  24. package/dist/api/google-shared.d.ts +2 -1
  25. package/dist/api/google-shared.d.ts.map +1 -1
  26. package/dist/api/google-shared.js +58 -3
  27. package/dist/api/google-shared.js.map +1 -1
  28. package/dist/api/google-vertex.d.ts.map +1 -1
  29. package/dist/api/google-vertex.js +9 -15
  30. package/dist/api/google-vertex.js.map +1 -1
  31. package/dist/api/lazy.d.ts +7 -2
  32. package/dist/api/lazy.d.ts.map +1 -1
  33. package/dist/api/lazy.js +24 -5
  34. package/dist/api/lazy.js.map +1 -1
  35. package/dist/api/llama-cpp-classify.d.ts.map +1 -1
  36. package/dist/api/llama-cpp-classify.js +2 -1
  37. package/dist/api/llama-cpp-classify.js.map +1 -1
  38. package/dist/api/mistral-conversations.d.ts.map +1 -1
  39. package/dist/api/mistral-conversations.js +19 -46
  40. package/dist/api/mistral-conversations.js.map +1 -1
  41. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  42. package/dist/api/openai-codex-responses.js +17 -16
  43. package/dist/api/openai-codex-responses.js.map +1 -1
  44. package/dist/api/openai-completions.d.ts.map +1 -1
  45. package/dist/api/openai-completions.js +56 -62
  46. package/dist/api/openai-completions.js.map +1 -1
  47. package/dist/api/openai-responses-shared.d.ts +16 -0
  48. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  49. package/dist/api/openai-responses-shared.js +119 -66
  50. package/dist/api/openai-responses-shared.js.map +1 -1
  51. package/dist/api/openai-responses.d.ts.map +1 -1
  52. package/dist/api/openai-responses.js +17 -15
  53. package/dist/api/openai-responses.js.map +1 -1
  54. package/dist/api/openrouter-images.d.ts.map +1 -1
  55. package/dist/api/openrouter-images.js +5 -3
  56. package/dist/api/openrouter-images.js.map +1 -1
  57. package/dist/api/pi-messages.d.ts +2 -0
  58. package/dist/api/pi-messages.d.ts.map +1 -1
  59. package/dist/api/pi-messages.js +27 -12
  60. package/dist/api/pi-messages.js.map +1 -1
  61. package/dist/api/simple-options.d.ts.map +1 -1
  62. package/dist/api/simple-options.js +2 -1
  63. package/dist/api/simple-options.js.map +1 -1
  64. package/dist/api/system-one-shared.d.ts +29 -2
  65. package/dist/api/system-one-shared.d.ts.map +1 -1
  66. package/dist/api/system-one-shared.js +55 -3
  67. package/dist/api/system-one-shared.js.map +1 -1
  68. package/dist/api/typesafe-system-one.d.ts.map +1 -1
  69. package/dist/api/typesafe-system-one.js +10 -0
  70. package/dist/api/typesafe-system-one.js.map +1 -1
  71. package/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  72. package/dist/auth/oauth/github-copilot.js +4 -11
  73. package/dist/auth/oauth/github-copilot.js.map +1 -1
  74. package/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
  75. package/dist/auth/oauth/kimi-coding.js +4 -22
  76. package/dist/auth/oauth/kimi-coding.js.map +1 -1
  77. package/dist/auth/oauth/meta.d.ts.map +1 -1
  78. package/dist/auth/oauth/meta.js +1 -14
  79. package/dist/auth/oauth/meta.js.map +1 -1
  80. package/dist/auth/oauth/radius.d.ts.map +1 -1
  81. package/dist/auth/oauth/radius.js +9 -3
  82. package/dist/auth/oauth/radius.js.map +1 -1
  83. package/dist/auth/oauth/trusted-url.d.ts +7 -0
  84. package/dist/auth/oauth/trusted-url.d.ts.map +1 -0
  85. package/dist/auth/oauth/trusted-url.js +12 -0
  86. package/dist/auth/oauth/trusted-url.js.map +1 -0
  87. package/dist/auth/types.d.ts +4 -0
  88. package/dist/auth/types.d.ts.map +1 -1
  89. package/dist/auth/types.js.map +1 -1
  90. package/dist/cli.js +6 -2
  91. package/dist/cli.js.map +1 -1
  92. package/dist/compat/extension-oauth-types.d.ts +4 -0
  93. package/dist/compat/extension-oauth-types.d.ts.map +1 -1
  94. package/dist/compat/extension-oauth-types.js.map +1 -1
  95. package/dist/index.d.ts +4 -3
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +4 -2
  98. package/dist/index.js.map +1 -1
  99. package/dist/models.d.ts +4 -0
  100. package/dist/models.d.ts.map +1 -1
  101. package/dist/models.generated.js +1 -1
  102. package/dist/models.generated.js.map +1 -1
  103. package/dist/models.js +20 -6
  104. package/dist/models.js.map +1 -1
  105. package/dist/providers/amazon-bedrock.models.js +1 -1
  106. package/dist/providers/amazon-bedrock.models.js.map +1 -1
  107. package/dist/providers/ant-ling.models.js +1 -1
  108. package/dist/providers/ant-ling.models.js.map +1 -1
  109. package/dist/providers/anthropic.models.js +1 -1
  110. package/dist/providers/anthropic.models.js.map +1 -1
  111. package/dist/providers/azure.models.js +1 -1
  112. package/dist/providers/azure.models.js.map +1 -1
  113. package/dist/providers/baseten.models.js +1 -1
  114. package/dist/providers/baseten.models.js.map +1 -1
  115. package/dist/providers/cerebras.models.js +1 -1
  116. package/dist/providers/cerebras.models.js.map +1 -1
  117. package/dist/providers/cloudflare-ai-gateway.models.js +1 -1
  118. package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
  119. package/dist/providers/cloudflare-workers-ai.models.js +1 -1
  120. package/dist/providers/cloudflare-workers-ai.models.js.map +1 -1
  121. package/dist/providers/compat-schema.d.ts +444 -304
  122. package/dist/providers/compat-schema.d.ts.map +1 -1
  123. package/dist/providers/compat-schema.js +155 -109
  124. package/dist/providers/compat-schema.js.map +1 -1
  125. package/dist/providers/data/.manifest.json +1 -1
  126. package/dist/providers/data/amazon-bedrock.json +1 -1
  127. package/dist/providers/data/anthropic.json +1 -1
  128. package/dist/providers/data/azure.json +1 -1
  129. package/dist/providers/data/baseten.json +1 -1
  130. package/dist/providers/data/cerebras.json +1 -1
  131. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  132. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  133. package/dist/providers/data/fireworks.json +1 -1
  134. package/dist/providers/data/github-copilot.json +1 -1
  135. package/dist/providers/data/google-vertex.json +1 -1
  136. package/dist/providers/data/google.json +1 -1
  137. package/dist/providers/data/groq.json +1 -1
  138. package/dist/providers/data/huggingface.json +1 -1
  139. package/dist/providers/data/kimi-coding.json +1 -1
  140. package/dist/providers/data/minimax-cn.json +1 -1
  141. package/dist/providers/data/minimax.json +1 -1
  142. package/dist/providers/data/mistral.json +1 -1
  143. package/dist/providers/data/moonshotai-cn.json +1 -1
  144. package/dist/providers/data/moonshotai.json +1 -1
  145. package/dist/providers/data/nvidia.json +1 -1
  146. package/dist/providers/data/openai-codex.json +1 -1
  147. package/dist/providers/data/openai.json +1 -1
  148. package/dist/providers/data/opencode-go.json +1 -1
  149. package/dist/providers/data/opencode.json +1 -1
  150. package/dist/providers/data/openrouter.json +1 -1
  151. package/dist/providers/data/qwen-token-plan-cn.json +1 -1
  152. package/dist/providers/data/qwen-token-plan-individual.json +1 -1
  153. package/dist/providers/data/qwen-token-plan.json +1 -1
  154. package/dist/providers/data/radius.json +1 -1
  155. package/dist/providers/data/together.json +1 -1
  156. package/dist/providers/data/typesafe.json +1 -1
  157. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  158. package/dist/providers/data/xai.json +1 -1
  159. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  160. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  161. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  162. package/dist/providers/data/xiaomi.json +1 -1
  163. package/dist/providers/data/zai-coding-cn.json +1 -1
  164. package/dist/providers/data/zai.json +1 -1
  165. package/dist/providers/deepseek.models.js +1 -1
  166. package/dist/providers/deepseek.models.js.map +1 -1
  167. package/dist/providers/faux.d.ts.map +1 -1
  168. package/dist/providers/faux.js +7 -2
  169. package/dist/providers/faux.js.map +1 -1
  170. package/dist/providers/fireworks.models.js +1 -1
  171. package/dist/providers/fireworks.models.js.map +1 -1
  172. package/dist/providers/github-copilot.models.js +1 -1
  173. package/dist/providers/github-copilot.models.js.map +1 -1
  174. package/dist/providers/google-vertex.models.js +1 -1
  175. package/dist/providers/google-vertex.models.js.map +1 -1
  176. package/dist/providers/google.models.js +1 -1
  177. package/dist/providers/google.models.js.map +1 -1
  178. package/dist/providers/groq.models.js +1 -1
  179. package/dist/providers/groq.models.js.map +1 -1
  180. package/dist/providers/huggingface.models.js +1 -1
  181. package/dist/providers/huggingface.models.js.map +1 -1
  182. package/dist/providers/kimi-coding.models.js +1 -1
  183. package/dist/providers/kimi-coding.models.js.map +1 -1
  184. package/dist/providers/meta.models.js +1 -1
  185. package/dist/providers/meta.models.js.map +1 -1
  186. package/dist/providers/minimax-cn.models.js +1 -1
  187. package/dist/providers/minimax-cn.models.js.map +1 -1
  188. package/dist/providers/minimax.models.js +1 -1
  189. package/dist/providers/minimax.models.js.map +1 -1
  190. package/dist/providers/mistral.models.js +1 -1
  191. package/dist/providers/mistral.models.js.map +1 -1
  192. package/dist/providers/model-schema.d.ts +65 -65
  193. package/dist/providers/model-schema.d.ts.map +1 -1
  194. package/dist/providers/model-schema.js +44 -31
  195. package/dist/providers/model-schema.js.map +1 -1
  196. package/dist/providers/moonshotai-cn.models.js +1 -1
  197. package/dist/providers/moonshotai-cn.models.js.map +1 -1
  198. package/dist/providers/moonshotai.models.js +1 -1
  199. package/dist/providers/moonshotai.models.js.map +1 -1
  200. package/dist/providers/nvidia.models.js +1 -1
  201. package/dist/providers/nvidia.models.js.map +1 -1
  202. package/dist/providers/openai-codex.models.js +1 -1
  203. package/dist/providers/openai-codex.models.js.map +1 -1
  204. package/dist/providers/openai.models.js +1 -1
  205. package/dist/providers/openai.models.js.map +1 -1
  206. package/dist/providers/opencode-go.models.js +1 -1
  207. package/dist/providers/opencode-go.models.js.map +1 -1
  208. package/dist/providers/opencode.js +1 -1
  209. package/dist/providers/opencode.js.map +1 -1
  210. package/dist/providers/opencode.models.js +1 -1
  211. package/dist/providers/opencode.models.js.map +1 -1
  212. package/dist/providers/openrouter.models.js +1 -1
  213. package/dist/providers/openrouter.models.js.map +1 -1
  214. package/dist/providers/qwen-token-plan-cn.models.js +1 -1
  215. package/dist/providers/qwen-token-plan-cn.models.js.map +1 -1
  216. package/dist/providers/qwen-token-plan-individual.models.js +1 -1
  217. package/dist/providers/qwen-token-plan-individual.models.js.map +1 -1
  218. package/dist/providers/qwen-token-plan.models.js +1 -1
  219. package/dist/providers/qwen-token-plan.models.js.map +1 -1
  220. package/dist/providers/radius-config.d.ts.map +1 -1
  221. package/dist/providers/radius-config.js +2 -1
  222. package/dist/providers/radius-config.js.map +1 -1
  223. package/dist/providers/radius.models.js +1 -1
  224. package/dist/providers/radius.models.js.map +1 -1
  225. package/dist/providers/schema-builders.d.ts +10 -0
  226. package/dist/providers/schema-builders.d.ts.map +1 -0
  227. package/dist/providers/schema-builders.js +18 -0
  228. package/dist/providers/schema-builders.js.map +1 -0
  229. package/dist/providers/together.models.js +1 -1
  230. package/dist/providers/together.models.js.map +1 -1
  231. package/dist/providers/typesafe.models.js +1 -1
  232. package/dist/providers/typesafe.models.js.map +1 -1
  233. package/dist/providers/vercel-ai-gateway.models.js +1 -1
  234. package/dist/providers/vercel-ai-gateway.models.js.map +1 -1
  235. package/dist/providers/xai.models.js +1 -1
  236. package/dist/providers/xai.models.js.map +1 -1
  237. package/dist/providers/xiaomi-token-plan-ams.models.js +1 -1
  238. package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
  239. package/dist/providers/xiaomi-token-plan-cn.models.js +1 -1
  240. package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
  241. package/dist/providers/xiaomi-token-plan-sgp.models.js +1 -1
  242. package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
  243. package/dist/providers/xiaomi.models.js +1 -1
  244. package/dist/providers/xiaomi.models.js.map +1 -1
  245. package/dist/providers/zai-coding-cn.models.js +1 -1
  246. package/dist/providers/zai-coding-cn.models.js.map +1 -1
  247. package/dist/providers/zai.models.js +1 -1
  248. package/dist/providers/zai.models.js.map +1 -1
  249. package/dist/telemetry.d.ts +288 -0
  250. package/dist/telemetry.d.ts.map +1 -0
  251. package/dist/telemetry.js +192 -0
  252. package/dist/telemetry.js.map +1 -0
  253. package/dist/types.d.ts +25 -7
  254. package/dist/types.d.ts.map +1 -1
  255. package/dist/types.js.map +1 -1
  256. package/dist/utils/error-body.d.ts.map +1 -1
  257. package/dist/utils/error-body.js +2 -1
  258. package/dist/utils/error-body.js.map +1 -1
  259. package/dist/utils/identity.d.ts +65 -0
  260. package/dist/utils/identity.d.ts.map +1 -0
  261. package/dist/utils/identity.js +141 -0
  262. package/dist/utils/identity.js.map +1 -0
  263. package/dist/utils/openrouter-cost.d.ts +24 -0
  264. package/dist/utils/openrouter-cost.d.ts.map +1 -0
  265. package/dist/utils/openrouter-cost.js +34 -0
  266. package/dist/utils/openrouter-cost.js.map +1 -0
  267. package/dist/utils/overflow.d.ts +1 -0
  268. package/dist/utils/overflow.d.ts.map +1 -1
  269. package/dist/utils/overflow.js +8 -1
  270. package/dist/utils/overflow.js.map +1 -1
  271. package/dist/utils/retry.d.ts.map +1 -1
  272. package/dist/utils/retry.js +19 -7
  273. package/dist/utils/retry.js.map +1 -1
  274. package/dist/utils/streaming-json.d.ts +84 -0
  275. package/dist/utils/streaming-json.d.ts.map +1 -0
  276. package/dist/utils/streaming-json.js +513 -0
  277. package/dist/utils/streaming-json.js.map +1 -0
  278. package/dist/utils/{typebox-helpers.d.ts → string-enum.d.ts} +5 -5
  279. package/dist/utils/string-enum.d.ts.map +1 -0
  280. package/dist/utils/{typebox-helpers.js → string-enum.js} +5 -6
  281. package/dist/utils/string-enum.js.map +1 -0
  282. package/dist/utils/text.d.ts +7 -0
  283. package/dist/utils/text.d.ts.map +1 -1
  284. package/dist/utils/text.js +9 -0
  285. package/dist/utils/text.js.map +1 -1
  286. package/dist/utils/transcript.d.ts +23 -9
  287. package/dist/utils/transcript.d.ts.map +1 -1
  288. package/dist/utils/transcript.js +30 -19
  289. package/dist/utils/transcript.js.map +1 -1
  290. package/dist/utils/validation.d.ts +5 -3
  291. package/dist/utils/validation.d.ts.map +1 -1
  292. package/dist/utils/validation.js +27 -277
  293. package/dist/utils/validation.js.map +1 -1
  294. package/dist/utils/version.d.ts +2 -0
  295. package/dist/utils/version.d.ts.map +1 -0
  296. package/dist/utils/version.js +3 -0
  297. package/dist/utils/version.js.map +1 -0
  298. package/package.json +11 -10
  299. package/dist/utils/pi-user-agent.d.ts +0 -2
  300. package/dist/utils/pi-user-agent.d.ts.map +0 -1
  301. package/dist/utils/pi-user-agent.js +0 -12
  302. package/dist/utils/pi-user-agent.js.map +0 -1
  303. package/dist/utils/typebox-helpers.d.ts.map +0 -1
  304. package/dist/utils/typebox-helpers.js.map +0 -1
@@ -10,5 +10,12 @@ export declare function getSystemMessageText(message: SystemMessage): string;
10
10
  * This framing is request-time only and may change between versions.
11
11
  */
12
12
  export declare function renderSystemMessageUpdate(message: SystemMessage): string;
13
+ /**
14
+ * `text.slice(start, end)` as a string of its own. V8 represents a slice of 13 or more characters as a view that keeps
15
+ * all of `text` alive, so a short excerpt kept from a large text (a 500-character error message cut from a megabyte
16
+ * response body) would keep the whole text alive as long as the excerpt lives. Appending a character and slicing it off
17
+ * again makes V8 copy just the slice.
18
+ */
19
+ export declare function ownedSlice(text: string, start: number, end?: number): string;
13
20
  export {};
14
21
  //# sourceMappingURL=text.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/utils/text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvG,KAAK,OAAO,GAAG,WAAW,GAAG,YAAY,GAAG,eAAe,GAAG,QAAQ,CAAC;AAEvE,kDAAkD;AAClD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,OAAO,EAAE,EAAE,SAAS,SAAO,GAAG,MAAM,CAM1F;AAED,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAMnE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAYxE"}
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/utils/text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvG,KAAK,OAAO,GAAG,WAAW,GAAG,YAAY,GAAG,eAAe,GAAG,QAAQ,CAAC;AAEvE,kDAAkD;AAClD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,OAAO,EAAE,EAAE,SAAS,SAAO,GAAG,MAAM,CAM1F;AAED,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAMnE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAYxE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAc,GAAG,MAAM,CAEjF"}
@@ -33,4 +33,13 @@ export function renderSystemMessageUpdate(message) {
33
33
  }
34
34
  return parts.join("\n\n");
35
35
  }
36
+ /**
37
+ * `text.slice(start, end)` as a string of its own. V8 represents a slice of 13 or more characters as a view that keeps
38
+ * all of `text` alive, so a short excerpt kept from a large text (a 500-character error message cut from a megabyte
39
+ * response body) would keep the whole text alive as long as the excerpt lives. Appending a character and slicing it off
40
+ * again makes V8 copy just the slice.
41
+ */
42
+ export function ownedSlice(text, start, end = text.length) {
43
+ return `${text.slice(start, end)} `.slice(0, -1);
44
+ }
36
45
  //# sourceMappingURL=text.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/utils/text.ts"],"names":[],"mappings":"AAIA,kDAAkD;AAClD,MAAM,UAAU,WAAW,CAAC,OAAoC,EAAE,SAAS,GAAG,IAAI;IACjF,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,OAAO,OAAO;SACZ,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC;SACxC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;SAC1B,IAAI,CAAC,SAAS,CAAC,CAAC;AACnB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,oBAAoB,CAAC,OAAsB;IAC1D,MAAM,KAAK,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;QAC1D,IAAI,IAAI,KAAK,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC7D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAsB;IAC/D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;QACpE,KAAK,CAAC,IAAI,CACT,KAAK,KAAK,IAAI;YACb,CAAC,CAAC,kCAAkC,IAAI,IAAI;YAC5C,CAAC,CAAC,kCAAkC,IAAI,SAAS,KAAK,EAAE,CACzD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC","sourcesContent":["import type { ImageContent, SystemMessage, TextContent, ThinkingContent, ToolCall } from \"../types.ts\";\n\ntype Content = TextContent | ImageContent | ThinkingContent | ToolCall;\n\n/** Extract and join text from message content. */\nexport function contentText(content: string | readonly Content[], separator = \"\\n\"): string {\n\tif (typeof content === \"string\") return content;\n\treturn content\n\t\t.filter((block) => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(separator);\n}\n\n/** Render a system message as a complete prompt: its content followed by its sections. */\nexport function getSystemMessageText(message: SystemMessage): string {\n\tconst parts = [contentText(message.content)];\n\tfor (const text of Object.values(message.sections ?? {})) {\n\t\tif (text !== null) parts.push(text);\n\t}\n\treturn parts.filter((part) => part.length > 0).join(\"\\n\\n\");\n}\n\n/**\n * Render a later system message for APIs that accept system messages mid-conversation.\n * Section changes are framed by name so the model can relate them to the leading prompt.\n * This framing is request-time only and may change between versions.\n */\nexport function renderSystemMessageUpdate(message: SystemMessage): string {\n\tconst parts: string[] = [];\n\tconst text = contentText(message.content);\n\tif (text.length > 0) parts.push(text);\n\tfor (const [name, value] of Object.entries(message.sections ?? {})) {\n\t\tparts.push(\n\t\t\tvalue === null\n\t\t\t\t? `Removed system prompt section \"${name}\".`\n\t\t\t\t: `Updated system prompt section \"${name}\":\\n\\n${value}`,\n\t\t);\n\t}\n\treturn parts.join(\"\\n\\n\");\n}\n"]}
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/utils/text.ts"],"names":[],"mappings":"AAIA,kDAAkD;AAClD,MAAM,UAAU,WAAW,CAAC,OAAoC,EAAE,SAAS,GAAG,IAAI;IACjF,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,OAAO,OAAO;SACZ,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC;SACxC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;SAC1B,IAAI,CAAC,SAAS,CAAC,CAAC;AACnB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,oBAAoB,CAAC,OAAsB;IAC1D,MAAM,KAAK,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;QAC1D,IAAI,IAAI,KAAK,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC7D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAsB;IAC/D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;QACpE,KAAK,CAAC,IAAI,CACT,KAAK,KAAK,IAAI;YACb,CAAC,CAAC,kCAAkC,IAAI,IAAI;YAC5C,CAAC,CAAC,kCAAkC,IAAI,SAAS,KAAK,EAAE,CACzD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAa,EAAE,GAAG,GAAG,IAAI,CAAC,MAAM;IACxE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAClD,CAAC","sourcesContent":["import type { ImageContent, SystemMessage, TextContent, ThinkingContent, ToolCall } from \"../types.ts\";\n\ntype Content = TextContent | ImageContent | ThinkingContent | ToolCall;\n\n/** Extract and join text from message content. */\nexport function contentText(content: string | readonly Content[], separator = \"\\n\"): string {\n\tif (typeof content === \"string\") return content;\n\treturn content\n\t\t.filter((block) => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(separator);\n}\n\n/** Render a system message as a complete prompt: its content followed by its sections. */\nexport function getSystemMessageText(message: SystemMessage): string {\n\tconst parts = [contentText(message.content)];\n\tfor (const text of Object.values(message.sections ?? {})) {\n\t\tif (text !== null) parts.push(text);\n\t}\n\treturn parts.filter((part) => part.length > 0).join(\"\\n\\n\");\n}\n\n/**\n * Render a later system message for APIs that accept system messages mid-conversation.\n * Section changes are framed by name so the model can relate them to the leading prompt.\n * This framing is request-time only and may change between versions.\n */\nexport function renderSystemMessageUpdate(message: SystemMessage): string {\n\tconst parts: string[] = [];\n\tconst text = contentText(message.content);\n\tif (text.length > 0) parts.push(text);\n\tfor (const [name, value] of Object.entries(message.sections ?? {})) {\n\t\tparts.push(\n\t\t\tvalue === null\n\t\t\t\t? `Removed system prompt section \"${name}\".`\n\t\t\t\t: `Updated system prompt section \"${name}\":\\n\\n${value}`,\n\t\t);\n\t}\n\treturn parts.join(\"\\n\\n\");\n}\n\n/**\n * `text.slice(start, end)` as a string of its own. V8 represents a slice of 13 or more characters as a view that keeps\n * all of `text` alive, so a short excerpt kept from a large text (a 500-character error message cut from a megabyte\n * response body) would keep the whole text alive as long as the excerpt lives. Appending a character and slicing it off\n * again makes V8 copy just the slice.\n */\nexport function ownedSlice(text: string, start: number, end = text.length): string {\n\treturn `${text.slice(start, end)} `.slice(0, -1);\n}\n"]}
@@ -45,7 +45,7 @@ export declare function toToolDeclaration(tool: Tool): Tool;
45
45
  * Whether two tools declare the same interface to the model.
46
46
  *
47
47
  * Both sides go through {@link toToolDeclaration} first: its JSON round-trip drops the
48
- * typebox symbol keys and `undefined` fields that a structural comparison would see, and
48
+ * schema metadata keys (TypeBox's non-enumerable kind marks) and `undefined` fields that a structural comparison would see, and
49
49
  * builds both objects with the same key order, so comparing the serialized declarations
50
50
  * is exact. This avoids a deep-equal dependency in a browser-safe package.
51
51
  */
@@ -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"}
@@ -115,7 +115,7 @@ export function toToolDeclaration(tool) {
115
115
  * Whether two tools declare the same interface to the model.
116
116
  *
117
117
  * Both sides go through {@link toToolDeclaration} first: its JSON round-trip drops the
118
- * typebox symbol keys and `undefined` fields that a structural comparison would see, and
118
+ * schema metadata keys (TypeBox's non-enumerable kind marks) and `undefined` fields that a structural comparison would see, and
119
119
  * builds both objects with the same key order, so comparing the serialized declarations
120
120
  * is exact. This avoids a deep-equal dependency in a browser-safe package.
121
121
  */
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"transcript.js","sourceRoot":"","sources":["../../src/utils/transcript.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context, Message, SystemMessage, Tool, ToolReference, TranscriptContext } from \"../types.ts\";\nimport { contentText, getSystemMessageText } from \"./text.ts\";\n\nexport type { TranscriptContext } from \"../types.ts\";\n\n/**\n * Build the leading system message for a prompt and tool set. Returns undefined when\n * both are empty, so an empty transcript stays empty.\n */\nexport function createInitialSystemMessage(\n\tsystemPrompt: string | undefined,\n\ttools: Tool[] | undefined,\n): SystemMessage | undefined {\n\tconst hasSystemPrompt = systemPrompt !== undefined && systemPrompt.length > 0;\n\tconst hasTools = tools !== undefined && tools.length > 0;\n\tif (!hasSystemPrompt && !hasTools) return undefined;\n\treturn {\n\t\trole: \"system\",\n\t\tcontent: systemPrompt ?? \"\",\n\t\t...(hasTools ? { toolsAdded: tools } : {}),\n\t\ttimestamp: 0,\n\t};\n}\n\n/**\n * Fold `Context.systemPrompt` and `Context.tools` into a leading system message.\n * This is the only entry point that produces a {@link TranscriptContext}; every\n * provider-facing function expects the result.\n */\nexport function normalizeContext(context: Context): TranscriptContext {\n\tconst initialMessage = createInitialSystemMessage(context.systemPrompt, context.tools);\n\tconst messages = initialMessage ? [initialMessage, ...context.messages] : context.messages;\n\treturn { messages } as TranscriptContext;\n}\n\n/**\n * Any message list. The replay helpers only read entries whose role is `\"system\"`, so\n * agent transcripts that carry custom message roles can be passed without filtering.\n */\nexport type TranscriptMessages = readonly { role: string }[];\n\nfunction isSystemMessage(message: { role: string }): message is SystemMessage {\n\treturn message.role === \"system\";\n}\n\n/** Return the leading system message, if the transcript starts with one. */\nexport function getInitialSystemMessage(messages: TranscriptMessages): SystemMessage | undefined {\n\tconst first = messages[0];\n\treturn first && isSystemMessage(first) ? first : undefined;\n}\n\n/** Drop the leading system message for APIs that carry the prompt outside the message list. */\nexport function withoutInitialSystemMessage(messages: Message[]): Message[] {\n\treturn getInitialSystemMessage(messages) ? messages.slice(1) : messages;\n}\n\n/** Resolve the tools available after applying every transcript delta in order. */\nexport function getCurrentTools(messages: TranscriptMessages): Tool[] {\n\tconst tools = new Map<string, Tool>();\n\tfor (const message of messages) {\n\t\tif (!isSystemMessage(message)) continue;\n\t\tfor (const tool of message.toolsRemoved ?? []) tools.delete(tool.name);\n\t\tfor (const tool of message.toolsAdded ?? []) tools.set(tool.name, tool);\n\t}\n\treturn [...tools.values()];\n}\n\n/**\n * Replay every system message into one leading system message holding the current\n * prompt and tools. Later `content` is appended to the base prompt, `sections` are\n * patched by name, and tools are resolved with {@link getCurrentTools}.\n */\nexport function getCurrentSystemMessage(messages: TranscriptMessages): SystemMessage | undefined {\n\tconst content: string[] = [];\n\tconst sections = new Map<string, string>();\n\tlet timestamp: number | undefined;\n\tfor (const message of messages) {\n\t\tif (!isSystemMessage(message)) continue;\n\t\ttimestamp ??= message.timestamp;\n\t\tconst text = contentText(message.content);\n\t\tif (text.length > 0) content.push(text);\n\t\tfor (const [name, value] of Object.entries(message.sections ?? {})) {\n\t\t\tif (value === null) sections.delete(name);\n\t\t\telse sections.set(name, value);\n\t\t}\n\t}\n\tconst tools = getCurrentTools(messages);\n\tif (timestamp === undefined && tools.length === 0) return undefined;\n\treturn {\n\t\trole: \"system\",\n\t\tcontent: content.join(\"\\n\\n\"),\n\t\t...(sections.size > 0 ? { sections: Object.fromEntries(sections) } : {}),\n\t\t...(tools.length > 0 ? { toolsAdded: tools } : {}),\n\t\ttimestamp: timestamp ?? 0,\n\t};\n}\n\n/** Render the current system prompt text after replaying every system message. */\nexport function getCurrentSystemPrompt(messages: TranscriptMessages): string {\n\tconst message = getCurrentSystemMessage(messages);\n\treturn message ? getSystemMessageText(message) : \"\";\n}\n\n/**\n * Rebuild the transcript for APIs without mid-conversation system messages: the replayed\n * system message leads, and every later system message is dropped.\n */\nexport function collapseSystemMessages(context: TranscriptContext): TranscriptContext {\n\tconst head = getCurrentSystemMessage(context.messages);\n\tconst messages = context.messages.filter((message) => message.role !== \"system\");\n\treturn { messages: head ? [head, ...messages] : messages } as TranscriptContext;\n}\n\n/** Keep later system messages in place when the model accepts them; otherwise collapse them. */\nexport function resolveTranscript(\n\tcontext: TranscriptContext,\n\tsupportsMidConvoSystemMessages: boolean | undefined,\n): TranscriptContext {\n\treturn supportsMidConvoSystemMessages ? context : collapseSystemMessages(context);\n}\n\n/** Strip executable and display-only fields from a tool before transcript comparison or persistence. */\nexport function toToolDeclaration(tool: Tool): Tool {\n\treturn {\n\t\tname: tool.name,\n\t\tdescription: tool.description,\n\t\tparameters: JSON.parse(JSON.stringify(tool.parameters)) as Tool[\"parameters\"],\n\t\t...(tool.constrainedSampling === undefined ? {} : { constrainedSampling: tool.constrainedSampling }),\n\t};\n}\n\n/**\n * Whether two tools declare the same interface to the model.\n *\n * Both sides go through {@link toToolDeclaration} first: its JSON round-trip drops the\n * typebox symbol keys and `undefined` fields that a structural comparison would see, and\n * builds both objects with the same key order, so comparing the serialized declarations\n * is exact. This avoids a deep-equal dependency in a browser-safe package.\n */\nexport function declarationsEqual(left: Tool, right: Tool): boolean {\n\treturn JSON.stringify(toToolDeclaration(left)) === JSON.stringify(toToolDeclaration(right));\n}\n\nexport interface ToolStateChanges {\n\ttoolsAdded: Tool[];\n\ttoolsRemoved: ToolReference[];\n}\n\n/** Compare two complete tool states. A changed definition is a removal followed by an addition. */\nexport function getToolStateChanges(previous: readonly Tool[], current: readonly Tool[]): ToolStateChanges {\n\tconst previousTools = new Map(previous.map((tool) => [tool.name, tool]));\n\tconst currentTools = new Map(current.map((tool) => [tool.name, tool]));\n\treturn {\n\t\ttoolsAdded: current\n\t\t\t.filter((tool) => {\n\t\t\t\tconst previousTool = previousTools.get(tool.name);\n\t\t\t\treturn previousTool === undefined || !declarationsEqual(previousTool, tool);\n\t\t\t})\n\t\t\t.map(toToolDeclaration),\n\t\ttoolsRemoved: previous\n\t\t\t.filter((tool) => {\n\t\t\t\tconst currentTool = currentTools.get(tool.name);\n\t\t\t\treturn currentTool === undefined || !declarationsEqual(tool, currentTool);\n\t\t\t})\n\t\t\t.map((tool) => ({ name: tool.name })),\n\t};\n}\n\n/** Every definition referenced by transcript tool state, in first-declaration order. */\nexport function getDeclaredTools(messages: TranscriptMessages): Tool[] {\n\tconst definitions = new Map<string, Tool>();\n\tfor (const message of messages) {\n\t\tif (!isSystemMessage(message)) continue;\n\t\tfor (const tool of message.toolsAdded ?? []) definitions.set(tool.name, tool);\n\t}\n\treturn [...definitions.values()];\n}\n\n/**\n * Whether a tool name was declared twice with different definitions. A transport that can\n * only reference previously declared tools by name cannot replay such a history.\n *\n * @deprecated No built-in transport needs this anymore: Anthropic expresses redefinitions with\n * inline `tool_definition` blocks. Kept for API compatibility and will be removed in a future release.\n */\nexport function hasToolRedefinitions(messages: TranscriptMessages): boolean {\n\tconst declared = new Map<string, Tool>();\n\tfor (const message of messages) {\n\t\tif (!isSystemMessage(message)) continue;\n\t\tfor (const tool of message.toolsAdded ?? []) {\n\t\t\tconst previous = declared.get(tool.name);\n\t\t\tif (previous !== undefined && !declarationsEqual(previous, tool)) return true;\n\t\t\tdeclared.set(tool.name, tool);\n\t\t}\n\t}\n\treturn false;\n}\n\n/** Whether tool history contains a removal or same-name redeclaration that an addition-only transport cannot replay. */\nexport function hasNonAdditiveToolChanges(messages: TranscriptMessages): boolean {\n\tconst declared = new Set<string>();\n\tfor (const message of messages) {\n\t\tif (!isSystemMessage(message)) continue;\n\t\tif ((message.toolsRemoved?.length ?? 0) > 0) return true;\n\t\tfor (const tool of message.toolsAdded ?? []) {\n\t\t\tif (declared.has(tool.name)) return true;\n\t\t\tdeclared.add(tool.name);\n\t\t}\n\t}\n\treturn false;\n}\n\nexport interface TranscriptTools {\n\t/** Tools sent in the top-level request field. */\n\trequestTools: Tool[];\n\t/**\n\t * Whether later system messages carry their own `toolsAdded` as in-place additions.\n\t * When false, `requestTools` already holds the complete current tool set.\n\t */\n\tanchorsAdditions: boolean;\n}\n\n/**\n * Split tool declarations between the top-level request field and in-place additions.\n * Transports that can anchor additions at a system message keep the initial tools at the\n * top and load later ones where they appear; that only works when no tool was removed or\n * redeclared, so everything else sends the current tool list.\n */\nexport function resolveTranscriptTools(messages: TranscriptMessages, supportsToolAdditions: boolean): TranscriptTools {\n\tconst anchorsAdditions = supportsToolAdditions && !hasNonAdditiveToolChanges(messages);\n\treturn {\n\t\trequestTools: anchorsAdditions\n\t\t\t? (getInitialSystemMessage(messages)?.toolsAdded ?? [])\n\t\t\t: getCurrentTools(messages),\n\t\tanchorsAdditions,\n\t};\n}\n"]}
1
+ {"version":3,"file":"transcript.js","sourceRoot":"","sources":["../../src/utils/transcript.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context, Message, SystemMessage, Tool, ToolReference, TranscriptContext } from \"../types.ts\";\nimport { contentText, getSystemMessageText } from \"./text.ts\";\n\nexport type { TranscriptContext } from \"../types.ts\";\n\n/**\n * Build the leading system message for a prompt and tool set. Returns undefined when\n * both are empty, so an empty transcript stays empty.\n */\nexport function createInitialSystemMessage(\n\tsystemPrompt: string | undefined,\n\ttools: Tool[] | undefined,\n): SystemMessage | undefined {\n\tconst hasSystemPrompt = systemPrompt !== undefined && systemPrompt.length > 0;\n\tconst hasTools = tools !== undefined && tools.length > 0;\n\tif (!hasSystemPrompt && !hasTools) return undefined;\n\treturn {\n\t\trole: \"system\",\n\t\tcontent: systemPrompt ?? \"\",\n\t\t...(hasTools ? { toolsAdded: tools } : {}),\n\t\ttimestamp: 0,\n\t};\n}\n\n/**\n * Fold `Context.systemPrompt` and `Context.tools` into a leading system message.\n * This is the only entry point that produces a {@link TranscriptContext}; every\n * provider-facing function expects the result.\n */\nexport function normalizeContext(context: Context): TranscriptContext {\n\tconst initialMessage = createInitialSystemMessage(context.systemPrompt, context.tools);\n\tconst messages = initialMessage ? [initialMessage, ...context.messages] : context.messages;\n\treturn { messages } as TranscriptContext;\n}\n\n/**\n * Any message list. The replay helpers only read entries whose role is `\"system\"`, so\n * agent transcripts that carry custom message roles can be passed without filtering.\n */\nexport type TranscriptMessages = readonly { role: string }[];\n\nfunction isSystemMessage(message: { role: string }): message is SystemMessage {\n\treturn message.role === \"system\";\n}\n\n/** Return the leading system message, if the transcript starts with one. */\nexport function getInitialSystemMessage(messages: TranscriptMessages): SystemMessage | undefined {\n\tconst first = messages[0];\n\treturn first && isSystemMessage(first) ? first : undefined;\n}\n\n/** Drop the leading system message for APIs that carry the prompt outside the message list. */\nexport function withoutInitialSystemMessage(messages: Message[]): Message[] {\n\treturn getInitialSystemMessage(messages) ? messages.slice(1) : messages;\n}\n\n/** Resolve the tools available after applying every transcript delta in order. */\nexport function getCurrentTools(messages: TranscriptMessages): Tool[] {\n\tconst tools = new Map<string, Tool>();\n\tfor (const message of messages) {\n\t\tif (!isSystemMessage(message)) continue;\n\t\tfor (const tool of message.toolsRemoved ?? []) tools.delete(tool.name);\n\t\tfor (const tool of message.toolsAdded ?? []) tools.set(tool.name, tool);\n\t}\n\treturn [...tools.values()];\n}\n\n/**\n * Replay every system message into one leading system message holding the current\n * prompt and tools. Later `content` is appended to the base prompt, `sections` are\n * patched by name, and tools are resolved with {@link getCurrentTools}.\n */\nexport function getCurrentSystemMessage(messages: TranscriptMessages): SystemMessage | undefined {\n\tconst content: string[] = [];\n\tconst sections = new Map<string, string>();\n\tlet timestamp: number | undefined;\n\tfor (const message of messages) {\n\t\tif (!isSystemMessage(message)) continue;\n\t\ttimestamp ??= message.timestamp;\n\t\tconst text = contentText(message.content);\n\t\tif (text.length > 0) content.push(text);\n\t\tfor (const [name, value] of Object.entries(message.sections ?? {})) {\n\t\t\tif (value === null) sections.delete(name);\n\t\t\telse sections.set(name, value);\n\t\t}\n\t}\n\tconst tools = getCurrentTools(messages);\n\tif (timestamp === undefined && tools.length === 0) return undefined;\n\treturn {\n\t\trole: \"system\",\n\t\tcontent: content.join(\"\\n\\n\"),\n\t\t...(sections.size > 0 ? { sections: Object.fromEntries(sections) } : {}),\n\t\t...(tools.length > 0 ? { toolsAdded: tools } : {}),\n\t\ttimestamp: timestamp ?? 0,\n\t};\n}\n\n/** Render the current system prompt text after replaying every system message. */\nexport function getCurrentSystemPrompt(messages: TranscriptMessages): string {\n\tconst message = getCurrentSystemMessage(messages);\n\treturn message ? getSystemMessageText(message) : \"\";\n}\n\n/**\n * Rebuild the transcript for APIs without mid-conversation system messages: the replayed\n * system message leads, and every later system message is dropped.\n */\nexport function collapseSystemMessages(context: TranscriptContext): TranscriptContext {\n\tconst head = getCurrentSystemMessage(context.messages);\n\tconst messages = context.messages.filter((message) => message.role !== \"system\");\n\treturn { messages: head ? [head, ...messages] : messages } as TranscriptContext;\n}\n\n/** Keep later system messages in place when the model accepts them; otherwise collapse them. */\nexport function resolveTranscript(\n\tcontext: TranscriptContext,\n\tsupportsMidConvoSystemMessages: boolean | undefined,\n): TranscriptContext {\n\treturn supportsMidConvoSystemMessages ? context : collapseSystemMessages(context);\n}\n\n/** Strip executable and display-only fields from a tool before transcript comparison or persistence. */\nexport function toToolDeclaration(tool: Tool): Tool {\n\treturn {\n\t\tname: tool.name,\n\t\tdescription: tool.description,\n\t\tparameters: JSON.parse(JSON.stringify(tool.parameters)) as Tool[\"parameters\"],\n\t\t...(tool.constrainedSampling === undefined ? {} : { constrainedSampling: tool.constrainedSampling }),\n\t};\n}\n\n/**\n * Whether two tools declare the same interface to the model.\n *\n * Both sides go through {@link toToolDeclaration} first: its JSON round-trip drops the\n * schema metadata keys (TypeBox's non-enumerable kind marks) and `undefined` fields that a structural comparison would see, and\n * builds both objects with the same key order, so comparing the serialized declarations\n * is exact. This avoids a deep-equal dependency in a browser-safe package.\n */\nexport function declarationsEqual(left: Tool, right: Tool): boolean {\n\treturn JSON.stringify(toToolDeclaration(left)) === JSON.stringify(toToolDeclaration(right));\n}\n\nexport interface ToolStateChanges {\n\ttoolsAdded: Tool[];\n\ttoolsRemoved: ToolReference[];\n}\n\n/** Compare two complete tool states. A changed definition is a removal followed by an addition. */\nexport function getToolStateChanges(previous: readonly Tool[], current: readonly Tool[]): ToolStateChanges {\n\tconst previousTools = new Map(previous.map((tool) => [tool.name, tool]));\n\tconst currentTools = new Map(current.map((tool) => [tool.name, tool]));\n\treturn {\n\t\ttoolsAdded: current\n\t\t\t.filter((tool) => {\n\t\t\t\tconst previousTool = previousTools.get(tool.name);\n\t\t\t\treturn previousTool === undefined || !declarationsEqual(previousTool, tool);\n\t\t\t})\n\t\t\t.map(toToolDeclaration),\n\t\ttoolsRemoved: previous\n\t\t\t.filter((tool) => {\n\t\t\t\tconst currentTool = currentTools.get(tool.name);\n\t\t\t\treturn currentTool === undefined || !declarationsEqual(tool, currentTool);\n\t\t\t})\n\t\t\t.map((tool) => ({ name: tool.name })),\n\t};\n}\n\n/** Every definition referenced by transcript tool state, in first-declaration order. */\nexport function getDeclaredTools(messages: TranscriptMessages): Tool[] {\n\tconst definitions = new Map<string, Tool>();\n\tfor (const message of messages) {\n\t\tif (!isSystemMessage(message)) continue;\n\t\tfor (const tool of message.toolsAdded ?? []) definitions.set(tool.name, tool);\n\t}\n\treturn [...definitions.values()];\n}\n\n/**\n * Whether a tool name was declared twice with different definitions. A transport that can\n * only reference previously declared tools by name cannot replay such a history.\n *\n * @deprecated No built-in transport needs this anymore: Anthropic expresses redefinitions with\n * inline `tool_definition` blocks. Kept for API compatibility and will be removed in a future release.\n */\nexport function hasToolRedefinitions(messages: TranscriptMessages): boolean {\n\tconst declared = new Map<string, Tool>();\n\tfor (const message of messages) {\n\t\tif (!isSystemMessage(message)) continue;\n\t\tfor (const tool of message.toolsAdded ?? []) {\n\t\t\tconst previous = declared.get(tool.name);\n\t\t\tif (previous !== undefined && !declarationsEqual(previous, tool)) return true;\n\t\t\tdeclared.set(tool.name, tool);\n\t\t}\n\t}\n\treturn false;\n}\n\n/** Whether tool history contains a removal or same-name redeclaration that an addition-only transport cannot replay. */\nexport function hasNonAdditiveToolChanges(messages: TranscriptMessages): boolean {\n\treturn findLastNonAdditiveToolChange(messages.filter(isSystemMessage)) >= 0;\n}\n\n/** Index of the last system message with a removal or same-name redeclaration, or -1. */\nfunction findLastNonAdditiveToolChange(systemMessages: readonly SystemMessage[]): number {\n\t// A name declared before counts as a redeclaration even after its removal: Kimi K3 ignores a\n\t// removed tool that a later tool message declares again (verified October 2026).\n\tconst declared = new Set<string>();\n\tlet last = -1;\n\tfor (let index = 0; index < systemMessages.length; index++) {\n\t\tconst message = systemMessages[index];\n\t\tif ((message.toolsRemoved?.length ?? 0) > 0) last = index;\n\t\tfor (const tool of message.toolsAdded ?? []) {\n\t\t\tif (declared.has(tool.name)) last = index;\n\t\t\tdeclared.add(tool.name);\n\t\t}\n\t}\n\treturn last;\n}\n\n/**\n * How a transport can express tool changes after the first request.\n *\n * - `\"none\"`: only the top-level tool list exists; it always holds the current tools.\n * - `\"additions\"`: tools can be added at a later message, but not removed or redefined.\n * - `\"changes\"`: every tool, including the initial ones, is declared in place, and a later\n * declaration under the same name replaces the earlier one. Removals are not expressible on\n * the wire, so the transport announces them as text and the removed tools stay declared.\n */\nexport type TranscriptToolSupport = \"none\" | \"additions\" | \"changes\";\n\nexport interface TranscriptTools {\n\t/** Tools sent in the top-level request field. */\n\trequestTools: Tool[];\n\t/**\n\t * Number of leading system messages whose tool changes `requestTools` already holds. Every\n\t * later system message carries its own tool changes in place.\n\t */\n\tfoldedSystemMessages: number;\n}\n\n/**\n * Split tool declarations between the top-level request field and in-place changes.\n *\n * With `\"changes\"`, the top-level list stays empty and every system message, including the\n * leading one, declares its tools in place: an in-place declaration can replace an earlier\n * in-place one, but not a top-level one. With `\"additions\"`, a removal or redefinition cannot be\n * expressed in place, so the top-level list becomes the tool state after the last such change;\n * later additions stay anchored, and the cached prefix is invalidated once per removal instead\n * of on every later addition. With `\"none\"`, the top-level list is the current tool set.\n */\nexport function resolveTranscriptTools(messages: TranscriptMessages, support: TranscriptToolSupport): TranscriptTools {\n\tconst systemMessages = messages.filter(isSystemMessage);\n\tif (support === \"none\") {\n\t\treturn { requestTools: getCurrentTools(systemMessages), foldedSystemMessages: systemMessages.length };\n\t}\n\tif (support === \"changes\") return { requestTools: [], foldedSystemMessages: 0 };\n\tconst folded = Math.max(\n\t\tgetInitialSystemMessage(messages) ? 1 : 0,\n\t\tfindLastNonAdditiveToolChange(systemMessages) + 1,\n\t);\n\treturn { requestTools: getCurrentTools(systemMessages.slice(0, folded)), foldedSystemMessages: folded };\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { Tool, ToolCall } from "../types.ts";
2
2
  /**
3
- * Finds a tool by name and validates the tool call arguments against its TypeBox schema
3
+ * Finds a tool by name and validates the tool call arguments against its JSON Schema
4
4
  * @param tools Array of tool definitions
5
5
  * @param toolCall The tool call from the LLM
6
6
  * @returns The validated arguments
@@ -8,8 +8,10 @@ import type { Tool, ToolCall } from "../types.ts";
8
8
  */
9
9
  export declare function validateToolCall(tools: Tool[], toolCall: ToolCall): any;
10
10
  /**
11
- * Validates tool call arguments against the tool's TypeBox schema
12
- * @param tool The tool definition with TypeBox schema
11
+ * Validates tool call arguments against the tool's JSON Schema (any JSON Schema: pi-schema's builders, TypeBox, plain
12
+ * objects). The arguments are coerced first (pi-schema's `parse()`): models often send `"42"` for a number, a single
13
+ * value for an array, or `null` for an omitted optional property.
14
+ * @param tool The tool definition with its JSON Schema
13
15
  * @param toolCall The tool call from the LLM
14
16
  * @returns The validated (and potentially coerced) arguments
15
17
  * @throws Error with formatted message if validation fails
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAmSlD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,QAAQ,GAAG,GAAG,CAMvE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,GAAG,GAAG,CAoCzE"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA2BlD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,QAAQ,GAAG,GAAG,CAMvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,GAAG,GAAG,CAezE"}
@@ -1,263 +1,30 @@
1
- import { Compile } from "typebox/compile";
2
- import { Value } from "typebox/value";
1
+ import { compile } from "@earendil-works/pi-schema";
2
+ import { formats } from "@earendil-works/pi-schema/formats";
3
+ // One compiled validator per schema object: tool schemas are long-lived, so the WeakMap drops entries with their tools.
3
4
  const validatorCache = new WeakMap();
4
- const TYPEBOX_KIND = Symbol.for("TypeBox.Kind");
5
- function getSchemaTypes(schema) {
6
- if (typeof schema.type === "string") {
7
- return [schema.type];
8
- }
9
- if (Array.isArray(schema.type)) {
10
- return schema.type.filter((type) => typeof type === "string");
11
- }
12
- return [];
13
- }
14
- function matchesJsonType(value, type) {
15
- switch (type) {
16
- case "number":
17
- return typeof value === "number";
18
- case "integer":
19
- return typeof value === "number" && Number.isInteger(value);
20
- case "boolean":
21
- return typeof value === "boolean";
22
- case "string":
23
- return typeof value === "string";
24
- case "null":
25
- return value === null;
26
- case "array":
27
- return Array.isArray(value);
28
- case "object":
29
- return typeof value === "object" && value !== null && !Array.isArray(value);
30
- default:
31
- return false;
32
- }
33
- }
34
- function getSubSchemaValidator(schema) {
35
- try {
36
- return getValidator(schema);
37
- }
38
- catch {
39
- return undefined;
40
- }
41
- }
42
- function coercePrimitiveByType(value, type) {
43
- switch (type) {
44
- case "number": {
45
- if (value === null) {
46
- return 0;
47
- }
48
- if (typeof value === "string" && value.trim() !== "") {
49
- const parsed = Number(value);
50
- if (Number.isFinite(parsed)) {
51
- return parsed;
52
- }
53
- }
54
- if (typeof value === "boolean") {
55
- return value ? 1 : 0;
56
- }
57
- return value;
58
- }
59
- case "integer": {
60
- if (value === null) {
61
- return 0;
62
- }
63
- if (typeof value === "string" && value.trim() !== "") {
64
- const parsed = Number(value);
65
- if (Number.isInteger(parsed)) {
66
- return parsed;
67
- }
68
- }
69
- if (typeof value === "boolean") {
70
- return value ? 1 : 0;
71
- }
72
- return value;
73
- }
74
- case "boolean": {
75
- if (value === null) {
76
- return false;
77
- }
78
- if (typeof value === "string") {
79
- if (value === "true") {
80
- return true;
81
- }
82
- if (value === "false") {
83
- return false;
84
- }
85
- }
86
- if (typeof value === "number") {
87
- if (value === 1) {
88
- return true;
89
- }
90
- if (value === 0) {
91
- return false;
92
- }
93
- }
94
- return value;
95
- }
96
- case "string": {
97
- if (value === null) {
98
- return "";
99
- }
100
- if (typeof value === "number" || typeof value === "boolean") {
101
- return String(value);
102
- }
103
- return value;
104
- }
105
- case "null": {
106
- if (value === "" || value === 0 || value === false) {
107
- return null;
108
- }
109
- return value;
110
- }
111
- default:
112
- return value;
113
- }
114
- }
115
- function applySchemaObjectCoercion(value, schema) {
116
- const properties = schema.properties;
117
- const definedKeys = new Set(properties ? Object.keys(properties) : []);
118
- if (properties) {
119
- for (const [key, propertySchema] of Object.entries(properties)) {
120
- if (!(key in value)) {
121
- continue;
122
- }
123
- value[key] = coerceWithJsonSchema(value[key], propertySchema);
124
- }
125
- }
126
- if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
127
- for (const [key, propertyValue] of Object.entries(value)) {
128
- if (definedKeys.has(key)) {
129
- continue;
130
- }
131
- value[key] = coerceWithJsonSchema(propertyValue, schema.additionalProperties);
132
- }
133
- }
134
- }
135
- function applySchemaArrayCoercion(value, schema) {
136
- if (Array.isArray(schema.items)) {
137
- for (let index = 0; index < value.length; index++) {
138
- const itemSchema = schema.items[index];
139
- if (!itemSchema) {
140
- continue;
141
- }
142
- value[index] = coerceWithJsonSchema(value[index], itemSchema);
143
- }
144
- return;
145
- }
146
- if (schema.items && typeof schema.items === "object") {
147
- for (let index = 0; index < value.length; index++) {
148
- value[index] = coerceWithJsonSchema(value[index], schema.items);
149
- }
150
- }
151
- }
152
- function coerceWithUnionSchema(value, schemas) {
153
- for (const schema of schemas) {
154
- const validator = getSubSchemaValidator(schema);
155
- if (validator?.Check(value)) {
156
- return value;
157
- }
158
- }
159
- for (const schema of schemas) {
160
- const candidate = structuredClone(value);
161
- const coerced = coerceWithJsonSchema(candidate, schema);
162
- const validator = getSubSchemaValidator(schema);
163
- if (validator?.Check(coerced)) {
164
- return coerced;
165
- }
166
- }
167
- return value;
168
- }
169
- function coerceWithJsonSchema(value, schema) {
170
- let nextValue = value;
171
- if (Array.isArray(schema.allOf)) {
172
- for (const nested of schema.allOf) {
173
- nextValue = coerceWithJsonSchema(nextValue, nested);
174
- }
175
- }
176
- if (Array.isArray(schema.anyOf)) {
177
- nextValue = coerceWithUnionSchema(nextValue, schema.anyOf);
178
- }
179
- if (Array.isArray(schema.oneOf)) {
180
- nextValue = coerceWithUnionSchema(nextValue, schema.oneOf);
181
- }
182
- const schemaTypes = getSchemaTypes(schema);
183
- const matchesUnionMember = schemaTypes.length > 1 && schemaTypes.some((schemaType) => matchesJsonType(nextValue, schemaType));
184
- if (schemaTypes.length > 0 && !matchesUnionMember) {
185
- for (const schemaType of schemaTypes) {
186
- const candidate = coercePrimitiveByType(nextValue, schemaType);
187
- if (candidate !== nextValue) {
188
- nextValue = candidate;
189
- break;
190
- }
191
- }
192
- }
193
- if (schemaTypes.includes("object") &&
194
- typeof nextValue === "object" &&
195
- nextValue !== null &&
196
- !Array.isArray(nextValue)) {
197
- applySchemaObjectCoercion(nextValue, schema);
198
- }
199
- if (schemaTypes.includes("array") && Array.isArray(nextValue)) {
200
- applySchemaArrayCoercion(nextValue, schema);
201
- }
202
- return nextValue;
203
- }
204
- function normalizeOptionalNulls(value, schema) {
205
- if (Array.isArray(value)) {
206
- if (Array.isArray(schema.items)) {
207
- for (let index = 0; index < value.length; index++) {
208
- const itemSchema = schema.items[index];
209
- if (itemSchema)
210
- normalizeOptionalNulls(value[index], itemSchema);
211
- }
212
- }
213
- else if (schema.items) {
214
- for (const item of value)
215
- normalizeOptionalNulls(item, schema.items);
216
- }
217
- return;
218
- }
219
- if (typeof value !== "object" || value === null || !schema.properties)
220
- return;
221
- const object = value;
222
- const required = new Set(schema.required ?? []);
223
- for (const [key, propertySchema] of Object.entries(schema.properties)) {
224
- if (!(key in object))
225
- continue;
226
- if (object[key] === null &&
227
- !required.has(key) &&
228
- typeof propertySchema.$ref !== "string" &&
229
- getSubSchemaValidator(propertySchema)?.Check(null) === false) {
230
- delete object[key];
231
- }
232
- else {
233
- normalizeOptionalNulls(object[key], propertySchema);
234
- }
235
- }
236
- }
237
5
  function getValidator(schema) {
238
- const key = schema;
239
- const cached = validatorCache.get(key);
6
+ const cached = validatorCache.get(schema);
240
7
  if (cached) {
241
8
  return cached;
242
9
  }
243
- const validator = Compile(schema);
244
- validatorCache.set(key, validator);
10
+ // A $ref that names nothing (a dangling definition in an MCP server's schema) rejects only values at its place,
11
+ // instead of making every call of the tool fail.
12
+ const validator = compile(schema, { formats, coerce: true, unresolvedRefs: "reject" });
13
+ validatorCache.set(schema, validator);
245
14
  return validator;
246
15
  }
247
- function formatValidationPath(error) {
248
- if (error.keyword === "required") {
249
- const requiredProperties = error.params.requiredProperties;
250
- const requiredProperty = requiredProperties?.[0];
251
- if (requiredProperty) {
252
- const basePath = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
253
- return basePath ? `${basePath}.${requiredProperty}` : requiredProperty;
254
- }
255
- }
256
- const path = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
257
- return path || "root";
16
+ /** A pi-schema error path (a JSON Pointer) as the dotted path the model sees: `/edits/0/oldText` -> `edits.0.oldText`. */
17
+ function formatValidationPath(pointer) {
18
+ if (pointer === "")
19
+ return "root";
20
+ return pointer
21
+ .slice(1)
22
+ .split("/")
23
+ .map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"))
24
+ .join(".");
258
25
  }
259
26
  /**
260
- * Finds a tool by name and validates the tool call arguments against its TypeBox schema
27
+ * Finds a tool by name and validates the tool call arguments against its JSON Schema
261
28
  * @param tools Array of tool definitions
262
29
  * @param toolCall The tool call from the LLM
263
30
  * @returns The validated arguments
@@ -271,8 +38,10 @@ export function validateToolCall(tools, toolCall) {
271
38
  return validateToolArguments(tool, toolCall);
272
39
  }
273
40
  /**
274
- * Validates tool call arguments against the tool's TypeBox schema
275
- * @param tool The tool definition with TypeBox schema
41
+ * Validates tool call arguments against the tool's JSON Schema (any JSON Schema: pi-schema's builders, TypeBox, plain
42
+ * objects). The arguments are coerced first (pi-schema's `parse()`): models often send `"42"` for a number, a single
43
+ * value for an array, or `null` for an omitted optional property.
44
+ * @param tool The tool definition with its JSON Schema
276
45
  * @param toolCall The tool call from the LLM
277
46
  * @returns The validated (and potentially coerced) arguments
278
47
  * @throws Error with formatted message if validation fails
@@ -281,31 +50,12 @@ export function validateToolArguments(tool, toolCall) {
281
50
  if (toolCall.argumentsError) {
282
51
  throw new Error(`Invalid arguments for tool "${toolCall.name}": ${toolCall.argumentsError}`);
283
52
  }
284
- const args = structuredClone(toolCall.arguments);
285
- normalizeOptionalNulls(args, tool.parameters);
286
- Value.Convert(tool.parameters, args);
287
- const validator = getValidator(tool.parameters);
288
- if (!Object.getOwnPropertySymbols(tool.parameters).includes(TYPEBOX_KIND)) {
289
- const coerced = coerceWithJsonSchema(args, tool.parameters);
290
- if (coerced !== args) {
291
- if (typeof args === "object" && args !== null && typeof coerced === "object" && coerced !== null) {
292
- for (const key of Object.keys(args)) {
293
- delete args[key];
294
- }
295
- Object.assign(args, coerced);
296
- }
297
- else {
298
- return validator.Check(coerced) ? coerced : args;
299
- }
300
- }
301
- }
302
- if (validator.Check(args)) {
303
- return args;
53
+ // A copy, so tools that modify their arguments never modify the tool call in the transcript.
54
+ const result = getValidator(tool.parameters).parse(structuredClone(toolCall.arguments));
55
+ if (result.ok) {
56
+ return result.value;
304
57
  }
305
- const errors = validator
306
- .Errors(args)
307
- .map((error) => ` - ${formatValidationPath(error)}: ${error.message}`)
308
- .join("\n") || "Unknown validation error";
58
+ const errors = result.errors.map((error) => ` - ${formatValidationPath(error.path)}: ${error.message}`).join("\n");
309
59
  const errorMessage = `Validation failed for tool "${toolCall.name}":\n${errors}\n\nReceived arguments:\n${JSON.stringify(toolCall.arguments, null, 2)}`;
310
60
  throw new Error(errorMessage);
311
61
  }