@averyyy/pi-ai 0.87.1-piclient.2 → 0.99.1-piclient.1

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 (626) hide show
  1. package/README.md +207 -27
  2. package/dist/api/anthropic-messages.d.ts.map +1 -1
  3. package/dist/api/anthropic-messages.js +6 -0
  4. package/dist/api/anthropic-messages.js.map +1 -1
  5. package/dist/api/anthropic-messages.lazy.d.ts.map +1 -1
  6. package/dist/api/azure-openai-responses.d.ts.map +1 -1
  7. package/dist/api/azure-openai-responses.js +6 -5
  8. package/dist/api/azure-openai-responses.js.map +1 -1
  9. package/dist/api/azure-openai-responses.lazy.d.ts.map +1 -1
  10. package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  11. package/dist/api/bedrock-converse-stream.js +1 -0
  12. package/dist/api/bedrock-converse-stream.js.map +1 -1
  13. package/dist/api/bedrock-converse-stream.lazy.d.ts.map +1 -1
  14. package/dist/api/bedrock-converse-stream.lazy.js.map +1 -1
  15. package/dist/api/cloudflare-ai-binding.d.ts.map +1 -1
  16. package/dist/api/cloudflare-ai-binding.js.map +1 -1
  17. package/dist/api/cloudflare-workers-ai-system-one.d.ts +4 -0
  18. package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -0
  19. package/dist/api/cloudflare-workers-ai-system-one.js +43 -0
  20. package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -0
  21. package/dist/api/cloudflare-workers-ai-system-one.lazy.d.ts +3 -0
  22. package/dist/api/cloudflare-workers-ai-system-one.lazy.d.ts.map +1 -0
  23. package/dist/api/cloudflare-workers-ai-system-one.lazy.js +4 -0
  24. package/dist/api/cloudflare-workers-ai-system-one.lazy.js.map +1 -0
  25. package/dist/api/cloudflare.d.ts +2 -0
  26. package/dist/api/cloudflare.d.ts.map +1 -1
  27. package/dist/api/cloudflare.js +2 -0
  28. package/dist/api/cloudflare.js.map +1 -1
  29. package/dist/api/constrained-sampling.d.ts.map +1 -1
  30. package/dist/api/constrained-sampling.js.map +1 -1
  31. package/dist/api/devin-catalog.d.ts.map +1 -1
  32. package/dist/api/devin-catalog.js.map +1 -1
  33. package/dist/api/devin-constants.d.ts.map +1 -1
  34. package/dist/api/devin-context-map.d.ts.map +1 -1
  35. package/dist/api/devin-context-map.js.map +1 -1
  36. package/dist/api/devin-jwt.d.ts.map +1 -1
  37. package/dist/api/devin-jwt.js.map +1 -1
  38. package/dist/api/devin-metadata.d.ts.map +1 -1
  39. package/dist/api/devin-metadata.js.map +1 -1
  40. package/dist/api/devin-stream.d.ts.map +1 -1
  41. package/dist/api/devin-stream.js.map +1 -1
  42. package/dist/api/devin-thinking.d.ts.map +1 -1
  43. package/dist/api/devin-thinking.js.map +1 -1
  44. package/dist/api/devin-wire.d.ts.map +1 -1
  45. package/dist/api/devin-wire.js.map +1 -1
  46. package/dist/api/devin.d.ts.map +1 -1
  47. package/dist/api/github-copilot-headers.d.ts.map +1 -1
  48. package/dist/api/github-copilot-headers.js.map +1 -1
  49. package/dist/api/google-generative-ai.d.ts.map +1 -1
  50. package/dist/api/google-generative-ai.js +1 -0
  51. package/dist/api/google-generative-ai.js.map +1 -1
  52. package/dist/api/google-generative-ai.lazy.d.ts.map +1 -1
  53. package/dist/api/google-shared.d.ts.map +1 -1
  54. package/dist/api/google-shared.js.map +1 -1
  55. package/dist/api/google-vertex.d.ts.map +1 -1
  56. package/dist/api/google-vertex.js +1 -0
  57. package/dist/api/google-vertex.js.map +1 -1
  58. package/dist/api/google-vertex.lazy.d.ts.map +1 -1
  59. package/dist/api/lazy.d.ts.map +1 -1
  60. package/dist/api/lazy.js.map +1 -1
  61. package/dist/api/llama-cpp-classify.d.ts +33 -0
  62. package/dist/api/llama-cpp-classify.d.ts.map +1 -0
  63. package/dist/api/llama-cpp-classify.js +365 -0
  64. package/dist/api/llama-cpp-classify.js.map +1 -0
  65. package/dist/api/llama-cpp-classify.lazy.d.ts +3 -0
  66. package/dist/api/llama-cpp-classify.lazy.d.ts.map +1 -0
  67. package/dist/api/llama-cpp-classify.lazy.js +4 -0
  68. package/dist/api/llama-cpp-classify.lazy.js.map +1 -0
  69. package/dist/api/mistral-conversations.d.ts +1 -1
  70. package/dist/api/mistral-conversations.d.ts.map +1 -1
  71. package/dist/api/mistral-conversations.js +18 -17
  72. package/dist/api/mistral-conversations.js.map +1 -1
  73. package/dist/api/mistral-conversations.lazy.d.ts.map +1 -1
  74. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  75. package/dist/api/openai-codex-responses.js +20 -4
  76. package/dist/api/openai-codex-responses.js.map +1 -1
  77. package/dist/api/openai-codex-responses.lazy.d.ts.map +1 -1
  78. package/dist/api/openai-completions.d.ts.map +1 -1
  79. package/dist/api/openai-completions.js +4 -4
  80. package/dist/api/openai-completions.js.map +1 -1
  81. package/dist/api/openai-completions.lazy.d.ts.map +1 -1
  82. package/dist/api/openai-prompt-cache.d.ts.map +1 -1
  83. package/dist/api/openai-prompt-cache.js.map +1 -1
  84. package/dist/api/openai-responses-shared.d.ts +2 -1
  85. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  86. package/dist/api/openai-responses-shared.js +14 -0
  87. package/dist/api/openai-responses-shared.js.map +1 -1
  88. package/dist/api/openai-responses.d.ts.map +1 -1
  89. package/dist/api/openai-responses.js +26 -9
  90. package/dist/api/openai-responses.js.map +1 -1
  91. package/dist/api/openai-responses.lazy.d.ts.map +1 -1
  92. package/dist/api/openrouter-images.d.ts +2 -1
  93. package/dist/api/openrouter-images.d.ts.map +1 -1
  94. package/dist/api/openrouter-images.js +1 -0
  95. package/dist/api/openrouter-images.js.map +1 -1
  96. package/dist/api/openrouter-images.lazy.d.ts.map +1 -1
  97. package/dist/api/openrouter-images.lazy.js.map +1 -1
  98. package/dist/api/pi-messages.d.ts.map +1 -1
  99. package/dist/api/pi-messages.js +1 -0
  100. package/dist/api/pi-messages.js.map +1 -1
  101. package/dist/api/pi-messages.lazy.d.ts.map +1 -1
  102. package/dist/api/simple-options.d.ts.map +1 -1
  103. package/dist/api/simple-options.js +2 -4
  104. package/dist/api/simple-options.js.map +1 -1
  105. package/dist/api/system-one-shared.d.ts +23 -0
  106. package/dist/api/system-one-shared.d.ts.map +1 -0
  107. package/dist/api/system-one-shared.js +183 -0
  108. package/dist/api/system-one-shared.js.map +1 -0
  109. package/dist/api/transform-messages.d.ts.map +1 -1
  110. package/dist/api/transform-messages.js.map +1 -1
  111. package/dist/api/typesafe-system-one.d.ts +4 -0
  112. package/dist/api/typesafe-system-one.d.ts.map +1 -0
  113. package/dist/api/typesafe-system-one.js +19 -0
  114. package/dist/api/typesafe-system-one.js.map +1 -0
  115. package/dist/api/typesafe-system-one.lazy.d.ts +3 -0
  116. package/dist/api/typesafe-system-one.lazy.d.ts.map +1 -0
  117. package/dist/api/typesafe-system-one.lazy.js +4 -0
  118. package/dist/api/typesafe-system-one.lazy.js.map +1 -0
  119. package/dist/auth/context.d.ts.map +1 -1
  120. package/dist/auth/context.js.map +1 -1
  121. package/dist/auth/credential-store.d.ts.map +1 -1
  122. package/dist/auth/credential-store.js.map +1 -1
  123. package/dist/auth/helpers.d.ts.map +1 -1
  124. package/dist/auth/helpers.js +1 -1
  125. package/dist/auth/helpers.js.map +1 -1
  126. package/dist/auth/oauth/anthropic.d.ts.map +1 -1
  127. package/dist/auth/oauth/anthropic.js +19 -128
  128. package/dist/auth/oauth/anthropic.js.map +1 -1
  129. package/dist/auth/oauth/callback-server.d.ts +55 -0
  130. package/dist/auth/oauth/callback-server.d.ts.map +1 -0
  131. package/dist/auth/oauth/callback-server.js +146 -0
  132. package/dist/auth/oauth/callback-server.js.map +1 -0
  133. package/dist/auth/oauth/device-code.d.ts.map +1 -1
  134. package/dist/auth/oauth/device-code.js.map +1 -1
  135. package/dist/auth/oauth/devin-runtime.d.ts.map +1 -1
  136. package/dist/auth/oauth/devin.d.ts.map +1 -1
  137. package/dist/auth/oauth/devin.js.map +1 -1
  138. package/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  139. package/dist/auth/oauth/github-copilot.js.map +1 -1
  140. package/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
  141. package/dist/auth/oauth/kimi-coding.js.map +1 -1
  142. package/dist/auth/oauth/load.d.ts +2 -0
  143. package/dist/auth/oauth/load.d.ts.map +1 -1
  144. package/dist/auth/oauth/load.js +5 -0
  145. package/dist/auth/oauth/load.js.map +1 -1
  146. package/dist/auth/oauth/meta.d.ts.map +1 -1
  147. package/dist/auth/oauth/meta.js.map +1 -1
  148. package/dist/auth/oauth/openai-chatgpt.d.ts +9 -0
  149. package/dist/auth/oauth/openai-chatgpt.d.ts.map +1 -0
  150. package/dist/auth/oauth/openai-chatgpt.js +266 -0
  151. package/dist/auth/oauth/openai-chatgpt.js.map +1 -0
  152. package/dist/auth/oauth/openai-codex.d.ts +1 -1
  153. package/dist/auth/oauth/openai-codex.d.ts.map +1 -1
  154. package/dist/auth/oauth/openai-codex.js +20 -124
  155. package/dist/auth/oauth/openai-codex.js.map +1 -1
  156. package/dist/auth/oauth/openrouter.d.ts +1 -1
  157. package/dist/auth/oauth/openrouter.d.ts.map +1 -1
  158. package/dist/auth/oauth/openrouter.js +19 -138
  159. package/dist/auth/oauth/openrouter.js.map +1 -1
  160. package/dist/auth/oauth/pkce.d.ts.map +1 -1
  161. package/dist/auth/oauth/pkce.js.map +1 -1
  162. package/dist/auth/oauth/radius.d.ts +1 -1
  163. package/dist/auth/oauth/radius.d.ts.map +1 -1
  164. package/dist/auth/oauth/radius.js +21 -89
  165. package/dist/auth/oauth/radius.js.map +1 -1
  166. package/dist/auth/oauth/xai.d.ts.map +1 -1
  167. package/dist/auth/oauth/xai.js.map +1 -1
  168. package/dist/auth/resolve.d.ts +2 -8
  169. package/dist/auth/resolve.d.ts.map +1 -1
  170. package/dist/auth/resolve.js +3 -19
  171. package/dist/auth/resolve.js.map +1 -1
  172. package/dist/auth/types.d.ts +10 -1
  173. package/dist/auth/types.d.ts.map +1 -1
  174. package/dist/auth/types.js.map +1 -1
  175. package/dist/bedrock-provider.d.ts.map +1 -1
  176. package/dist/bun-oauth.d.ts.map +1 -1
  177. package/dist/bun-oauth.js +2 -0
  178. package/dist/bun-oauth.js.map +1 -1
  179. package/dist/cli.d.ts.map +1 -1
  180. package/dist/cli.js +3 -1
  181. package/dist/cli.js.map +1 -1
  182. package/dist/compat/extension-oauth-types.d.ts.map +1 -1
  183. package/dist/compat.d.ts.map +1 -1
  184. package/dist/compat.js.map +1 -1
  185. package/dist/env-api-keys.d.ts.map +1 -1
  186. package/dist/env-api-keys.js +1 -0
  187. package/dist/env-api-keys.js.map +1 -1
  188. package/dist/image-models.d.ts +19 -8
  189. package/dist/image-models.d.ts.map +1 -1
  190. package/dist/image-models.js +14 -13
  191. package/dist/image-models.js.map +1 -1
  192. package/dist/images-api-registry.d.ts +7 -7
  193. package/dist/images-api-registry.d.ts.map +1 -1
  194. package/dist/images-api-registry.js.map +1 -1
  195. package/dist/images.d.ts +7 -2
  196. package/dist/images.d.ts.map +1 -1
  197. package/dist/images.js +5 -0
  198. package/dist/images.js.map +1 -1
  199. package/dist/index.d.ts +0 -1
  200. package/dist/index.d.ts.map +1 -1
  201. package/dist/index.js +0 -1
  202. package/dist/index.js.map +1 -1
  203. package/dist/legacy-api-aliases.d.ts.map +1 -1
  204. package/dist/model-catalog.d.ts +25 -9
  205. package/dist/model-catalog.d.ts.map +1 -1
  206. package/dist/model-catalog.js +14 -2
  207. package/dist/model-catalog.js.map +1 -1
  208. package/dist/models-store.d.ts +3 -2
  209. package/dist/models-store.d.ts.map +1 -1
  210. package/dist/models-store.js.map +1 -1
  211. package/dist/models.d.ts +97 -30
  212. package/dist/models.d.ts.map +1 -1
  213. package/dist/models.generated.d.ts +131 -41
  214. package/dist/models.generated.d.ts.map +1 -1
  215. package/dist/models.generated.js +131 -41
  216. package/dist/models.generated.js.map +1 -1
  217. package/dist/models.js +157 -30
  218. package/dist/models.js.map +1 -1
  219. package/dist/oauth.d.ts.map +1 -1
  220. package/dist/providers/all.d.ts +19 -13
  221. package/dist/providers/all.d.ts.map +1 -1
  222. package/dist/providers/all.js +25 -21
  223. package/dist/providers/all.js.map +1 -1
  224. package/dist/providers/amazon-bedrock.d.ts.map +1 -1
  225. package/dist/providers/amazon-bedrock.js.map +1 -1
  226. package/dist/providers/amazon-bedrock.models.d.ts +4 -2
  227. package/dist/providers/amazon-bedrock.models.d.ts.map +1 -1
  228. package/dist/providers/amazon-bedrock.models.js +4 -2
  229. package/dist/providers/amazon-bedrock.models.js.map +1 -1
  230. package/dist/providers/ant-ling.d.ts.map +1 -1
  231. package/dist/providers/ant-ling.js.map +1 -1
  232. package/dist/providers/ant-ling.models.d.ts +4 -2
  233. package/dist/providers/ant-ling.models.d.ts.map +1 -1
  234. package/dist/providers/ant-ling.models.js +4 -2
  235. package/dist/providers/ant-ling.models.js.map +1 -1
  236. package/dist/providers/anthropic.d.ts.map +1 -1
  237. package/dist/providers/anthropic.js.map +1 -1
  238. package/dist/providers/anthropic.models.d.ts +4 -2
  239. package/dist/providers/anthropic.models.d.ts.map +1 -1
  240. package/dist/providers/anthropic.models.js +4 -2
  241. package/dist/providers/anthropic.models.js.map +1 -1
  242. package/dist/providers/azure-openai-responses.d.ts.map +1 -1
  243. package/dist/providers/azure-openai-responses.js.map +1 -1
  244. package/dist/providers/azure-openai-responses.models.d.ts +4 -2
  245. package/dist/providers/azure-openai-responses.models.d.ts.map +1 -1
  246. package/dist/providers/azure-openai-responses.models.js +4 -2
  247. package/dist/providers/azure-openai-responses.models.js.map +1 -1
  248. package/dist/providers/baseten.d.ts.map +1 -1
  249. package/dist/providers/baseten.js.map +1 -1
  250. package/dist/providers/baseten.models.d.ts +4 -2
  251. package/dist/providers/baseten.models.d.ts.map +1 -1
  252. package/dist/providers/baseten.models.js +4 -2
  253. package/dist/providers/baseten.models.js.map +1 -1
  254. package/dist/providers/cerebras.d.ts.map +1 -1
  255. package/dist/providers/cerebras.js.map +1 -1
  256. package/dist/providers/cerebras.models.d.ts +4 -2
  257. package/dist/providers/cerebras.models.d.ts.map +1 -1
  258. package/dist/providers/cerebras.models.js +4 -2
  259. package/dist/providers/cerebras.models.js.map +1 -1
  260. package/dist/providers/cloudflare-ai-gateway.d.ts.map +1 -1
  261. package/dist/providers/cloudflare-ai-gateway.js.map +1 -1
  262. package/dist/providers/cloudflare-ai-gateway.models.d.ts +4 -2
  263. package/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -1
  264. package/dist/providers/cloudflare-ai-gateway.models.js +4 -2
  265. package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
  266. package/dist/providers/cloudflare-auth.d.ts.map +1 -1
  267. package/dist/providers/cloudflare-auth.js.map +1 -1
  268. package/dist/providers/cloudflare-stream.d.ts +6 -2
  269. package/dist/providers/cloudflare-stream.d.ts.map +1 -1
  270. package/dist/providers/cloudflare-stream.js +6 -0
  271. package/dist/providers/cloudflare-stream.js.map +1 -1
  272. package/dist/providers/cloudflare-workers-ai.d.ts.map +1 -1
  273. package/dist/providers/cloudflare-workers-ai.js +10 -3
  274. package/dist/providers/cloudflare-workers-ai.js.map +1 -1
  275. package/dist/providers/cloudflare-workers-ai.models.d.ts +4 -2
  276. package/dist/providers/cloudflare-workers-ai.models.d.ts.map +1 -1
  277. package/dist/providers/cloudflare-workers-ai.models.js +4 -2
  278. package/dist/providers/cloudflare-workers-ai.models.js.map +1 -1
  279. package/dist/providers/data/.manifest.json +1 -1
  280. package/dist/providers/data/amazon-bedrock.json +1 -1
  281. package/dist/providers/data/ant-ling.json +1 -1
  282. package/dist/providers/data/anthropic.json +1 -1
  283. package/dist/providers/data/azure-openai-responses.json +1 -1
  284. package/dist/providers/data/baseten.json +1 -1
  285. package/dist/providers/data/cerebras.json +1 -1
  286. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  287. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  288. package/dist/providers/data/deepseek.json +1 -1
  289. package/dist/providers/data/fireworks.json +1 -1
  290. package/dist/providers/data/github-copilot.json +1 -1
  291. package/dist/providers/data/google-vertex.json +1 -1
  292. package/dist/providers/data/google.json +1 -1
  293. package/dist/providers/data/groq.json +1 -1
  294. package/dist/providers/data/huggingface.json +1 -1
  295. package/dist/providers/data/kimi-coding.json +1 -1
  296. package/dist/providers/data/meta.json +1 -1
  297. package/dist/providers/data/minimax-cn.json +1 -1
  298. package/dist/providers/data/minimax.json +1 -1
  299. package/dist/providers/data/mistral.json +1 -1
  300. package/dist/providers/data/moonshotai-cn.json +1 -1
  301. package/dist/providers/data/moonshotai.json +1 -1
  302. package/dist/providers/data/nvidia.json +1 -1
  303. package/dist/providers/data/openai-codex.json +1 -1
  304. package/dist/providers/data/openai.json +1 -1
  305. package/dist/providers/data/opencode-go.json +1 -1
  306. package/dist/providers/data/opencode.json +1 -1
  307. package/dist/providers/data/openrouter.json +1 -1
  308. package/dist/providers/data/qwen-token-plan-cn.json +1 -1
  309. package/dist/providers/data/qwen-token-plan-individual.json +1 -1
  310. package/dist/providers/data/qwen-token-plan.json +1 -1
  311. package/dist/providers/data/radius.json +1 -1
  312. package/dist/providers/data/together.json +1 -1
  313. package/dist/providers/data/typesafe.json +1 -0
  314. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  315. package/dist/providers/data/xai.json +1 -1
  316. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  317. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  318. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  319. package/dist/providers/data/xiaomi.json +1 -1
  320. package/dist/providers/data/zai-coding-cn.json +1 -1
  321. package/dist/providers/data/zai.json +1 -1
  322. package/dist/providers/deepseek.d.ts.map +1 -1
  323. package/dist/providers/deepseek.js.map +1 -1
  324. package/dist/providers/deepseek.models.d.ts +4 -2
  325. package/dist/providers/deepseek.models.d.ts.map +1 -1
  326. package/dist/providers/deepseek.models.js +4 -2
  327. package/dist/providers/deepseek.models.js.map +1 -1
  328. package/dist/providers/devin.d.ts.map +1 -1
  329. package/dist/providers/devin.js.map +1 -1
  330. package/dist/providers/faux.d.ts +2 -2
  331. package/dist/providers/faux.d.ts.map +1 -1
  332. package/dist/providers/faux.js.map +1 -1
  333. package/dist/providers/fireworks.d.ts.map +1 -1
  334. package/dist/providers/fireworks.js.map +1 -1
  335. package/dist/providers/fireworks.models.d.ts +4 -2
  336. package/dist/providers/fireworks.models.d.ts.map +1 -1
  337. package/dist/providers/fireworks.models.js +4 -2
  338. package/dist/providers/fireworks.models.js.map +1 -1
  339. package/dist/providers/github-copilot.d.ts.map +1 -1
  340. package/dist/providers/github-copilot.js.map +1 -1
  341. package/dist/providers/github-copilot.models.d.ts +4 -2
  342. package/dist/providers/github-copilot.models.d.ts.map +1 -1
  343. package/dist/providers/github-copilot.models.js +4 -2
  344. package/dist/providers/github-copilot.models.js.map +1 -1
  345. package/dist/providers/google-vertex.d.ts.map +1 -1
  346. package/dist/providers/google-vertex.js.map +1 -1
  347. package/dist/providers/google-vertex.models.d.ts +4 -2
  348. package/dist/providers/google-vertex.models.d.ts.map +1 -1
  349. package/dist/providers/google-vertex.models.js +4 -2
  350. package/dist/providers/google-vertex.models.js.map +1 -1
  351. package/dist/providers/google.d.ts.map +1 -1
  352. package/dist/providers/google.js.map +1 -1
  353. package/dist/providers/google.models.d.ts +4 -2
  354. package/dist/providers/google.models.d.ts.map +1 -1
  355. package/dist/providers/google.models.js +4 -2
  356. package/dist/providers/google.models.js.map +1 -1
  357. package/dist/providers/groq.d.ts.map +1 -1
  358. package/dist/providers/groq.js.map +1 -1
  359. package/dist/providers/groq.models.d.ts +4 -2
  360. package/dist/providers/groq.models.d.ts.map +1 -1
  361. package/dist/providers/groq.models.js +4 -2
  362. package/dist/providers/groq.models.js.map +1 -1
  363. package/dist/providers/huggingface.d.ts.map +1 -1
  364. package/dist/providers/huggingface.js.map +1 -1
  365. package/dist/providers/huggingface.models.d.ts +4 -2
  366. package/dist/providers/huggingface.models.d.ts.map +1 -1
  367. package/dist/providers/huggingface.models.js +4 -2
  368. package/dist/providers/huggingface.models.js.map +1 -1
  369. package/dist/providers/images/register-builtins.d.ts +1 -1
  370. package/dist/providers/images/register-builtins.d.ts.map +1 -1
  371. package/dist/providers/images/register-builtins.js.map +1 -1
  372. package/dist/providers/kimi-coding.d.ts.map +1 -1
  373. package/dist/providers/kimi-coding.js.map +1 -1
  374. package/dist/providers/kimi-coding.models.d.ts +4 -2
  375. package/dist/providers/kimi-coding.models.d.ts.map +1 -1
  376. package/dist/providers/kimi-coding.models.js +4 -2
  377. package/dist/providers/kimi-coding.models.js.map +1 -1
  378. package/dist/providers/meta.d.ts.map +1 -1
  379. package/dist/providers/meta.js.map +1 -1
  380. package/dist/providers/meta.models.d.ts +4 -2
  381. package/dist/providers/meta.models.d.ts.map +1 -1
  382. package/dist/providers/meta.models.js +4 -2
  383. package/dist/providers/meta.models.js.map +1 -1
  384. package/dist/providers/minimax-cn.d.ts.map +1 -1
  385. package/dist/providers/minimax-cn.js.map +1 -1
  386. package/dist/providers/minimax-cn.models.d.ts +4 -2
  387. package/dist/providers/minimax-cn.models.d.ts.map +1 -1
  388. package/dist/providers/minimax-cn.models.js +4 -2
  389. package/dist/providers/minimax-cn.models.js.map +1 -1
  390. package/dist/providers/minimax.d.ts.map +1 -1
  391. package/dist/providers/minimax.js.map +1 -1
  392. package/dist/providers/minimax.models.d.ts +4 -2
  393. package/dist/providers/minimax.models.d.ts.map +1 -1
  394. package/dist/providers/minimax.models.js +4 -2
  395. package/dist/providers/minimax.models.js.map +1 -1
  396. package/dist/providers/mistral.d.ts.map +1 -1
  397. package/dist/providers/mistral.js.map +1 -1
  398. package/dist/providers/mistral.models.d.ts +4 -2
  399. package/dist/providers/mistral.models.d.ts.map +1 -1
  400. package/dist/providers/mistral.models.js +4 -2
  401. package/dist/providers/mistral.models.js.map +1 -1
  402. package/dist/providers/moonshotai-cn.d.ts.map +1 -1
  403. package/dist/providers/moonshotai-cn.js.map +1 -1
  404. package/dist/providers/moonshotai-cn.models.d.ts +4 -2
  405. package/dist/providers/moonshotai-cn.models.d.ts.map +1 -1
  406. package/dist/providers/moonshotai-cn.models.js +4 -2
  407. package/dist/providers/moonshotai-cn.models.js.map +1 -1
  408. package/dist/providers/moonshotai.d.ts.map +1 -1
  409. package/dist/providers/moonshotai.js.map +1 -1
  410. package/dist/providers/moonshotai.models.d.ts +4 -2
  411. package/dist/providers/moonshotai.models.d.ts.map +1 -1
  412. package/dist/providers/moonshotai.models.js +4 -2
  413. package/dist/providers/moonshotai.models.js.map +1 -1
  414. package/dist/providers/nvidia.d.ts.map +1 -1
  415. package/dist/providers/nvidia.js.map +1 -1
  416. package/dist/providers/nvidia.models.d.ts +4 -2
  417. package/dist/providers/nvidia.models.d.ts.map +1 -1
  418. package/dist/providers/nvidia.models.js +4 -2
  419. package/dist/providers/nvidia.models.js.map +1 -1
  420. package/dist/providers/openai-codex.d.ts.map +1 -1
  421. package/dist/providers/openai-codex.js +1 -1
  422. package/dist/providers/openai-codex.js.map +1 -1
  423. package/dist/providers/openai-codex.models.d.ts +4 -2
  424. package/dist/providers/openai-codex.models.d.ts.map +1 -1
  425. package/dist/providers/openai-codex.models.js +4 -2
  426. package/dist/providers/openai-codex.models.js.map +1 -1
  427. package/dist/providers/openai.d.ts.map +1 -1
  428. package/dist/providers/openai.js +11 -2
  429. package/dist/providers/openai.js.map +1 -1
  430. package/dist/providers/openai.models.d.ts +4 -2
  431. package/dist/providers/openai.models.d.ts.map +1 -1
  432. package/dist/providers/openai.models.js +4 -2
  433. package/dist/providers/openai.models.js.map +1 -1
  434. package/dist/providers/opencode-go.d.ts.map +1 -1
  435. package/dist/providers/opencode-go.js.map +1 -1
  436. package/dist/providers/opencode-go.models.d.ts +4 -2
  437. package/dist/providers/opencode-go.models.d.ts.map +1 -1
  438. package/dist/providers/opencode-go.models.js +4 -2
  439. package/dist/providers/opencode-go.models.js.map +1 -1
  440. package/dist/providers/opencode-headers.d.ts.map +1 -1
  441. package/dist/providers/opencode-headers.js.map +1 -1
  442. package/dist/providers/opencode.d.ts +3 -1
  443. package/dist/providers/opencode.d.ts.map +1 -1
  444. package/dist/providers/opencode.js +5 -2
  445. package/dist/providers/opencode.js.map +1 -1
  446. package/dist/providers/opencode.models.d.ts +4 -2
  447. package/dist/providers/opencode.models.d.ts.map +1 -1
  448. package/dist/providers/opencode.models.js +4 -2
  449. package/dist/providers/opencode.models.js.map +1 -1
  450. package/dist/providers/openrouter.d.ts.map +1 -1
  451. package/dist/providers/openrouter.js +11 -2
  452. package/dist/providers/openrouter.js.map +1 -1
  453. package/dist/providers/openrouter.models.d.ts +4 -2
  454. package/dist/providers/openrouter.models.d.ts.map +1 -1
  455. package/dist/providers/openrouter.models.js +4 -2
  456. package/dist/providers/openrouter.models.js.map +1 -1
  457. package/dist/providers/qwen-token-plan-cn.d.ts.map +1 -1
  458. package/dist/providers/qwen-token-plan-cn.js.map +1 -1
  459. package/dist/providers/qwen-token-plan-cn.models.d.ts +4 -2
  460. package/dist/providers/qwen-token-plan-cn.models.d.ts.map +1 -1
  461. package/dist/providers/qwen-token-plan-cn.models.js +4 -2
  462. package/dist/providers/qwen-token-plan-cn.models.js.map +1 -1
  463. package/dist/providers/qwen-token-plan-individual.d.ts.map +1 -1
  464. package/dist/providers/qwen-token-plan-individual.js.map +1 -1
  465. package/dist/providers/qwen-token-plan-individual.models.d.ts +4 -2
  466. package/dist/providers/qwen-token-plan-individual.models.d.ts.map +1 -1
  467. package/dist/providers/qwen-token-plan-individual.models.js +4 -2
  468. package/dist/providers/qwen-token-plan-individual.models.js.map +1 -1
  469. package/dist/providers/qwen-token-plan.d.ts.map +1 -1
  470. package/dist/providers/qwen-token-plan.js.map +1 -1
  471. package/dist/providers/qwen-token-plan.models.d.ts +4 -2
  472. package/dist/providers/qwen-token-plan.models.d.ts.map +1 -1
  473. package/dist/providers/qwen-token-plan.models.js +4 -2
  474. package/dist/providers/qwen-token-plan.models.js.map +1 -1
  475. package/dist/providers/radius-config.d.ts.map +1 -1
  476. package/dist/providers/radius-config.js.map +1 -1
  477. package/dist/providers/radius.d.ts.map +1 -1
  478. package/dist/providers/radius.js.map +1 -1
  479. package/dist/providers/radius.models.d.ts +4 -2
  480. package/dist/providers/radius.models.d.ts.map +1 -1
  481. package/dist/providers/radius.models.js +4 -2
  482. package/dist/providers/radius.models.js.map +1 -1
  483. package/dist/providers/together.d.ts.map +1 -1
  484. package/dist/providers/together.js.map +1 -1
  485. package/dist/providers/together.models.d.ts +4 -2
  486. package/dist/providers/together.models.d.ts.map +1 -1
  487. package/dist/providers/together.models.js +4 -2
  488. package/dist/providers/together.models.js.map +1 -1
  489. package/dist/providers/typesafe.d.ts +3 -0
  490. package/dist/providers/typesafe.d.ts.map +1 -0
  491. package/dist/providers/typesafe.js +18 -0
  492. package/dist/providers/typesafe.js.map +1 -0
  493. package/dist/providers/typesafe.models.d.ts +6 -0
  494. package/dist/providers/typesafe.models.d.ts.map +1 -0
  495. package/dist/providers/typesafe.models.js +8 -0
  496. package/dist/providers/typesafe.models.js.map +1 -0
  497. package/dist/providers/vercel-ai-gateway.d.ts.map +1 -1
  498. package/dist/providers/vercel-ai-gateway.js +5 -2
  499. package/dist/providers/vercel-ai-gateway.js.map +1 -1
  500. package/dist/providers/vercel-ai-gateway.models.d.ts +4 -2
  501. package/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
  502. package/dist/providers/vercel-ai-gateway.models.js +4 -2
  503. package/dist/providers/vercel-ai-gateway.models.js.map +1 -1
  504. package/dist/providers/xai.d.ts.map +1 -1
  505. package/dist/providers/xai.js.map +1 -1
  506. package/dist/providers/xai.models.d.ts +4 -2
  507. package/dist/providers/xai.models.d.ts.map +1 -1
  508. package/dist/providers/xai.models.js +4 -2
  509. package/dist/providers/xai.models.js.map +1 -1
  510. package/dist/providers/xiaomi-token-plan-ams.d.ts.map +1 -1
  511. package/dist/providers/xiaomi-token-plan-ams.js.map +1 -1
  512. package/dist/providers/xiaomi-token-plan-ams.models.d.ts +4 -2
  513. package/dist/providers/xiaomi-token-plan-ams.models.d.ts.map +1 -1
  514. package/dist/providers/xiaomi-token-plan-ams.models.js +4 -2
  515. package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
  516. package/dist/providers/xiaomi-token-plan-cn.d.ts.map +1 -1
  517. package/dist/providers/xiaomi-token-plan-cn.js.map +1 -1
  518. package/dist/providers/xiaomi-token-plan-cn.models.d.ts +4 -2
  519. package/dist/providers/xiaomi-token-plan-cn.models.d.ts.map +1 -1
  520. package/dist/providers/xiaomi-token-plan-cn.models.js +4 -2
  521. package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
  522. package/dist/providers/xiaomi-token-plan-sgp.d.ts.map +1 -1
  523. package/dist/providers/xiaomi-token-plan-sgp.js.map +1 -1
  524. package/dist/providers/xiaomi-token-plan-sgp.models.d.ts +4 -2
  525. package/dist/providers/xiaomi-token-plan-sgp.models.d.ts.map +1 -1
  526. package/dist/providers/xiaomi-token-plan-sgp.models.js +4 -2
  527. package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
  528. package/dist/providers/xiaomi.d.ts.map +1 -1
  529. package/dist/providers/xiaomi.js.map +1 -1
  530. package/dist/providers/xiaomi.models.d.ts +4 -2
  531. package/dist/providers/xiaomi.models.d.ts.map +1 -1
  532. package/dist/providers/xiaomi.models.js +4 -2
  533. package/dist/providers/xiaomi.models.js.map +1 -1
  534. package/dist/providers/zai-coding-cn.d.ts.map +1 -1
  535. package/dist/providers/zai-coding-cn.js.map +1 -1
  536. package/dist/providers/zai-coding-cn.models.d.ts +4 -2
  537. package/dist/providers/zai-coding-cn.models.d.ts.map +1 -1
  538. package/dist/providers/zai-coding-cn.models.js +4 -2
  539. package/dist/providers/zai-coding-cn.models.js.map +1 -1
  540. package/dist/providers/zai.d.ts.map +1 -1
  541. package/dist/providers/zai.js.map +1 -1
  542. package/dist/providers/zai.models.d.ts +4 -2
  543. package/dist/providers/zai.models.d.ts.map +1 -1
  544. package/dist/providers/zai.models.js +4 -2
  545. package/dist/providers/zai.models.js.map +1 -1
  546. package/dist/session-resources.d.ts.map +1 -1
  547. package/dist/session-resources.js.map +1 -1
  548. package/dist/types.d.ts +141 -21
  549. package/dist/types.d.ts.map +1 -1
  550. package/dist/types.js.map +1 -1
  551. package/dist/utils/abort-signals.d.ts.map +1 -1
  552. package/dist/utils/abort-signals.js.map +1 -1
  553. package/dist/utils/abort.d.ts.map +1 -1
  554. package/dist/utils/abort.js.map +1 -1
  555. package/dist/utils/assistant-message-frame.d.ts.map +1 -1
  556. package/dist/utils/assistant-message-frame.js.map +1 -1
  557. package/dist/utils/diagnostics.d.ts.map +1 -1
  558. package/dist/utils/diagnostics.js.map +1 -1
  559. package/dist/utils/error-body.d.ts.map +1 -1
  560. package/dist/utils/error-body.js.map +1 -1
  561. package/dist/utils/estimate.d.ts.map +1 -1
  562. package/dist/utils/estimate.js.map +1 -1
  563. package/dist/utils/event-stream.d.ts.map +1 -1
  564. package/dist/utils/event-stream.js.map +1 -1
  565. package/dist/utils/hash.d.ts.map +1 -1
  566. package/dist/utils/hash.js.map +1 -1
  567. package/dist/utils/headers.d.ts +1 -1
  568. package/dist/utils/headers.d.ts.map +1 -1
  569. package/dist/utils/headers.js +10 -8
  570. package/dist/utils/headers.js.map +1 -1
  571. package/dist/utils/json-parse.d.ts.map +1 -1
  572. package/dist/utils/json-parse.js.map +1 -1
  573. package/dist/utils/model-operations.d.ts +11 -0
  574. package/dist/utils/model-operations.d.ts.map +1 -0
  575. package/dist/utils/model-operations.js +47 -0
  576. package/dist/utils/model-operations.js.map +1 -0
  577. package/dist/utils/models-error.d.ts +8 -0
  578. package/dist/utils/models-error.d.ts.map +1 -0
  579. package/dist/utils/models-error.js +19 -0
  580. package/dist/utils/models-error.js.map +1 -0
  581. package/dist/utils/node-http-proxy.d.ts.map +1 -1
  582. package/dist/utils/node-http-proxy.js.map +1 -1
  583. package/dist/utils/oauth-page.d.ts.map +1 -0
  584. package/dist/utils/oauth-page.js.map +1 -0
  585. package/dist/utils/overflow.d.ts.map +1 -1
  586. package/dist/utils/overflow.js.map +1 -1
  587. package/dist/utils/pi-user-agent.d.ts.map +1 -1
  588. package/dist/utils/pi-user-agent.js.map +1 -1
  589. package/dist/utils/provider-env.d.ts.map +1 -1
  590. package/dist/utils/provider-env.js.map +1 -1
  591. package/dist/utils/provider-retry.d.ts.map +1 -1
  592. package/dist/utils/provider-retry.js.map +1 -1
  593. package/dist/utils/retry.d.ts.map +1 -1
  594. package/dist/utils/retry.js +6 -0
  595. package/dist/utils/retry.js.map +1 -1
  596. package/dist/utils/sanitize-unicode.d.ts.map +1 -1
  597. package/dist/utils/sanitize-unicode.js.map +1 -1
  598. package/dist/utils/sleep.d.ts.map +1 -1
  599. package/dist/utils/sleep.js.map +1 -1
  600. package/dist/utils/text.d.ts.map +1 -1
  601. package/dist/utils/text.js.map +1 -1
  602. package/dist/utils/transcript.d.ts.map +1 -1
  603. package/dist/utils/transcript.js.map +1 -1
  604. package/dist/utils/typebox-helpers.d.ts.map +1 -1
  605. package/dist/utils/typebox-helpers.js.map +1 -1
  606. package/dist/utils/uuid.d.ts.map +1 -1
  607. package/dist/utils/uuid.js.map +1 -1
  608. package/dist/utils/validation.d.ts.map +1 -1
  609. package/dist/utils/validation.js.map +1 -1
  610. package/package.json +5 -6
  611. package/dist/auth/oauth/oauth-page.d.ts.map +0 -1
  612. package/dist/auth/oauth/oauth-page.js.map +0 -1
  613. package/dist/image-models.generated.d.ts +0 -830
  614. package/dist/image-models.generated.d.ts.map +0 -1
  615. package/dist/image-models.generated.js +0 -832
  616. package/dist/image-models.generated.js.map +0 -1
  617. package/dist/images-models.d.ts +0 -95
  618. package/dist/images-models.d.ts.map +0 -1
  619. package/dist/images-models.js +0 -143
  620. package/dist/images-models.js.map +0 -1
  621. package/dist/providers/openrouter-images.d.ts +0 -3
  622. package/dist/providers/openrouter-images.d.ts.map +0 -1
  623. package/dist/providers/openrouter-images.js +0 -22
  624. package/dist/providers/openrouter-images.js.map +0 -1
  625. /package/dist/{auth/oauth → utils}/oauth-page.d.ts +0 -0
  626. /package/dist/{auth/oauth → utils}/oauth-page.js +0 -0
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Unified LLM API with provider collections, automatic auth resolution, token and cost tracking, and simple context persistence and hand-off to other models mid-session.
4
4
 
5
- **Note**: This library only includes models that support tool calling (function calling), as this is essential for agentic workflows.
5
+ **Note**: The chat catalog only includes models that support tool calling (function calling), as this is essential for agentic workflows. Image and classifier catalogs use their operation-specific capabilities.
6
6
 
7
7
  ## Table of Contents
8
8
 
@@ -29,6 +29,7 @@ Unified LLM API with provider collections, automatic auth resolution, token and
29
29
  - [Compact Assistant Message Frames](#compact-assistant-message-frames)
30
30
  - [Image Input](#image-input)
31
31
  - [Image Generation](#image-generation)
32
+ - [Classification](#classification)
32
33
  - [Thinking/Reasoning](#thinkingreasoning)
33
34
  - [Unified Interface](#unified-interface-streamsimplecompletesimple)
34
35
  - [Provider-Specific Options](#provider-specific-options-streamcomplete)
@@ -38,6 +39,7 @@ Unified LLM API with provider collections, automatic auth resolution, token and
38
39
  - [Aborting Requests](#aborting-requests)
39
40
  - [Continuing After Abort](#continuing-after-abort)
40
41
  - [Debugging Provider Payloads](#debugging-provider-payloads)
42
+ - [Observing Provider Stream Events](#observing-provider-stream-events)
41
43
  - [Custom Providers](#custom-providers)
42
44
  - [createProvider()](#createprovider)
43
45
  - [Calling API Implementations Directly](#calling-api-implementations-directly)
@@ -61,8 +63,9 @@ Unified LLM API with provider collections, automatic auth resolution, token and
61
63
  - **OpenAI**
62
64
  - **Ant Ling**
63
65
  - **Azure OpenAI (Responses)**
64
- - **OpenAI Codex** (ChatGPT Plus/Pro subscription, requires OAuth, see below)
66
+ - **OpenAI Codex (legacy)** (ChatGPT Plus/Pro subscription, requires OAuth, see below)
65
67
  - **Radius** (API key or OAuth, with a dynamically refreshed gateway catalog)
68
+ - **TypeSafe** (System One classifier API)
66
69
  - **DeepSeek**
67
70
  - **NVIDIA NIM**
68
71
  - **Anthropic**
@@ -275,7 +278,7 @@ Reads are synchronous and return the last-known lists:
275
278
  const providers = models.getProviders(); // registered Provider objects
276
279
  const provider = models.getProvider('anthropic'); // one provider
277
280
 
278
- const all = models.getModels(); // every model across providers
281
+ const all = models.getModels(); // every chat model across providers
279
282
  const anthropicModels = models.getModels('anthropic');
280
283
  const model = models.getModel('anthropic', 'claude-sonnet-4-5');
281
284
 
@@ -288,7 +291,31 @@ for (const m of anthropicModels) {
288
291
  }
289
292
  ```
290
293
 
291
- Dynamically listed models are typed `Model<Api>`. Narrow with the `hasApi()` guard when you need API-specific option typing:
294
+ The unqualified reads `getModels()`/`getModel()`/`getAvailable()` return chat models (`Model<Api>`) usable with `stream()`. The `*OfType` reads return one model type, and `getAllModels()`/`getAllAvailable()` return every type as `AnyModel`:
295
+
296
+ ```typescript
297
+ const images = models.getModelsOfType('image', 'openrouter'); // ImageModel[]
298
+ const flux = models.getModelOfType('image', 'openrouter', 'black-forest-labs/flux.2-pro');
299
+ const jev = models.getModelOfType('classifier', 'typesafe', 'jev-latest');
300
+ const availableImages = await models.getAvailableOfType('image');
301
+ const everything = models.getAllModels(); // AnyModel[]
302
+ ```
303
+
304
+ The model's `type` decides which operation accepts it: chat models stream, `type: "image"` models generate images, and `type: "classifier"` models classify structured state. `type` is optional on chat models, so a model without `type` is a chat model. Do not compare `type` directly; narrow mixed lists with `isModelType()` or read the effective type with `getModelType()`:
305
+
306
+ ```typescript
307
+ import { isModelType } from '@earendil-works/pi-ai';
308
+
309
+ for (const model of models.getAllModels()) {
310
+ if (isModelType(model, 'image')) {
311
+ // model: ImageModel<ImageApi>
312
+ }
313
+ }
314
+ ```
315
+
316
+ IDs are unique within each provider and type; one upstream model may have separate entries for different operations. On a provider, `getModels()` returns chat models and the optional `getAllModels()` returns every type; providers with only chat models can omit it.
317
+
318
+ Dynamically listed chat models are typed `Model<Api>`. Narrow with the `hasApi()` guard when you need API-specific option typing:
292
319
 
293
320
  ```typescript
294
321
  import { hasApi } from '@earendil-works/pi-ai';
@@ -305,12 +332,26 @@ if (m && hasApi(m, 'anthropic-messages')) {
305
332
  For tooling that wants the generated built-in catalog with full literal typing (provider and model IDs auto-complete), independent of any collection:
306
333
 
307
334
  ```typescript
308
- import { getBuiltinModel, getBuiltinModels, getBuiltinProviders } from '@earendil-works/pi-ai/providers/all';
335
+ import {
336
+ getAllBuiltinModels,
337
+ getBuiltinClassifierModel,
338
+ getBuiltinClassifierModels,
339
+ getBuiltinImageModel,
340
+ getBuiltinImageModels,
341
+ getBuiltinModel,
342
+ getBuiltinModels,
343
+ getBuiltinProviders,
344
+ } from '@earendil-works/pi-ai/providers/all';
309
345
 
310
346
  const model = getBuiltinModel('openai', 'gpt-4o-mini'); // typed Model<'openai-responses'>
311
- const radius = getBuiltinModel('radius', 'balanced'); // typed Model<'pi-messages'>
347
+ const radius = getBuiltinModel('radius', 'balanced'); // typed Model<'pi-messages'>
348
+ const flux = getBuiltinImageModel('openrouter', 'black-forest-labs/flux.2-pro');
349
+ const jev = getBuiltinClassifierModel('typesafe', 'jev-latest');
312
350
  const providers = getBuiltinProviders();
313
- const anthropic = getBuiltinModels('anthropic');
351
+ const openrouterChat = getBuiltinModels('openrouter'); // Model[]
352
+ const openrouterImages = getBuiltinImageModels('openrouter'); // ImageModel[]
353
+ const typesafeClassifiers = getBuiltinClassifierModels('typesafe'); // ClassifierModel[]
354
+ const openrouterAll = getAllBuiltinModels('openrouter'); // AnyModel[]
314
355
  ```
315
356
 
316
357
  ### Dynamic Providers
@@ -422,6 +463,7 @@ Built-in providers resolve these env vars (Node.js; in browsers pass `apiKey` ex
422
463
  | Azure OpenAI | `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_BASE_URL` (e.g. `https://{resource}.ai.azure.com`) or `AZURE_OPENAI_RESOURCE_NAME`. Supports `*.openai.azure.com`, `*.cognitiveservices.azure.com` and `*.ai.azure.com`; root endpoints auto-normalize to `/openai/v1`. Optional: `AZURE_OPENAI_API_VERSION` (default `v1`), `AZURE_OPENAI_DEPLOYMENT_NAME_MAP`. |
423
464
  | Anthropic | `ANTHROPIC_API_KEY` or `ANTHROPIC_OAUTH_TOKEN` |
424
465
  | Radius | `RADIUS_API_KEY` |
466
+ | TypeSafe | `TYPESAFE_API_KEY` |
425
467
  | DeepSeek | `DEEPSEEK_API_KEY` |
426
468
  | NVIDIA NIM | `NVIDIA_API_KEY` |
427
469
  | Google | `GEMINI_API_KEY` |
@@ -747,20 +789,19 @@ for (const block of response.content) {
747
789
 
748
790
  ## Image Generation
749
791
 
750
- Image generation uses a separate API surface from text/chat generation, mirroring the chat-side design: an `ImagesModels` collection holds `ImagesProvider`s, reads are sync, and auth resolves through the owning provider. Image generation is a one-shot API: `generateImages()` waits for the provider response and returns the final `AssistantImages` result — do not use the chat/stream APIs for it.
792
+ Image models live in the same `Models` collection and on the same `Provider` as chat models, so one credential per provider covers both. They are typed `ImageModel` with `type: "image"` and are used through `generateImages()`, a one-shot API that waits for the provider response and returns the final `AssistantImages` result. Do not use the chat/stream APIs for them; `stream()` rejects image models.
751
793
 
752
794
  ### Basic Image Generation
753
795
 
754
796
  ```typescript
755
- import { builtinImagesModels } from '@earendil-works/pi-ai/providers/all';
797
+ import { builtinModels } from '@earendil-works/pi-ai/providers/all';
756
798
 
757
- // Every built-in image-generation provider; accepts the same options as createModels()
758
- const imagesModels = builtinImagesModels();
799
+ const models = builtinModels();
759
800
 
760
- const model = imagesModels.getModel('openrouter', 'google/gemini-2.5-flash-image')!;
801
+ const model = models.getModelOfType('image', 'openrouter', 'google/gemini-2.5-flash-image')!;
761
802
 
762
803
  // Auth resolves through the provider (OPENROUTER_API_KEY here); explicit apiKey wins
763
- const result = await imagesModels.generateImages(model, {
804
+ const result = await models.generateImages(model, {
764
805
  input: [{ type: 'text', text: 'Generate a red circle on a plain white background.' }]
765
806
  });
766
807
 
@@ -774,7 +815,33 @@ for (const block of result.output) {
774
815
  }
775
816
  ```
776
817
 
777
- Like the chat side, you can build the collection from parts: `createImagesModels({ credentials?, authContext? })`, the `openrouterImagesProvider()` factory from `@earendil-works/pi-ai/providers/openrouter-images`, and `createImagesProvider({ id, auth, models, refreshModels?, api })` for custom image providers (with `imagesModels.refresh(provider?)` for dynamic lists). Failures never reject — they return an `AssistantImages` with `stopReason: "error"`. The collection's provider-scoped `getAuth(providerId)` works exactly like the chat-side one.
818
+ `generateImages()` accepts only `ImageModel` values. If an upstream model supports both chat and image generation, the catalog contains separate entries with the same provider and ID: `getModel()` returns its chat operation and `getModelOfType('image', ...)` returns its image operation. Failures never reject; they return an `AssistantImages` with `stopReason: "error"`, including unknown providers, unconfigured auth, and providers without an image implementation.
819
+
820
+ A provider declares image support with the `images` option of [`createProvider()`](#createprovider): a map from `model.api` to an implementation with `generateImages()`. Image models go into the same `models` list as chat models. `api` becomes optional when `images` is present, so an image-only provider is just a provider without chat models:
821
+
822
+ ```typescript
823
+ import { createProvider, envApiKeyAuth } from '@earendil-works/pi-ai';
824
+
825
+ const pixels = createProvider({
826
+ id: 'pixels',
827
+ auth: { apiKey: envApiKeyAuth('Pixels API key', ['PIXELS_API_KEY']) },
828
+ models: [{
829
+ type: 'image',
830
+ id: 'flux-pro',
831
+ name: 'FLUX Pro',
832
+ api: 'pixels-images',
833
+ provider: 'pixels',
834
+ baseUrl: 'https://api.pixels.test/v1',
835
+ input: ['text'],
836
+ output: ['image'],
837
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
838
+ }],
839
+ images: {
840
+ 'pixels-images': { generateImages: async (model, context, options) => { /* ... */ } },
841
+ },
842
+ });
843
+ models.setProvider(pixels);
844
+ ```
778
845
 
779
846
  The old global API (`getImageModel()` / `getImageModels()` / `getImageProviders()` / `generateImages()`) remains available on the [compat entrypoint](#migrating-from-the-old-global-api):
780
847
 
@@ -795,7 +862,7 @@ Some models also support image input:
795
862
  import { readFileSync } from 'fs';
796
863
 
797
864
  const imageBuffer = readFileSync('input.png');
798
- const result = await imagesModels.generateImages(model, {
865
+ const result = await models.generateImages(model, {
799
866
  input: [
800
867
  { type: 'text', text: 'Create a variation of this image with a blue background.' },
801
868
  { type: 'image', data: imageBuffer.toString('base64'), mimeType: 'image/png' }
@@ -806,14 +873,13 @@ const result = await imagesModels.generateImages(model, {
806
873
  Check capabilities on the model metadata:
807
874
 
808
875
  ```typescript
809
- console.log(model.input); // ['text', 'image']
810
- console.log(model.output); // ['image'] or ['image', 'text']
876
+ console.log(model.input); // ['text'] or ['text', 'image']
877
+ console.log(model.output); // ['image'] or ['image', 'text']
811
878
  ```
812
879
 
813
880
  ### Notes and Limitations
814
881
 
815
- - Image models live in `ImagesModels` collections, chat models in `Models` collections; the two are separate surfaces.
816
- - Use `generateImages()`, not the chat/stream APIs.
882
+ - Image models and chat models share `Models` and `Provider`; list them with `getModelsOfType('image')` and run them with `generateImages()`, never the chat/stream APIs.
817
883
  - Image-generation models do not participate in tool calling.
818
884
  - Outputs are returned in `AssistantImages.output` and can include both base64-encoded `ImageContent` blocks and `TextContent` blocks.
819
885
  - Some models return only images, others return images plus text. Check `model.output`.
@@ -822,6 +888,97 @@ console.log(model.output); // ['image'] or ['image', 'text']
822
888
  - If you want a model to analyze images in a conversation or call tools, use the regular chat APIs with a model that supports image input.
823
889
  - At the moment, image generation is available through only one provider, OpenRouter.
824
890
 
891
+ ## Classification
892
+
893
+ Classifier models consume structured JSON state and answer one or more typed questions. They do not use chat or image-generation APIs. TypeSafe's Jev model is available from these built-in providers:
894
+
895
+ | Provider | Model IDs | Auth |
896
+ | --- | --- | --- |
897
+ | `typesafe` | `jev-latest` | `TYPESAFE_API_KEY` |
898
+ | `openrouter` | `typesafe/jev-1.13`, `~typesafe/jev-latest` | `OPENROUTER_API_KEY` or OpenRouter OAuth |
899
+ | `cloudflare-workers-ai` | `typesafe/jev` | `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ACCOUNT_ID` |
900
+ | `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
901
+ | `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
902
+
903
+ ```typescript
904
+ import { builtinModels } from '@earendil-works/pi-ai/providers/all';
905
+
906
+ const models = builtinModels();
907
+ const model = models.getModelOfType('classifier', 'typesafe', 'jev-latest')!;
908
+ const result = await models.classify(model, {
909
+ state: { message: 'The change works perfectly, thanks.' },
910
+ questions: {
911
+ category: {
912
+ type: 'choice',
913
+ instructions: 'Classify the message.',
914
+ criteria: {
915
+ approval: 'The user approves of the result',
916
+ correction: 'The user requests a correction'
917
+ }
918
+ },
919
+ satisfaction: {
920
+ type: 'score',
921
+ instructions: 'Score user satisfaction.',
922
+ criteria: ['dissatisfied', 'neutral', 'satisfied']
923
+ },
924
+ approved: {
925
+ type: 'bool',
926
+ instructions: 'Does the user approve?',
927
+ criteria: { true: 'Approval', false: 'No approval' }
928
+ }
929
+ }
930
+ });
931
+
932
+ console.log(result.answers);
933
+ ```
934
+
935
+ The public contract uses `bool` questions and `{ type: "bool", probability }` answers. The TypeSafe adapter translates those to and from its `noul` wire representation. Like image generation, `classify()` resolves to a result with `stopReason: "error"` instead of rejecting for provider, authentication, or response errors.
936
+
937
+ When the service reports token counts, `result.usage` carries them with their cost at the model's catalog price, the same `Usage` shape as chat messages. All System One services report token counts; a request that was answered with malformed answers keeps its usage. Local classifiers such as `llama-cpp-classify` report no usage.
938
+
939
+ `ClassifierOptions.temperature` divides the answer logits by the given value before they are normalized; values above 1 soften the distribution. APIs that cannot apply it, such as System One, ignore it.
940
+
941
+ ### Chat models on llama.cpp
942
+
943
+ The `llama-cpp-classify` API turns a chat model served by llama.cpp's `llama-server` into a classifier. Each question becomes one chat prompt: the state, every question of the request, the state again, and the question with its answers under single-token labels (letters for a choice, `Yes`/`No` for a bool, digits for a score). The prompt up to the final question is shared by all questions of a request, so the server's prompt cache evaluates the state once per request. The server returns the log-probabilities of the next token, and the answer is the softmax over the label tokens. Choices support up to 62 options and scores up to 10 levels. The model's `baseUrl` is the server URL; a trailing `/v1` is ignored. In router mode, the model ID selects the model.
944
+
945
+ ```typescript
946
+ import { createProvider } from '@earendil-works/pi-ai';
947
+ import { llamaCppClassifyApi } from '@earendil-works/pi-ai/api/llama-cpp-classify.lazy';
948
+
949
+ const provider = createProvider({
950
+ id: 'local-llama',
951
+ auth: { apiKey: { name: 'llama.cpp', resolve: async () => ({ auth: {} }) } },
952
+ models: [{
953
+ type: 'classifier',
954
+ id: 'qwen3-4b',
955
+ name: 'Qwen3 4B',
956
+ api: 'llama-cpp-classify',
957
+ provider: 'local-llama',
958
+ baseUrl: 'http://127.0.0.1:8080',
959
+ input: ['text'],
960
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
961
+ contextWindow: 32768
962
+ }],
963
+ classifiers: { 'llama-cpp-classify': llamaCppClassifyApi() }
964
+ });
965
+ ```
966
+
967
+ Raw label probabilities are usually overconfident; pass `temperature` above 1 to soften them.
968
+
969
+ Custom providers register classifier models and implementations by API ID:
970
+
971
+ ```typescript
972
+ createProvider({
973
+ id: 'classifier-service',
974
+ auth,
975
+ models: [model],
976
+ classifiers: {
977
+ 'classifier-api': { classify: async (model, context, options) => result }
978
+ }
979
+ });
980
+ ```
981
+
825
982
  ## Thinking/Reasoning
826
983
 
827
984
  Many models support thinking/reasoning capabilities where they can show their internal thought process. You can check if a model supports reasoning via the `reasoning` property. If you pass reasoning options to a non-reasoning model, they are silently ignored.
@@ -1030,11 +1187,30 @@ const response = await models.complete(model, context, {
1030
1187
 
1031
1188
  The callback is supported by `stream`, `complete`, `streamSimple`, and `completeSimple`.
1032
1189
 
1190
+ ### Observing Provider Stream Events
1191
+
1192
+ Use `onProviderStreamEvent` to inspect provider-specific fields that Pi does not include in `AssistantMessage`. The callback receives the parsed event available to the adapter before Pi normalizes it. Treat the event as read-only because mutations can affect normalization. This is not guaranteed to be the original HTTP bytes or SSE frame.
1193
+
1194
+ ```typescript
1195
+ const openRouterModel = models.getModel('openrouter', 'openrouter/auto')!;
1196
+ const response = await models.complete(openRouterModel, context, {
1197
+ headers: { "X-OpenRouter-Metadata": "enabled" },
1198
+ onProviderStreamEvent: (data) => {
1199
+ const chunk = data as Record<string, unknown>;
1200
+ if (chunk.openrouter_metadata) {
1201
+ console.log(chunk.openrouter_metadata);
1202
+ }
1203
+ },
1204
+ });
1205
+ ```
1206
+
1207
+ Callbacks are awaited in stream order, so slow callbacks delay stream consumption and thrown errors fail the request. SDK-backed adapters can expose only fields retained by their SDK.
1208
+
1033
1209
  ## Custom Providers
1034
1210
 
1035
1211
  ### createProvider()
1036
1212
 
1037
- `createProvider()` builds a provider from parts: identity, auth, a model list, and an API implementation. Use it for local inference servers, proxies, or any OpenAI/Anthropic-compatible endpoint:
1213
+ `createProvider()` builds a provider from parts: identity, auth, a model list, and an API implementation (`api` for chat models, `images` for image generation, `classifiers` for classification; at least one is required, see [Image Generation](#image-generation)). Use it for local inference servers, proxies, or any OpenAI/Anthropic-compatible endpoint:
1038
1214
 
1039
1215
  ```typescript
1040
1216
  import { createModels, createProvider, envApiKeyAuth, type Model } from '@earendil-works/pi-ai';
@@ -1116,7 +1292,7 @@ const tenantGateway = createProvider({
1116
1292
  });
1117
1293
  ```
1118
1294
 
1119
- Dynamic model lists use `fetchModels`. `Models.refresh()` refreshes every configured dynamic provider, passing its effective API-key or refreshed OAuth credential. A `ModelsStore` persists dynamic catalogs; both stores default to in-memory implementations. Its `read`, `write`, and `delete` operations accept optional cancellation, and `Models` binds those waits to the provider refresh signal.
1295
+ Dynamic model lists use `fetchModels`, which can return models of every type. `Models.refresh()` refreshes every configured dynamic provider, passing its effective API-key or refreshed OAuth credential. A `ModelsStore` persists dynamic catalogs; both stores default to in-memory implementations. Its `read`, `write`, and `delete` operations accept optional cancellation, and `Models` binds those waits to the provider refresh signal.
1120
1296
 
1121
1297
  ```typescript
1122
1298
  const models = createModels({ credentials, modelsStore });
@@ -1138,7 +1314,7 @@ for (const [provider, error] of result.errors) console.error(provider, error);
1138
1314
 
1139
1315
  Use `models.refresh({ providers: ['openrouter'] })` to restrict work to selected providers, `models.refresh({ allowNetwork: false })` to restore persisted catalogs without network access, or `models.refresh({ force: true })` to bypass provider freshness checks. Model reads stay synchronous and return the last restored or refreshed list.
1140
1316
 
1141
- `createProvider()` handles dynamic publication and persistence automatically. Handwritten `Provider.refreshModels()` implementations receive the read-only `context.stored` snapshot and publish through `context.publish({ persist?, update? })`. Omit `persist` to leave storage unchanged, pass a `ModelsStoreEntry` to write it, or pass `persist: null` to delete it. Publication is generation-checked; put synchronous in-memory catalog changes in `update` rather than mutating state before publication.
1317
+ `createProvider()` handles dynamic publication and persistence automatically. Handwritten `Provider.refreshModels()` implementations receive the read-only `context.stored` snapshot and publish through `context.publish({ persist?, update? })`. Omit `persist` to leave storage unchanged, pass a `ModelsStoreEntry` to write it, or pass `persist: null` to delete it. `ModelsStoreEntry.models` contains models of every type. Publication is generation-checked; put synchronous in-memory catalog changes in `update` rather than mutating state before publication.
1142
1318
 
1143
1319
  Custom models can carry `headers` (e.g. proxies behind bot detection) and `compat` flags. `Models.getAuth(model)` includes those model headers, and stream methods merge them before explicit request headers and `transformHeaders`. See [OpenAI Compatibility Settings](#openai-compatibility-settings).
1144
1320
 
@@ -1553,7 +1729,8 @@ Use this when one process needs different provider settings per request, or when
1553
1729
  Several providers support OAuth authentication instead of static API keys:
1554
1730
 
1555
1731
  - **Anthropic** (Claude Pro/Max subscription)
1556
- - **OpenAI Codex** (ChatGPT Plus/Pro subscription, access to GPT-5.x Codex models)
1732
+ - **OpenAI** (Sign in with ChatGPT: uses the ChatGPT subscription with the OpenAI API)
1733
+ - **OpenAI Codex (legacy)** (ChatGPT Plus/Pro subscription, access to GPT-5.x Codex models)
1557
1734
  - **GitHub Copilot** (Copilot subscription)
1558
1735
  - **OpenRouter** (OAuth PKCE that mints a user-controlled API key)
1559
1736
 
@@ -1633,7 +1810,7 @@ Built-in login and refresh flows are private provider implementations. Use provi
1633
1810
 
1634
1811
  Provider notes:
1635
1812
 
1636
- **OpenAI Codex**: Requires a ChatGPT Plus or Pro subscription. Provides access to GPT-5.x Codex models with extended context windows and reasoning capabilities. The library automatically handles session-based prompt caching when `sessionId` is provided in stream options unless `cacheRetention` is `"none"`. You can set `transport` in stream options to `"sse"`, `"websocket"`, or `"auto"` for Codex Responses transport selection. When using WebSocket with a `sessionId` and cache retention enabled, connections are reused per session and expire after 5 minutes of inactivity. Call `cleanupSessionResources(sessionId)` when finished so the pooled connection does not keep the process alive.
1813
+ **OpenAI Codex (legacy)**: Superseded by Sign in with ChatGPT on the OpenAI provider. Requires a ChatGPT Plus or Pro subscription. Provides access to GPT-5.x Codex models with extended context windows and reasoning capabilities. The library automatically handles session-based prompt caching when `sessionId` is provided in stream options unless `cacheRetention` is `"none"`. You can set `transport` in stream options to `"sse"`, `"websocket"`, or `"auto"` for Codex Responses transport selection. When using WebSocket with a `sessionId` and cache retention enabled, connections are reused per session and expire after 5 minutes of inactivity. Call `cleanupSessionResources(sessionId)` when finished so the pooled connection does not keep the process alive.
1637
1814
 
1638
1815
  **Azure OpenAI (Responses)**: Uses the Responses API only. Set `AZURE_OPENAI_API_KEY` and either `AZURE_OPENAI_BASE_URL` or `AZURE_OPENAI_RESOURCE_NAME`. `AZURE_OPENAI_BASE_URL` supports both `https://<resource>.openai.azure.com` and `https://<resource>.cognitiveservices.azure.com`; root endpoints are normalized to `.../openai/v1` automatically. Use `AZURE_OPENAI_API_VERSION` (defaults to `v1`) to override the API version if needed. Deployment names are treated as model IDs by default, override with `azureDeploymentName` or `AZURE_OPENAI_DEPLOYMENT_NAME_MAP` using comma-separated `model-id=deployment` pairs (for example `gpt-4o-mini=my-deployment,gpt-4o=prod`). Legacy deployment-based URLs are intentionally unsupported.
1639
1816
 
@@ -1662,6 +1839,9 @@ Compat is a strict superset of the root entrypoint, so a file can switch its imp
1662
1839
  | `getEnvApiKey('openai')` | `await models.getAuth(model.provider)` |
1663
1840
  | `streamAnthropic(model, ctx, opts)` | `stream` from `@earendil-works/pi-ai/api/anthropic-messages`, or a provider in a collection |
1664
1841
  | `registerFauxProvider()` | `fauxProvider()` + `models.setProvider()` |
1842
+ | `getImageModel('openrouter', id)` / `generateImages(model, ctx, { apiKey })` | `models.getModelOfType('image', 'openrouter', id)` / `models.generateImages(model, ctx)` |
1843
+
1844
+ The separate `ImagesModels`/`ImagesProvider` collection that existed briefly (`createImagesModels()`, `createImagesProvider()`, `openrouterImagesProvider()`, `builtinImagesModels()`) is gone: image models now live on the regular provider. Replace `builtinImagesModels()` with `builtinModels()`, `imagesModels.getModel()` with `models.getModelOfType('image', ...)`, and `createImagesProvider({ models, api })` with `createProvider({ models, images })`. The old plural image type names are removed; use `ImageModel` and `ImageApi`, and add `type: "image"` to image model literals.
1665
1845
 
1666
1846
  ## Development
1667
1847
 
@@ -1686,11 +1866,11 @@ Create a new API implementation file (for example `bedrock-converse-stream.ts`)
1686
1866
 
1687
1867
  Add a lazy wrapper `src/api/<api-id>.lazy.ts` (`<name>Api()` via `lazyApi()`) so providers can reference the implementation without importing its SDK. Add any root-level `export type` re-exports in `src/index.ts` that should remain available from `@earendil-works/pi-ai`.
1688
1868
 
1689
- #### 3. Model Generation (`scripts/generate-models.ts`, `scripts/generate-image-models.ts`)
1869
+ #### 3. Model Generation (`scripts/generate-models.ts`)
1690
1870
 
1691
1871
  - Add logic to fetch and parse models from the provider's source (e.g., models.dev API)
1692
- - Map chat/tool-capable provider model data to the standardized `Model` interface via `scripts/generate-models.ts`; hydration groups the ignored `src/providers/data/<id>.json` values by API, while stable `src/providers/<id>.models.ts` wrappers derive exact model/API types directly from those JSON keys
1693
- - Map image-generation provider model data to the standardized `ImagesModel` interface via `scripts/generate-image-models.ts`
1872
+ - Map chat/tool-capable provider data to `Model`, image-generation data to `ImageModel`, and models.dev `type: "decision"` entries to `ClassifierModel`; hydration groups the ignored `src/providers/data/<id>.json` values by API while stable `src/providers/<id>.models.ts` wrappers derive exact model/API types directly from those JSON keys
1873
+ - Keep model ids unique within each provider and model type; emit separate entries when an upstream model supports multiple operations
1694
1874
  - Handle provider-specific quirks (pricing format, capability flags, model ID transformations)
1695
1875
 
1696
1876
  #### 4. Provider Factory (`src/providers/<id>.ts`)
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAa1C,OAAO,KAAK,EASX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAMb,MAAM,aAAa,CAAC;AAiJrB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA2ChE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AAqOD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CAgUzE,CAAC;AA2BF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CA8ClF,CAAC","sourcesContent":["import Anthropic from \"@anthropic-ai/sdk\";\nimport type {\n\tBetaStopReason,\n\tBetaThinkingDroppedInputTransformation,\n\tBetaTool,\n\tBetaCacheControlEphemeral as CacheControlEphemeral,\n\tBetaContentBlockParam as ContentBlockParam,\n\tMessageCreateParamsStreaming,\n\tBetaMessageParam as MessageParam,\n\tBetaRawMessageStreamEvent as RawMessageStreamEvent,\n\tBetaRefusalStopDetails as RefusalStopDetails,\n} from \"@anthropic-ai/sdk/resources/beta/messages/messages.js\";\nimport { calculateCost } from \"../models.ts\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tCacheRetention,\n\tImageContent,\n\tMessage,\n\tModel,\n\tProviderEnv,\n\tProviderHeaders,\n\tSimpleStreamOptions,\n\tStopReason,\n\tStreamFunction,\n\tStreamOptions,\n\tTextContent,\n\tThinkingContent,\n\tTool,\n\tToolCall,\n\tToolResultMessage,\n} from \"../types.ts\";\nimport { appendAssistantMessageDiagnostic } from \"../utils/diagnostics.ts\";\nimport { AssistantMessageEventStream } from \"../utils/event-stream.ts\";\nimport { headersToRecord } from \"../utils/headers.ts\";\nimport { parseJsonWithRepair, parseStreamingJson } from \"../utils/json-parse.ts\";\nimport { getPiUserAgent } from \"../utils/pi-user-agent.ts\";\nimport { getProviderEnvValue } from \"../utils/provider-env.ts\";\nimport { retryProviderRequest } from \"../utils/provider-retry.ts\";\nimport { sanitizeSurrogates } from \"../utils/sanitize-unicode.ts\";\nimport { getSystemMessageText, renderSystemMessageUpdate } from \"../utils/text.ts\";\nimport {\n\tgetCurrentTools,\n\tgetDeclaredTools,\n\tgetInitialSystemMessage,\n\thasToolRedefinitions,\n\tresolveTranscript,\n\ttype TranscriptContext,\n} from \"../utils/transcript.ts\";\n\nimport { getJsonSchemaToolParameters, resolveJsonSchemaStrictSampling } from \"./constrained-sampling.ts\";\nimport { buildCopilotDynamicHeaders, hasCopilotVisionInput } from \"./github-copilot-headers.ts\";\nimport { adjustMaxTokensForThinking, buildBaseOptions, clampMaxTokensToContext } from \"./simple-options.ts\";\nimport { transformMessages } from \"./transform-messages.ts\";\n\n/**\n * Resolve cache retention preference.\n * Defaults to \"short\" and uses PI_CACHE_RETENTION for backward compatibility.\n */\nfunction resolveCacheRetention(cacheRetention?: CacheRetention, env?: ProviderEnv): CacheRetention {\n\tif (cacheRetention) {\n\t\treturn cacheRetention;\n\t}\n\tif (getProviderEnvValue(\"PI_CACHE_RETENTION\", env) === \"long\") {\n\t\treturn \"long\";\n\t}\n\treturn \"short\";\n}\n\nfunction getCacheControl(\n\tmodel: Model<\"anthropic-messages\">,\n\tcacheRetention?: CacheRetention,\n\tenv?: ProviderEnv,\n): { retention: CacheRetention; cacheControl?: CacheControlEphemeral } {\n\tconst retention = resolveCacheRetention(cacheRetention, env);\n\tif (retention === \"none\") {\n\t\treturn { retention };\n\t}\n\tconst ttl = retention === \"long\" && getAnthropicCompat(model).supportsLongCacheRetention ? \"1h\" : undefined;\n\treturn {\n\t\tretention,\n\t\tcacheControl: { type: \"ephemeral\", ...(ttl && { ttl }) },\n\t};\n}\n\n// Stealth mode: Mimic Claude Code's tool naming exactly\nconst claudeCodeVersion = \"2.1.280\";\n\n// Claude Code 2.x tool names (canonical casing)\n// Source: https://cchistory.mariozechner.at/data/prompts-2.1.11.md\n// To update: https://github.com/badlogic/cchistory\nconst claudeCodeTools = [\n\t\"Read\",\n\t\"Write\",\n\t\"Edit\",\n\t\"Bash\",\n\t\"Grep\",\n\t\"Glob\",\n\t\"AskUserQuestion\",\n\t\"EnterPlanMode\",\n\t\"ExitPlanMode\",\n\t\"KillShell\",\n\t\"NotebookEdit\",\n\t\"Skill\",\n\t\"Task\",\n\t\"TaskOutput\",\n\t\"TodoWrite\",\n\t\"WebFetch\",\n\t\"WebSearch\",\n];\n\nconst ccToolLookup = new Map(claudeCodeTools.map((t) => [t.toLowerCase(), t]));\n\n// Convert tool name to CC canonical casing if it matches (case-insensitive)\nconst toClaudeCodeName = (name: string) => ccToolLookup.get(name.toLowerCase()) ?? name;\nconst fromClaudeCodeName = (name: string, tools?: Tool[]) => {\n\tif (tools && tools.length > 0) {\n\t\tconst lowerName = name.toLowerCase();\n\t\tconst matchedTool = tools.find((tool) => tool.name.toLowerCase() === lowerName);\n\t\tif (matchedTool) return matchedTool.name;\n\t}\n\treturn name;\n};\n\n/**\n * Convert content blocks to Anthropic API format\n */\nfunction convertContentBlocks(content: (TextContent | ImageContent)[]):\n\t| string\n\t| Array<\n\t\t\t| { type: \"text\"; text: string }\n\t\t\t| {\n\t\t\t\t\ttype: \"image\";\n\t\t\t\t\tsource: {\n\t\t\t\t\t\ttype: \"base64\";\n\t\t\t\t\t\tmedia_type: \"image/jpeg\" | \"image/png\" | \"image/gif\" | \"image/webp\";\n\t\t\t\t\t\tdata: string;\n\t\t\t\t\t};\n\t\t\t }\n\t > {\n\t// If only text blocks, return as concatenated string for simplicity\n\tconst hasImages = content.some((c) => c.type === \"image\");\n\tif (!hasImages) {\n\t\treturn sanitizeSurrogates(content.map((c) => (c as TextContent).text).join(\"\\n\"));\n\t}\n\n\t// If we have images, convert to content block array\n\tconst blocks = content.map((block) => {\n\t\tif (block.type === \"text\") {\n\t\t\treturn {\n\t\t\t\ttype: \"text\" as const,\n\t\t\t\ttext: sanitizeSurrogates(block.text),\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\ttype: \"image\" as const,\n\t\t\tsource: {\n\t\t\t\ttype: \"base64\" as const,\n\t\t\t\tmedia_type: block.mimeType as \"image/jpeg\" | \"image/png\" | \"image/gif\" | \"image/webp\",\n\t\t\t\tdata: block.data,\n\t\t\t},\n\t\t};\n\t});\n\n\t// If only images (no text), add placeholder text block\n\tconst hasText = blocks.some((b) => b.type === \"text\");\n\tif (!hasText) {\n\t\tblocks.unshift({\n\t\t\ttype: \"text\" as const,\n\t\t\ttext: \"(see attached image)\",\n\t\t});\n\t}\n\n\treturn blocks;\n}\n\nexport type AnthropicEffort = \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n\nexport type AnthropicThinkingDisplay = \"summarized\" | \"omitted\";\n\nconst FINE_GRAINED_TOOL_STREAMING_BETA = \"fine-grained-tool-streaming-2025-05-14\";\nconst INTERLEAVED_THINKING_BETA = \"interleaved-thinking-2025-05-14\";\nconst SERVER_SIDE_FALLBACK_BETA = \"server-side-fallback-2026-07-01\";\nconst MID_CONVERSATION_OUTPUT_CONFIG_BETA = \"mid-conversation-output-config-2026-07-01\";\nconst THINKING_BINDING_CONTROLS_BETA = \"thinking-binding-controls-2026-08-01\";\nconst MID_CONVERSATION_TOOL_CHANGES_BETA = \"mid-conversation-tool-changes-2026-07-01\";\n\n/**\n * Stable deferred tool declared whenever native tool changes are in use. Anthropic adds\n * hidden prompt scaffolding as soon as any tool has `defer_loading`; declaring this\n * placeholder from the first request keeps that scaffolding in the cached prefix, so the\n * first real late tool does not invalidate the cache (measured: full miss without it).\n * It is never activated and the model cannot see it.\n */\nconst DEFERRED_TOOL_PLACEHOLDER: BetaTool = {\n\tname: \"__pi_deferred_placeholder__\",\n\tdescription: \"Reserved placeholder. Never available. Never call this.\",\n\tinput_schema: { type: \"object\", properties: {}, required: [] },\n\tdefer_loading: true,\n};\n\nfunction shouldUseServerSideFallbackBeta(model: Model<\"anthropic-messages\">): boolean {\n\treturn (model.compat?.allowedFallbackModels?.length ?? 0) > 0;\n}\n\nfunction getAnthropicCompat(model: Model<\"anthropic-messages\">) {\n\tconst isOpenRouter = model.provider === \"openrouter\" || model.baseUrl.includes(\"openrouter.ai\");\n\treturn {\n\t\tsupportsEagerToolInputStreaming: model.compat?.supportsEagerToolInputStreaming ?? true,\n\t\tsupportsLongCacheRetention: model.compat?.supportsLongCacheRetention ?? true,\n\t\tsendSessionAffinityHeaders: model.compat?.sendSessionAffinityHeaders ?? isOpenRouter,\n\t\tsessionAffinityFormat: model.compat?.sessionAffinityFormat ?? (isOpenRouter ? \"openrouter\" : undefined),\n\t\tsupportsCacheControlOnTools: model.compat?.supportsCacheControlOnTools ?? true,\n\t\tsupportsTemperature: model.compat?.supportsTemperature ?? true,\n\t\tallowEmptySignature: model.compat?.allowEmptySignature ?? false,\n\t\tsupportsStrictTools: model.compat?.supportsStrictTools ?? false,\n\t\tsupportsMidConvoSystemMessages: model.compat?.supportsMidConvoSystemMessages ?? false,\n\t\tsupportsMidConvoToolChanges: model.compat?.supportsMidConvoToolChanges ?? false,\n\t};\n}\n\nexport interface AnthropicOptions extends StreamOptions {\n\t/**\n\t * Enable extended thinking.\n\t * For adaptive thinking models: the model decides when/how much to think.\n\t * For older models: uses budget-based thinking with thinkingBudgetTokens.\n\t * Default: undefined (thinking is omitted unless `streamSimple()` maps\n\t * a simple reasoning level to this option, or callers set it explicitly).\n\t */\n\tthinkingEnabled?: boolean;\n\t/**\n\t * Token budget for extended thinking (older models only).\n\t * Ignored for adaptive thinking models.\n\t * Default: 1024 when `thinkingEnabled` is true and no budget is provided.\n\t */\n\tthinkingBudgetTokens?: number;\n\t/**\n\t * Effort level for adaptive thinking models.\n\t * Controls how much thinking Claude allocates:\n\t * - \"max\": Always thinks with no constraints (Opus 4.6 only)\n\t * - \"xhigh\": Highest reasoning level (Opus 4.7+, Fable 5)\n\t * - \"high\": Always thinks, deep reasoning\n\t * - \"medium\": Moderate thinking, may skip for simple queries\n\t * - \"low\": Minimal thinking, skips for simple tasks\n\t * Ignored for older models.\n\t * Default: omitted unless `streamSimple()` maps a simple reasoning\n\t * level to this option.\n\t */\n\teffort?: AnthropicEffort;\n\t/**\n\t * Controls how thinking content is returned in API responses.\n\t * - \"summarized\": Thinking blocks contain summarized thinking text.\n\t * - \"omitted\": Thinking blocks return an empty thinking field; the encrypted\n\t * signature still travels back for multi-turn continuity. Use for faster\n\t * time-to-first-text-token when your UI does not surface thinking.\n\t *\n\t * Note: Anthropic's API default for Claude Opus 4.7 and Claude Mythos Preview\n\t * is \"omitted\". We default to \"summarized\" here to keep behavior consistent\n\t * with older Claude 4 models. Set this explicitly to \"omitted\" to opt in.\n\t * Default: \"summarized\" when thinking is enabled.\n\t */\n\tthinkingDisplay?: AnthropicThinkingDisplay;\n\t/**\n\t * Whether to request the interleaved thinking beta header for non-adaptive\n\t * thinking models. Adaptive thinking models have interleaved thinking built in,\n\t * so the header is skipped for them regardless of this setting.\n\t * Default: true.\n\t */\n\tinterleavedThinking?: boolean;\n\t/**\n\t * Anthropic tool choice behavior. String values map to Anthropic's built-in\n\t * choices; `{ type: \"tool\", name }` forces a specific tool.\n\t * Default: omitted (Anthropic default behavior, currently equivalent to auto).\n\t */\n\ttoolChoice?: \"auto\" | \"any\" | \"none\" | { type: \"tool\"; name: string };\n\t/**\n\t * Pre-built Anthropic client instance. When provided, skips internal client\n\t * construction entirely. Use this to inject alternative SDK clients such as\n\t * `AnthropicVertex` that shares the same messaging API.\n\t */\n\tclient?: Anthropic;\n}\n\nfunction mergeHeaders(...headerSources: (ProviderHeaders | undefined)[]): ProviderHeaders {\n\tconst merged: ProviderHeaders = {};\n\tfor (const headers of headerSources) {\n\t\tif (headers) {\n\t\t\tObject.assign(merged, headers);\n\t\t}\n\t}\n\treturn merged;\n}\n\nfunction mergeClientHeaders(...headerSources: (ProviderHeaders | undefined)[]): ProviderHeaders {\n\treturn mergeHeaders({ \"User-Agent\": getPiUserAgent() }, ...headerSources);\n}\n\nfunction hasHeader(headers: ProviderHeaders | undefined, name: string): boolean {\n\tif (!headers) return false;\n\tconst expected = name.toLowerCase();\n\tfor (const [key, value] of Object.entries(headers)) {\n\t\tif (key.toLowerCase() === expected && value !== null && value.trim().length > 0) return true;\n\t}\n\treturn false;\n}\n\nfunction assertRequestAuth(provider: string, apiKey: string | undefined, headers: ProviderHeaders | undefined): void {\n\tif (apiKey) return;\n\tif (\n\t\thasHeader(headers, \"authorization\") ||\n\t\thasHeader(headers, \"x-api-key\") ||\n\t\thasHeader(headers, \"cf-aig-authorization\")\n\t) {\n\t\treturn;\n\t}\n\tthrow new Error(`No API key for provider: ${provider}`);\n}\n\ninterface ServerSentEvent {\n\tevent: string | null;\n\tdata: string;\n\traw: string[];\n}\n\ninterface SseDecoderState {\n\tevent: string | null;\n\tdata: string[];\n\traw: string[];\n}\n\nconst ANTHROPIC_MESSAGE_EVENTS: ReadonlySet<string> = new Set([\n\t\"message_start\",\n\t\"message_delta\",\n\t\"message_stop\",\n\t\"content_block_start\",\n\t\"content_block_delta\",\n\t\"content_block_stop\",\n]);\n\nfunction flushSseEvent(state: SseDecoderState): ServerSentEvent | null {\n\tif (!state.event && state.data.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst event: ServerSentEvent = {\n\t\tevent: state.event,\n\t\tdata: state.data.join(\"\\n\"),\n\t\traw: [...state.raw],\n\t};\n\tstate.event = null;\n\tstate.data = [];\n\tstate.raw = [];\n\treturn event;\n}\n\nfunction decodeSseLine(line: string, state: SseDecoderState): ServerSentEvent | null {\n\tif (line === \"\") {\n\t\treturn flushSseEvent(state);\n\t}\n\n\tstate.raw.push(line);\n\tif (line.startsWith(\":\")) {\n\t\treturn null;\n\t}\n\n\tconst delimiterIndex = line.indexOf(\":\");\n\tconst fieldName = delimiterIndex === -1 ? line : line.slice(0, delimiterIndex);\n\tlet value = delimiterIndex === -1 ? \"\" : line.slice(delimiterIndex + 1);\n\tif (value.startsWith(\" \")) {\n\t\tvalue = value.slice(1);\n\t}\n\n\tif (fieldName === \"event\") {\n\t\tstate.event = value;\n\t} else if (fieldName === \"data\") {\n\t\tstate.data.push(value);\n\t}\n\n\treturn null;\n}\n\nfunction nextLineBreakIndex(text: string): number {\n\tconst carriageReturnIndex = text.indexOf(\"\\r\");\n\tconst newlineIndex = text.indexOf(\"\\n\");\n\tif (carriageReturnIndex === -1) {\n\t\treturn newlineIndex;\n\t}\n\tif (newlineIndex === -1) {\n\t\treturn carriageReturnIndex;\n\t}\n\treturn Math.min(carriageReturnIndex, newlineIndex);\n}\n\nfunction consumeLine(text: string): { line: string; rest: string } | null {\n\tconst lineBreakIndex = nextLineBreakIndex(text);\n\tif (lineBreakIndex === -1) {\n\t\treturn null;\n\t}\n\n\tlet nextIndex = lineBreakIndex + 1;\n\tif (text[lineBreakIndex] === \"\\r\" && text[nextIndex] === \"\\n\") {\n\t\tnextIndex += 1;\n\t}\n\n\treturn {\n\t\tline: text.slice(0, lineBreakIndex),\n\t\trest: text.slice(nextIndex),\n\t};\n}\n\nasync function* iterateSseMessages(\n\tbody: ReadableStream<Uint8Array>,\n\tsignal?: AbortSignal,\n): AsyncGenerator<ServerSentEvent> {\n\tconst reader = body.getReader();\n\tconst decoder = new TextDecoder();\n\tconst state: SseDecoderState = { event: null, data: [], raw: [] };\n\tlet buffer = \"\";\n\n\ttry {\n\t\twhile (true) {\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t}\n\n\t\t\tconst { value, done } = await reader.read();\n\t\t\tif (done) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\tlet consumed = consumeLine(buffer);\n\t\t\twhile (consumed) {\n\t\t\t\tbuffer = consumed.rest;\n\t\t\t\tconst event = decodeSseLine(consumed.line, state);\n\t\t\t\tif (event) {\n\t\t\t\t\tyield event;\n\t\t\t\t}\n\t\t\t\tconsumed = consumeLine(buffer);\n\t\t\t}\n\t\t}\n\n\t\tbuffer += decoder.decode();\n\t\tlet consumed = consumeLine(buffer);\n\t\twhile (consumed) {\n\t\t\tbuffer = consumed.rest;\n\t\t\tconst event = decodeSseLine(consumed.line, state);\n\t\t\tif (event) {\n\t\t\t\tyield event;\n\t\t\t}\n\t\t\tconsumed = consumeLine(buffer);\n\t\t}\n\n\t\tif (buffer.length > 0) {\n\t\t\tconst event = decodeSseLine(buffer, state);\n\t\t\tif (event) {\n\t\t\t\tyield event;\n\t\t\t}\n\t\t}\n\n\t\tconst trailingEvent = flushSseEvent(state);\n\t\tif (trailingEvent) {\n\t\t\tyield trailingEvent;\n\t\t}\n\t} finally {\n\t\treader.releaseLock();\n\t}\n}\n\nasync function* iterateAnthropicEvents(\n\tresponse: Response,\n\tsignal?: AbortSignal,\n): AsyncGenerator<RawMessageStreamEvent> {\n\tif (!response.body) {\n\t\tthrow new Error(\"Attempted to iterate over an Anthropic response with no body\");\n\t}\n\n\tlet sawMessageStart = false;\n\tlet sawMessageEnd = false;\n\n\tfor await (const sse of iterateSseMessages(response.body, signal)) {\n\t\tif (sse.event === \"error\") {\n\t\t\tthrow new Error(sse.data);\n\t\t}\n\n\t\tif (!ANTHROPIC_MESSAGE_EVENTS.has(sse.event ?? \"\")) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst event = parseJsonWithRepair<RawMessageStreamEvent>(sse.data);\n\t\t\tif (event.type === \"message_start\") {\n\t\t\t\tsawMessageStart = true;\n\t\t\t} else if (event.type === \"message_stop\") {\n\t\t\t\tsawMessageEnd = true;\n\t\t\t}\n\t\t\tyield event;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthrow new Error(\n\t\t\t\t`Could not parse Anthropic SSE event ${sse.event}: ${message}; data=${sse.data}; raw=${sse.raw.join(\"\\\\n\")}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (sawMessageStart && !sawMessageEnd) {\n\t\tthrow new Error(\"Anthropic stream ended before message_stop\");\n\t}\n}\n\nexport const stream: StreamFunction<\"anthropic-messages\", AnthropicOptions> = (\n\tmodel: Model<\"anthropic-messages\">,\n\tcontext: TranscriptContext,\n\toptions?: AnthropicOptions,\n): AssistantMessageEventStream => {\n\tconst stream = new AssistantMessageEventStream();\n\tconst normalizedContext = resolveTranscript(context, getAnthropicCompat(model).supportsMidConvoSystemMessages);\n\tconst currentTools = getCurrentTools(normalizedContext.messages);\n\n\t(async () => {\n\t\tconst providerThinkingLevel = model.compat?.supportsMidConvoEffort ? (options?.effort ?? \"high\") : undefined;\n\t\tconst output: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api as Api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\t...(providerThinkingLevel === undefined ? {} : { providerThinkingLevel }),\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\tstopReason: \"pending\",\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\ttry {\n\t\t\tlet client: Anthropic;\n\t\t\tlet isOAuth: boolean;\n\t\t\tlet usageModel = model;\n\t\t\tlet inputTransformations: BetaThinkingDroppedInputTransformation[] | undefined;\n\n\t\t\tif (options?.client) {\n\t\t\t\tclient = options.client;\n\t\t\t\tisOAuth = false;\n\t\t\t} else {\n\t\t\t\tconst apiKey = options?.apiKey;\n\t\t\t\tassertRequestAuth(model.provider, apiKey, options?.headers);\n\n\t\t\t\tlet copilotDynamicHeaders: Record<string, string> | undefined;\n\t\t\t\tif (model.provider === \"github-copilot\") {\n\t\t\t\t\tconst hasImages = hasCopilotVisionInput(normalizedContext.messages);\n\t\t\t\t\tcopilotDynamicHeaders = buildCopilotDynamicHeaders({\n\t\t\t\t\t\tmessages: normalizedContext.messages,\n\t\t\t\t\t\thasImages,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst cacheRetention = resolveCacheRetention(options?.cacheRetention, options?.env);\n\t\t\t\tconst cacheSessionId = cacheRetention === \"none\" ? undefined : options?.sessionId;\n\n\t\t\t\tconst created = createClient(\n\t\t\t\t\tmodel,\n\t\t\t\t\tapiKey,\n\t\t\t\t\toptions?.headers,\n\t\t\t\t\toptions?.fetch,\n\t\t\t\t\tcopilotDynamicHeaders,\n\t\t\t\t\tcacheSessionId,\n\t\t\t\t);\n\t\t\t\tclient = created.client;\n\t\t\t\tisOAuth = created.isOAuthToken;\n\t\t\t}\n\t\t\tlet params = buildParams(model, normalizedContext, isOAuth, options);\n\t\t\tconst nextParams = await options?.onPayload?.(params, model);\n\t\t\tif (nextParams !== undefined) {\n\t\t\t\tparams = { ...(nextParams as MessageCreateParamsStreaming), stream: true };\n\t\t\t}\n\t\t\tconst requestOptions = {\n\t\t\t\t...(options?.signal ? { signal: options.signal } : {}),\n\t\t\t\t...(options?.timeoutMs !== undefined ? { timeout: options.timeoutMs } : {}),\n\t\t\t\tmaxRetries: 0,\n\t\t\t};\n\t\t\tconst response = await retryProviderRequest(\n\t\t\t\t() => client.beta.messages.create(params, requestOptions).asResponse(),\n\t\t\t\t{\n\t\t\t\t\tmaxRetries: options?.maxRetries,\n\t\t\t\t\tmaxRetryDelayMs: options?.maxRetryDelayMs,\n\t\t\t\t\tsignal: options?.signal,\n\t\t\t\t},\n\t\t\t);\n\t\t\tawait options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);\n\t\t\tstream.push({ type: \"start\", partial: output });\n\n\t\t\ttype Block = (ThinkingContent | TextContent | (ToolCall & { partialJson: string })) & { index: number };\n\t\t\tconst blocks = output.content as Block[];\n\n\t\t\tfor await (const event of iterateAnthropicEvents(response, options?.signal)) {\n\t\t\t\tif (event.type === \"message_start\") {\n\t\t\t\t\toutput.responseId = event.message.id;\n\t\t\t\t\tconst transformations = event.message.input_transformations;\n\t\t\t\t\tif (Array.isArray(transformations)) inputTransformations = transformations;\n\t\t\t\t\tconst responseModel = event.message.model;\n\t\t\t\t\tif (responseModel !== model.id) output.responseModel = responseModel;\n\t\t\t\t\tconst fallbackCost =\n\t\t\t\t\t\tresponseModel === model.id\n\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t: model.compat?.allowedFallbackModels?.find(\n\t\t\t\t\t\t\t\t\t(fallback) => fallback.provider === model.provider && fallback.model === responseModel,\n\t\t\t\t\t\t\t\t)?.cost;\n\t\t\t\t\tusageModel = fallbackCost ? { ...model, id: responseModel, cost: fallbackCost } : model;\n\t\t\t\t\t// Capture initial token usage from message_start event\n\t\t\t\t\t// This ensures we have input token counts even if the stream is aborted early\n\t\t\t\t\toutput.usage.input = event.message.usage.input_tokens || 0;\n\t\t\t\t\toutput.usage.output = event.message.usage.output_tokens || 0;\n\t\t\t\t\toutput.usage.cacheRead = event.message.usage.cache_read_input_tokens || 0;\n\t\t\t\t\toutput.usage.cacheWrite = event.message.usage.cache_creation_input_tokens || 0;\n\t\t\t\t\toutput.usage.cacheWrite1h = event.message.usage.cache_creation?.ephemeral_1h_input_tokens || 0;\n\t\t\t\t\t// Anthropic doesn't provide total_tokens, compute from components\n\t\t\t\t\toutput.usage.totalTokens =\n\t\t\t\t\t\toutput.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;\n\t\t\t\t\tcalculateCost(usageModel, output.usage);\n\t\t\t\t} else if (event.type === \"content_block_start\") {\n\t\t\t\t\tif (event.content_block.type === \"fallback\") {\n\t\t\t\t\t\tif (output.content.length > 0) {\n\t\t\t\t\t\t\tthrow new Error(\"Anthropic performed an unsupported mid-output model fallback\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (event.content_block.type === \"text\") {\n\t\t\t\t\t\tconst block: Block = {\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: event.content_block.text ?? \"\",\n\t\t\t\t\t\t\tindex: event.index,\n\t\t\t\t\t\t};\n\t\t\t\t\t\toutput.content.push(block);\n\t\t\t\t\t\tstream.push({ type: \"text_start\", contentIndex: output.content.length - 1, partial: output });\n\t\t\t\t\t} else if (event.content_block.type === \"thinking\") {\n\t\t\t\t\t\tconst block: Block = {\n\t\t\t\t\t\t\ttype: \"thinking\",\n\t\t\t\t\t\t\tthinking: event.content_block.thinking ?? \"\",\n\t\t\t\t\t\t\tthinkingSignature: event.content_block.signature ?? \"\",\n\t\t\t\t\t\t\tindex: event.index,\n\t\t\t\t\t\t};\n\t\t\t\t\t\toutput.content.push(block);\n\t\t\t\t\t\tstream.push({ type: \"thinking_start\", contentIndex: output.content.length - 1, partial: output });\n\t\t\t\t\t} else if (event.content_block.type === \"redacted_thinking\") {\n\t\t\t\t\t\tconst block: Block = {\n\t\t\t\t\t\t\ttype: \"thinking\",\n\t\t\t\t\t\t\tthinking: \"[Reasoning redacted]\",\n\t\t\t\t\t\t\tthinkingSignature: event.content_block.data,\n\t\t\t\t\t\t\tredacted: true,\n\t\t\t\t\t\t\tindex: event.index,\n\t\t\t\t\t\t};\n\t\t\t\t\t\toutput.content.push(block);\n\t\t\t\t\t\tstream.push({ type: \"thinking_start\", contentIndex: output.content.length - 1, partial: output });\n\t\t\t\t\t} else if (event.content_block.type === \"tool_use\") {\n\t\t\t\t\t\tconst block: Block = {\n\t\t\t\t\t\t\ttype: \"toolCall\",\n\t\t\t\t\t\t\tid: event.content_block.id,\n\t\t\t\t\t\t\tname: isOAuth\n\t\t\t\t\t\t\t\t? fromClaudeCodeName(event.content_block.name, currentTools)\n\t\t\t\t\t\t\t\t: event.content_block.name,\n\t\t\t\t\t\t\targuments: (event.content_block.input as Record<string, any>) ?? {},\n\t\t\t\t\t\t\tpartialJson: \"\",\n\t\t\t\t\t\t\tindex: event.index,\n\t\t\t\t\t\t};\n\t\t\t\t\t\toutput.content.push(block);\n\t\t\t\t\t\tstream.push({ type: \"toolcall_start\", contentIndex: output.content.length - 1, partial: output });\n\t\t\t\t\t}\n\t\t\t\t} else if (event.type === \"content_block_delta\") {\n\t\t\t\t\tif (event.delta.type === \"text_delta\") {\n\t\t\t\t\t\tconst index = blocks.findIndex((b) => b.index === event.index);\n\t\t\t\t\t\tconst block = blocks[index];\n\t\t\t\t\t\tif (block && block.type === \"text\") {\n\t\t\t\t\t\t\tblock.text += event.delta.text;\n\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\t\t\tcontentIndex: index,\n\t\t\t\t\t\t\t\tdelta: event.delta.text,\n\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (event.delta.type === \"thinking_delta\") {\n\t\t\t\t\t\tconst index = blocks.findIndex((b) => b.index === event.index);\n\t\t\t\t\t\tconst block = blocks[index];\n\t\t\t\t\t\tif (block && block.type === \"thinking\") {\n\t\t\t\t\t\t\tblock.thinking += event.delta.thinking;\n\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\t\t\t\tcontentIndex: index,\n\t\t\t\t\t\t\t\tdelta: event.delta.thinking,\n\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (event.delta.type === \"input_json_delta\") {\n\t\t\t\t\t\tconst index = blocks.findIndex((b) => b.index === event.index);\n\t\t\t\t\t\tconst block = blocks[index];\n\t\t\t\t\t\tif (block && block.type === \"toolCall\") {\n\t\t\t\t\t\t\tblock.partialJson += event.delta.partial_json;\n\t\t\t\t\t\t\tblock.arguments = parseStreamingJson(block.partialJson);\n\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\t\t\t\tcontentIndex: index,\n\t\t\t\t\t\t\t\tdelta: event.delta.partial_json,\n\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (event.delta.type === \"signature_delta\") {\n\t\t\t\t\t\tconst index = blocks.findIndex((b) => b.index === event.index);\n\t\t\t\t\t\tconst block = blocks[index];\n\t\t\t\t\t\tif (block && block.type === \"thinking\") {\n\t\t\t\t\t\t\tblock.thinkingSignature = block.thinkingSignature || \"\";\n\t\t\t\t\t\t\tblock.thinkingSignature += event.delta.signature;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (event.type === \"content_block_stop\") {\n\t\t\t\t\tconst index = blocks.findIndex((b) => b.index === event.index);\n\t\t\t\t\tconst block = blocks[index];\n\t\t\t\t\tif (block) {\n\t\t\t\t\t\tdelete (block as any).index;\n\t\t\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\t\t\tcontentIndex: index,\n\t\t\t\t\t\t\t\tcontent: block.text,\n\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (block.type === \"thinking\") {\n\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\t\t\tcontentIndex: index,\n\t\t\t\t\t\t\t\tcontent: block.thinking,\n\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (block.type === \"toolCall\") {\n\t\t\t\t\t\t\tblock.arguments = parseStreamingJson(block.partialJson);\n\t\t\t\t\t\t\t// Finalize in-place and strip the scratch buffer so replay only\n\t\t\t\t\t\t\t// carries parsed arguments.\n\t\t\t\t\t\t\tdelete (block as { partialJson?: string }).partialJson;\n\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\t\t\t\tcontentIndex: index,\n\t\t\t\t\t\t\t\ttoolCall: block,\n\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (event.type === \"message_delta\") {\n\t\t\t\t\tconst transformations = event.input_transformations;\n\t\t\t\t\tif (Array.isArray(transformations)) inputTransformations = transformations;\n\t\t\t\t\tif (event.delta.stop_reason) {\n\t\t\t\t\t\toutput.rawStopReason = event.delta.stop_reason;\n\t\t\t\t\t\tconst stopReasonResult = mapStopReason(event.delta.stop_reason, event.delta.stop_details);\n\t\t\t\t\t\toutput.stopReason = stopReasonResult.stopReason;\n\t\t\t\t\t\tif (stopReasonResult.errorMessage) {\n\t\t\t\t\t\t\toutput.errorMessage = stopReasonResult.errorMessage;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Only update usage fields if present (not null).\n\t\t\t\t\t// Preserves input_tokens from message_start when proxies omit it in message_delta.\n\t\t\t\t\tif (event.usage) {\n\t\t\t\t\t\tif (event.usage.input_tokens != null) {\n\t\t\t\t\t\t\toutput.usage.input = event.usage.input_tokens;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (event.usage.output_tokens != null) {\n\t\t\t\t\t\t\toutput.usage.output = event.usage.output_tokens;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (event.usage.cache_read_input_tokens != null) {\n\t\t\t\t\t\t\toutput.usage.cacheRead = event.usage.cache_read_input_tokens;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (event.usage.cache_creation_input_tokens != null) {\n\t\t\t\t\t\t\toutput.usage.cacheWrite = event.usage.cache_creation_input_tokens;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Anthropic reports reasoning tokens as a subset of output tokens.\n\t\t\t\t\t\tconst thinkingTokens = event.usage.output_tokens_details?.thinking_tokens;\n\t\t\t\t\t\tif (thinkingTokens != null) {\n\t\t\t\t\t\t\toutput.usage.reasoning = thinkingTokens;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Anthropic doesn't provide total_tokens, compute from components\n\t\t\t\t\toutput.usage.totalTokens =\n\t\t\t\t\t\toutput.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;\n\t\t\t\t\tcalculateCost(usageModel, output.usage);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t}\n\n\t\t\tif (output.stopReason === \"pending\") {\n\t\t\t\tthrow new Error(\"Anthropic stream ended without a stop reason\");\n\t\t\t}\n\t\t\tif (output.stopReason === \"aborted\" || output.stopReason === \"error\") {\n\t\t\t\tthrow new Error(output.errorMessage || \"An unknown error occurred\");\n\t\t\t}\n\t\t\tif (inputTransformations && inputTransformations.length > 0) {\n\t\t\t\tappendAssistantMessageDiagnostic(output, {\n\t\t\t\t\ttype: \"anthropic_input_transformations\",\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttransformations: inputTransformations.map((transformation) => ({\n\t\t\t\t\t\t\ttype: transformation.type ?? undefined,\n\t\t\t\t\t\t\tpath: transformation.path ?? undefined,\n\t\t\t\t\t\t\treason: transformation.reason ?? undefined,\n\t\t\t\t\t\t})),\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tstream.push({ type: \"done\", reason: output.stopReason, message: output });\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tfor (const block of output.content) {\n\t\t\t\tdelete (block as { index?: number }).index;\n\t\t\t\t// partialJson is only a streaming scratch buffer; never persist it.\n\t\t\t\tdelete (block as { partialJson?: string }).partialJson;\n\t\t\t}\n\t\t\toutput.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\toutput.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);\n\t\t\tstream.push({ type: \"error\", reason: output.stopReason, error: output });\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n};\n\n/**\n * Map ThinkingLevel to Anthropic effort levels for adaptive thinking.\n * Note: effort \"max\" is available on all adaptive-thinking Claude models, while native\n * \"xhigh\" is only available on Opus 4.7/4.8, Sonnet 5, and Fable 5.\n */\nfunction mapThinkingLevelToEffort(\n\tmodel: Model<\"anthropic-messages\">,\n\tlevel: SimpleStreamOptions[\"reasoning\"],\n): AnthropicEffort {\n\tconst mapped = level ? model.thinkingLevelMap?.[level] : undefined;\n\tif (typeof mapped === \"string\") return mapped as AnthropicEffort;\n\n\tswitch (level) {\n\t\tcase \"minimal\":\n\t\tcase \"low\":\n\t\t\treturn \"low\";\n\t\tcase \"medium\":\n\t\t\treturn \"medium\";\n\t\tcase \"high\":\n\t\t\treturn \"high\";\n\t\tdefault:\n\t\t\treturn \"high\";\n\t}\n}\n\nexport const streamSimple: StreamFunction<\"anthropic-messages\", SimpleStreamOptions> = (\n\tmodel: Model<\"anthropic-messages\">,\n\tcontext: TranscriptContext,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream => {\n\tassertRequestAuth(model.provider, options?.apiKey, options?.headers);\n\n\tconst base = {\n\t\t...buildBaseOptions(model, context, options, options?.apiKey),\n\t\ttoolChoice: options?.toolChoice,\n\t} satisfies AnthropicOptions;\n\tif (!options?.reasoning) {\n\t\treturn stream(model, context, {\n\t\t\t...base,\n\t\t\tthinkingEnabled: false,\n\t\t} satisfies AnthropicOptions);\n\t}\n\n\t// For models with adaptive thinking: use an effort level.\n\t// For older models: use budget-based thinking.\n\tif (model.compat?.forceAdaptiveThinking === true) {\n\t\tconst effort = mapThinkingLevelToEffort(model, options.reasoning);\n\t\treturn stream(model, context, {\n\t\t\t...base,\n\t\t\tthinkingEnabled: true,\n\t\t\teffort,\n\t\t} satisfies AnthropicOptions);\n\t}\n\n\t// Undefined means the caller did not request an output cap; let the helper use the model cap.\n\t// Do not coerce to 0 here, or the thinking budget would become the entire max_tokens value.\n\tconst adjusted = adjustMaxTokensForThinking(\n\t\tbase.maxTokens,\n\t\tmodel.maxTokens,\n\t\toptions.reasoning,\n\t\toptions.thinkingBudgets,\n\t);\n\n\tconst maxTokens = clampMaxTokensToContext(model, context, adjusted.maxTokens);\n\n\treturn stream(model, context, {\n\t\t...base,\n\t\tmaxTokens,\n\t\tthinkingEnabled: true,\n\t\tthinkingBudgetTokens: Math.min(adjusted.thinkingBudget, Math.max(0, maxTokens - 1024)),\n\t} satisfies AnthropicOptions);\n};\n\nfunction isOAuthToken(apiKey: string): boolean {\n\treturn apiKey.includes(\"sk-ant-oat\");\n}\n\nfunction createClient(\n\tmodel: Model<\"anthropic-messages\">,\n\tapiKey: string | undefined,\n\toptionsHeaders?: ProviderHeaders,\n\tfetch?: typeof globalThis.fetch,\n\tdynamicHeaders?: Record<string, string>,\n\tsessionId?: string,\n): { client: Anthropic; isOAuthToken: boolean } {\n\t// Copilot: Bearer auth.\n\tif (model.provider === \"github-copilot\") {\n\t\tconst client = new Anthropic({\n\t\t\tapiKey: null,\n\t\t\tauthToken: apiKey ?? null,\n\t\t\tbaseURL: model.baseUrl,\n\t\t\tdangerouslyAllowBrowser: true,\n\t\t\tfetch,\n\t\t\tdefaultHeaders: mergeClientHeaders(\n\t\t\t\t{\n\t\t\t\t\taccept: \"application/json\",\n\t\t\t\t\t\"anthropic-dangerous-direct-browser-access\": \"true\",\n\t\t\t\t},\n\t\t\t\tmodel.headers,\n\t\t\t\tdynamicHeaders,\n\t\t\t\toptionsHeaders,\n\t\t\t),\n\t\t});\n\n\t\treturn { client, isOAuthToken: false };\n\t}\n\n\t// OAuth: Bearer auth, Claude Code identity headers\n\tif (apiKey && isOAuthToken(apiKey)) {\n\t\tconst client = new Anthropic({\n\t\t\tapiKey: null,\n\t\t\tauthToken: apiKey,\n\t\t\tbaseURL: model.baseUrl,\n\t\t\tdangerouslyAllowBrowser: true,\n\t\t\tfetch,\n\t\t\tdefaultHeaders: mergeClientHeaders(\n\t\t\t\t{\n\t\t\t\t\taccept: \"application/json\",\n\t\t\t\t\t\"anthropic-dangerous-direct-browser-access\": \"true\",\n\t\t\t\t\t\"user-agent\": `claude-cli/${claudeCodeVersion}`,\n\t\t\t\t\t\"x-app\": \"cli\",\n\t\t\t\t},\n\t\t\t\tmodel.headers,\n\t\t\t\toptionsHeaders,\n\t\t\t),\n\t\t});\n\n\t\treturn { client, isOAuthToken: true };\n\t}\n\n\t// API key or header-owned auth.\n\tconst compat = getAnthropicCompat(model);\n\tconst sessionAffinityHeaders: ProviderHeaders = {};\n\tif (sessionId && compat.sendSessionAffinityHeaders) {\n\t\tconst header = compat.sessionAffinityFormat === \"openrouter\" ? \"x-session-id\" : \"x-session-affinity\";\n\t\tsessionAffinityHeaders[header] = sessionId;\n\t}\n\tconst defaultHeaders = mergeClientHeaders(\n\t\t{\n\t\t\taccept: \"application/json\",\n\t\t\t\"anthropic-dangerous-direct-browser-access\": \"true\",\n\t\t},\n\t\tsessionAffinityHeaders,\n\t\tmodel.headers,\n\t\toptionsHeaders,\n\t);\n\tconst client = new Anthropic({\n\t\tapiKey: apiKey ?? null,\n\t\tauthToken: null,\n\t\tbaseURL: model.baseUrl,\n\t\tdangerouslyAllowBrowser: true,\n\t\tfetch,\n\t\tdefaultHeaders,\n\t});\n\n\treturn { client, isOAuthToken: false };\n}\n\nfunction getBetaFeatures(\n\tmodel: Model<\"anthropic-messages\">,\n\tcontext: TranscriptContext,\n\tisOAuthToken: boolean,\n\tnativeToolChanges: boolean,\n\toptions?: AnthropicOptions,\n): NonNullable<MessageCreateParamsStreaming[\"betas\"]> {\n\tlet configuredFeatures: string | null | undefined;\n\tfor (const headers of [model.headers, options?.headers]) {\n\t\tfor (const [name, value] of Object.entries(headers ?? {})) {\n\t\t\tif (name.toLowerCase() === \"anthropic-beta\") configuredFeatures = value;\n\t\t}\n\t}\n\tif (configuredFeatures === null) return [];\n\tif (configuredFeatures !== undefined) {\n\t\treturn [\n\t\t\t...new Set(\n\t\t\t\tconfiguredFeatures\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((feature) => feature.trim())\n\t\t\t\t\t.filter((feature) => feature.length > 0),\n\t\t\t),\n\t\t];\n\t}\n\n\tconst features: NonNullable<MessageCreateParamsStreaming[\"betas\"]> = [];\n\tif (isOAuthToken) features.push(\"claude-code-20250219\", \"oauth-2025-04-20\");\n\tif (shouldUseFineGrainedToolStreamingBeta(model, context)) features.push(FINE_GRAINED_TOOL_STREAMING_BETA);\n\tif (\n\t\tmodel.reasoning &&\n\t\toptions?.thinkingEnabled === true &&\n\t\t(options.interleavedThinking ?? true) &&\n\t\tmodel.compat?.forceAdaptiveThinking !== true\n\t) {\n\t\tfeatures.push(INTERLEAVED_THINKING_BETA);\n\t}\n\tif (shouldUseServerSideFallbackBeta(model)) features.push(SERVER_SIDE_FALLBACK_BETA);\n\tif (model.compat?.supportsMidConvoEffort === true) {\n\t\tfeatures.push(MID_CONVERSATION_OUTPUT_CONFIG_BETA, THINKING_BINDING_CONTROLS_BETA);\n\t}\n\tif (nativeToolChanges) features.push(MID_CONVERSATION_TOOL_CHANGES_BETA);\n\treturn [...new Set(features)];\n}\n\nfunction buildParams(\n\tmodel: Model<\"anthropic-messages\">,\n\tcontext: TranscriptContext,\n\tisOAuthToken: boolean,\n\toptions?: AnthropicOptions,\n): MessageCreateParamsStreaming {\n\tconst { cacheControl } = getCacheControl(model, options?.cacheRetention, options?.env);\n\tconst compat = getAnthropicCompat(model);\n\tconst initialSystemMessage = getInitialSystemMessage(context.messages);\n\tconst initialSystemText = initialSystemMessage ? getSystemMessageText(initialSystemMessage) : \"\";\n\tconst transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);\n\tconst conversationMessages = initialSystemMessage ? transformedMessages.slice(1) : transformedMessages;\n\t// Native tool changes reference tools by name, so a redefined name cannot be expressed,\n\t// and Anthropic rejects a tool list where every tool is deferred, so there must be an\n\t// initial active tool to anchor the deferred ones. Otherwise the current tool list is sent.\n\tconst initialTools = initialSystemMessage?.toolsAdded ?? [];\n\tconst nativeToolChanges =\n\t\tcompat.supportsMidConvoSystemMessages &&\n\t\tcompat.supportsMidConvoToolChanges &&\n\t\tinitialTools.length > 0 &&\n\t\t!hasToolRedefinitions(context.messages);\n\tconst converted = convertMessages(\n\t\tconversationMessages,\n\t\tisOAuthToken,\n\t\tcacheControl,\n\t\tcompat.allowEmptySignature,\n\t\tmodel.compat?.supportsMidConvoEffort === true ? model.provider : undefined,\n\t\tnativeToolChanges,\n\t);\n\tconst activeEffort = options?.effort ?? \"high\";\n\tconst betaFeatures = getBetaFeatures(model, context, isOAuthToken, nativeToolChanges, options);\n\tconst params: MessageCreateParamsStreaming = {\n\t\tmodel: model.id,\n\t\tmessages:\n\t\t\tmodel.compat?.supportsMidConvoEffort === true\n\t\t\t\t? insertThinkingLevelMessages(converted, activeEffort)\n\t\t\t\t: converted.messages,\n\t\tmax_tokens: options?.maxTokens ?? model.maxTokens,\n\t\tstream: true,\n\t\t...(betaFeatures.length > 0 ? { betas: betaFeatures } : {}),\n\t};\n\n\t// For OAuth tokens, we MUST include Claude Code identity\n\tif (isOAuthToken) {\n\t\tparams.system = [\n\t\t\t{\n\t\t\t\ttype: \"text\",\n\t\t\t\ttext: \"You are Claude Code, Anthropic's official CLI for Claude.\",\n\t\t\t\t...(cacheControl ? { cache_control: cacheControl } : {}),\n\t\t\t},\n\t\t];\n\t\tif (initialSystemText) {\n\t\t\tparams.system.push({\n\t\t\t\ttype: \"text\",\n\t\t\t\ttext: sanitizeSurrogates(initialSystemText),\n\t\t\t\t...(cacheControl ? { cache_control: cacheControl } : {}),\n\t\t\t});\n\t\t}\n\t} else if (initialSystemText) {\n\t\t// Add cache control to system prompt for non-OAuth tokens\n\t\tparams.system = [\n\t\t\t{\n\t\t\t\ttype: \"text\",\n\t\t\t\ttext: sanitizeSurrogates(initialSystemText),\n\t\t\t\t...(cacheControl ? { cache_control: cacheControl } : {}),\n\t\t\t},\n\t\t];\n\t}\n\n\t// Temperature is incompatible with extended thinking and unsupported on Claude Opus 4.7+.\n\tif (\n\t\toptions?.temperature !== undefined &&\n\t\t!options?.thinkingEnabled &&\n\t\tmodel.compat?.supportsMidConvoEffort !== true &&\n\t\tcompat.supportsTemperature\n\t) {\n\t\tparams.temperature = options.temperature;\n\t}\n\n\tconst toolCacheControl = compat.supportsCacheControlOnTools ? cacheControl : undefined;\n\tif (nativeToolChanges) {\n\t\t// Initial tools stay active with the cache breakpoint on the last one. Every later\n\t\t// declaration is deferred and only surfaced by its `tool_addition` block; removed\n\t\t// tools stay declared and are withdrawn by `tool_removal`. The request-level list\n\t\t// therefore only grows, keeping the cached prefix intact across tool changes.\n\t\tconst initialNames = new Set(initialTools.map((tool) => tool.name));\n\t\tconst laterTools = getDeclaredTools(context.messages).filter((tool) => !initialNames.has(tool.name));\n\t\tparams.tools = [\n\t\t\t...convertTools(\n\t\t\t\tinitialTools,\n\t\t\t\tisOAuthToken,\n\t\t\t\tcompat.supportsEagerToolInputStreaming,\n\t\t\t\tcompat.supportsStrictTools,\n\t\t\t\ttoolCacheControl,\n\t\t\t),\n\t\t\tDEFERRED_TOOL_PLACEHOLDER,\n\t\t\t...convertTools(\n\t\t\t\tlaterTools,\n\t\t\t\tisOAuthToken,\n\t\t\t\tcompat.supportsEagerToolInputStreaming,\n\t\t\t\tcompat.supportsStrictTools,\n\t\t\t).map((tool) => ({ ...tool, defer_loading: true })),\n\t\t];\n\t} else {\n\t\tconst tools = getCurrentTools(context.messages);\n\t\tif (tools.length > 0) {\n\t\t\tparams.tools = convertTools(\n\t\t\t\ttools,\n\t\t\t\tisOAuthToken,\n\t\t\t\tcompat.supportsEagerToolInputStreaming,\n\t\t\t\tcompat.supportsStrictTools,\n\t\t\t\ttoolCacheControl,\n\t\t\t);\n\t\t}\n\t}\n\n\t// Managed effort models always use adaptive thinking so prefix mismatches can\n\t// be dropped instead of surfacing as persistent 400 responses.\n\tif (model.compat?.supportsMidConvoEffort === true) {\n\t\tparams.thinking = {\n\t\t\ttype: \"adaptive\",\n\t\t\tdisplay: options?.thinkingDisplay ?? \"summarized\",\n\t\t\tblock_binding: { prefix_mismatch_behavior: \"drop_block\" },\n\t\t};\n\t\tparams.output_config = { effort: \"high\" };\n\t} else if (model.reasoning) {\n\t\tif (options?.thinkingEnabled) {\n\t\t\t// Default to \"summarized\" so Opus 4.7 and Mythos Preview behave like\n\t\t\t// older Claude 4 models (whose API default is also \"summarized\").\n\t\t\tconst display: AnthropicThinkingDisplay = options.thinkingDisplay ?? \"summarized\";\n\t\t\tif (model.compat?.forceAdaptiveThinking === true) {\n\t\t\t\t// Adaptive thinking: Claude decides when and how much to think.\n\t\t\t\tparams.thinking = { type: \"adaptive\", display };\n\t\t\t\tif (options.effort) {\n\t\t\t\t\tparams.output_config = { effort: options.effort };\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Budget-based thinking for older models\n\t\t\t\tparams.thinking = {\n\t\t\t\t\ttype: \"enabled\",\n\t\t\t\t\tbudget_tokens: options.thinkingBudgetTokens || 1024,\n\t\t\t\t\tdisplay,\n\t\t\t\t};\n\t\t\t}\n\t\t} else if (options?.thinkingEnabled === false && model.thinkingLevelMap?.off !== null) {\n\t\t\tparams.thinking = { type: \"disabled\" };\n\t\t}\n\t}\n\n\tif (options?.metadata) {\n\t\tconst userId = options.metadata.user_id;\n\t\tif (typeof userId === \"string\") {\n\t\t\tparams.metadata = { user_id: userId };\n\t\t}\n\t}\n\n\tif (options?.toolChoice) {\n\t\tif (typeof options.toolChoice === \"string\") {\n\t\t\tparams.tool_choice = { type: options.toolChoice };\n\t\t} else {\n\t\t\tparams.tool_choice = options.toolChoice;\n\t\t}\n\t}\n\n\tconst allowedFallbackModels = model.compat?.allowedFallbackModels;\n\tif (allowedFallbackModels && allowedFallbackModels.length > 0) {\n\t\tparams.fallbacks = allowedFallbackModels.map((fallback) => ({ model: fallback.model }));\n\t}\n\n\treturn params;\n}\n\n// Normalize tool call IDs to match Anthropic's required pattern and length\nfunction normalizeToolCallId(id: string): string {\n\treturn id.replace(/[^a-zA-Z0-9_-]/g, \"_\").slice(0, 64);\n}\n\nfunction convertToolResult(msg: ToolResultMessage): ContentBlockParam {\n\treturn {\n\t\ttype: \"tool_result\",\n\t\ttool_use_id: msg.toolCallId,\n\t\tcontent: convertContentBlocks(msg.content),\n\t\tis_error: msg.isError,\n\t};\n}\n\ninterface ConvertedAnthropicMessages {\n\tmessages: MessageParam[];\n\tassistantLevels: Map<number, AnthropicEffort>;\n}\n\nfunction convertMessages(\n\ttransformedMessages: Message[],\n\tisOAuthToken: boolean,\n\tcacheControl?: CacheControlEphemeral,\n\tallowEmptySignature = false,\n\tmanagedProvider?: string,\n\tnativeToolChanges = false,\n): ConvertedAnthropicMessages {\n\tconst params: MessageParam[] = [];\n\tconst assistantLevels = new Map<number, AnthropicEffort>();\n\t// Later system messages are held back and emitted directly before the next assistant\n\t// message (or at the end of the transcript). Anthropic requires `tool_result` blocks to\n\t// immediately follow their `tool_use`, so a system message between them is rejected; this\n\t// also mirrors where the managed-effort system messages are inserted. As a result an\n\t// update placed before a user message in the transcript lands after it on the wire.\n\tconst pendingSystemMessages: MessageParam[] = [];\n\tconst flushPendingSystemMessages = (): void => {\n\t\tparams.push(...pendingSystemMessages);\n\t\tpendingSystemMessages.length = 0;\n\t};\n\n\tfor (let i = 0; i < transformedMessages.length; i++) {\n\t\tconst msg = transformedMessages[i];\n\n\t\tif (msg.role === \"system\") {\n\t\t\t// Later system messages only reach this point when the model accepts them natively;\n\t\t\t// otherwise the transcript was collapsed into the leading message before conversion.\n\t\t\tconst text = renderSystemMessageUpdate(msg);\n\t\t\tconst blocks: ContentBlockParam[] = [];\n\t\t\tif (text.length > 0) blocks.push({ type: \"text\", text: sanitizeSurrogates(text) });\n\t\t\tif (nativeToolChanges) {\n\t\t\t\tfor (const tool of msg.toolsRemoved ?? []) {\n\t\t\t\t\tblocks.push({\n\t\t\t\t\t\ttype: \"tool_removal\",\n\t\t\t\t\t\ttool: { type: \"tool_reference\", name: isOAuthToken ? toClaudeCodeName(tool.name) : tool.name },\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tfor (const tool of msg.toolsAdded ?? []) {\n\t\t\t\t\tblocks.push({\n\t\t\t\t\t\ttype: \"tool_addition\",\n\t\t\t\t\t\ttool: { type: \"tool_reference\", name: isOAuthToken ? toClaudeCodeName(tool.name) : tool.name },\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (blocks.length > 0) pendingSystemMessages.push({ role: \"system\", content: blocks });\n\t\t} else if (msg.role === \"user\") {\n\t\t\tif (typeof msg.content === \"string\") {\n\t\t\t\tif (msg.content.trim().length > 0) {\n\t\t\t\t\tparams.push({\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: sanitizeSurrogates(msg.content),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst blocks: ContentBlockParam[] = msg.content.map((item) => {\n\t\t\t\t\tif (item.type === \"text\") {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: sanitizeSurrogates(item.text),\n\t\t\t\t\t\t};\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttype: \"image\",\n\t\t\t\t\t\t\tsource: {\n\t\t\t\t\t\t\t\ttype: \"base64\",\n\t\t\t\t\t\t\t\tmedia_type: item.mimeType as \"image/jpeg\" | \"image/png\" | \"image/gif\" | \"image/webp\",\n\t\t\t\t\t\t\t\tdata: item.data,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tconst filteredBlocks = blocks.filter((b) => {\n\t\t\t\t\tif (b.type === \"text\") {\n\t\t\t\t\t\treturn b.text.trim().length > 0;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t\tif (filteredBlocks.length === 0) continue;\n\t\t\t\tparams.push({\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tcontent: filteredBlocks,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (msg.role === \"assistant\") {\n\t\t\tflushPendingSystemMessages();\n\t\t\tconst blocks: ContentBlockParam[] = [];\n\n\t\t\tfor (const block of msg.content) {\n\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\tif (block.text.trim().length === 0) continue;\n\t\t\t\t\tblocks.push({\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: sanitizeSurrogates(block.text),\n\t\t\t\t\t});\n\t\t\t\t} else if (block.type === \"thinking\") {\n\t\t\t\t\t// Redacted thinking: pass the opaque payload back as redacted_thinking\n\t\t\t\t\tif (block.redacted) {\n\t\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t\ttype: \"redacted_thinking\",\n\t\t\t\t\t\t\tdata: block.thinkingSignature!,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst thinkingSignature = block.thinkingSignature;\n\t\t\t\t\tconst hasThinkingSignature = !!thinkingSignature && thinkingSignature.trim().length > 0;\n\t\t\t\t\tif (block.thinking.trim().length === 0 && !hasThinkingSignature) continue;\n\t\t\t\t\t// If thinking signature is missing/empty (e.g., from aborted stream),\n\t\t\t\t\t// convert to plain text for Anthropic. Some compatible providers emit\n\t\t\t\t\t// and accept empty signatures, so let marked models preserve the block.\n\t\t\t\t\tif (!hasThinkingSignature) {\n\t\t\t\t\t\tblocks.push(\n\t\t\t\t\t\t\tallowEmptySignature\n\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\ttype: \"thinking\",\n\t\t\t\t\t\t\t\t\t\tthinking: sanitizeSurrogates(block.thinking),\n\t\t\t\t\t\t\t\t\t\tsignature: \"\",\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: sanitizeSurrogates(block.thinking),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t\ttype: \"thinking\",\n\t\t\t\t\t\t\tthinking: sanitizeSurrogates(block.thinking),\n\t\t\t\t\t\t\tsignature: thinkingSignature,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else if (block.type === \"toolCall\") {\n\t\t\t\t\tblocks.push({\n\t\t\t\t\t\ttype: \"tool_use\",\n\t\t\t\t\t\tid: block.id,\n\t\t\t\t\t\tname: isOAuthToken ? toClaudeCodeName(block.name) : block.name,\n\t\t\t\t\t\tinput: block.arguments ?? {},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (blocks.length === 0) continue;\n\t\t\tconst messageIndex = params.length;\n\t\t\tparams.push({\n\t\t\t\trole: \"assistant\",\n\t\t\t\tcontent: blocks,\n\t\t\t});\n\t\t\tif (\n\t\t\t\tmanagedProvider !== undefined &&\n\t\t\t\tmsg.api === \"anthropic-messages\" &&\n\t\t\t\tmsg.provider === managedProvider &&\n\t\t\t\tisAnthropicEffort(msg.providerThinkingLevel)\n\t\t\t) {\n\t\t\t\tassistantLevels.set(messageIndex, msg.providerThinkingLevel);\n\t\t\t}\n\t\t} else if (msg.role === \"toolResult\") {\n\t\t\t// Collect all consecutive toolResult messages, needed for z.ai Anthropic endpoint.\n\t\t\tconst toolResults: ContentBlockParam[] = [];\n\t\t\tlet j = i;\n\t\t\twhile (j < transformedMessages.length && transformedMessages[j].role === \"toolResult\") {\n\t\t\t\ttoolResults.push(convertToolResult(transformedMessages[j] as ToolResultMessage));\n\t\t\t\tj++;\n\t\t\t}\n\n\t\t\t// Skip the messages we've already processed.\n\t\t\ti = j - 1;\n\n\t\t\tparams.push({\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: toolResults,\n\t\t\t});\n\t\t}\n\t}\n\n\tflushPendingSystemMessages();\n\n\t// Add cache_control to the last user or system message to cache conversation history\n\tif (cacheControl && params.length > 0) {\n\t\tconst lastMessage = params[params.length - 1];\n\t\tif (lastMessage.role === \"user\" || lastMessage.role === \"system\") {\n\t\t\tif (Array.isArray(lastMessage.content)) {\n\t\t\t\tconst lastBlock = lastMessage.content[lastMessage.content.length - 1];\n\t\t\t\tif (\n\t\t\t\t\tlastBlock &&\n\t\t\t\t\t(lastBlock.type === \"text\" ||\n\t\t\t\t\t\tlastBlock.type === \"image\" ||\n\t\t\t\t\t\tlastBlock.type === \"tool_result\" ||\n\t\t\t\t\t\tlastBlock.type === \"tool_addition\" ||\n\t\t\t\t\t\tlastBlock.type === \"tool_removal\")\n\t\t\t\t) {\n\t\t\t\t\t(lastBlock as any).cache_control = cacheControl;\n\t\t\t\t}\n\t\t\t} else if (typeof lastMessage.content === \"string\") {\n\t\t\t\tlastMessage.content = [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: lastMessage.content,\n\t\t\t\t\t\tcache_control: cacheControl,\n\t\t\t\t\t},\n\t\t\t\t] as any;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { messages: params, assistantLevels };\n}\n\nfunction isAnthropicEffort(value: unknown): value is AnthropicEffort {\n\treturn value === \"low\" || value === \"medium\" || value === \"high\" || value === \"xhigh\" || value === \"max\";\n}\n\nfunction insertThinkingLevelMessages(\n\tconverted: ConvertedAnthropicMessages,\n\tactiveEffort: AnthropicEffort,\n): MessageParam[] {\n\tconst messages: MessageParam[] = [];\n\tfor (let index = 0; index < converted.messages.length; index++) {\n\t\tconst historicalEffort = converted.assistantLevels.get(index);\n\t\tif (historicalEffort !== undefined) {\n\t\t\tmessages.push({ role: \"system\", content: [], output_config: { effort: historicalEffort } });\n\t\t}\n\t\tmessages.push(converted.messages[index]);\n\t}\n\tmessages.push({ role: \"system\", content: [], output_config: { effort: activeEffort } });\n\treturn messages;\n}\n\nfunction shouldUseFineGrainedToolStreamingBeta(\n\tmodel: Model<\"anthropic-messages\">,\n\tcontext: TranscriptContext,\n): boolean {\n\treturn getCurrentTools(context.messages).length > 0 && !getAnthropicCompat(model).supportsEagerToolInputStreaming;\n}\n\nfunction convertTools(\n\ttools: Tool[],\n\tisOAuthToken: boolean,\n\tsupportsEagerToolInputStreaming: boolean,\n\tsupportsStrictTools: boolean,\n\tcacheControl?: CacheControlEphemeral,\n): BetaTool[] {\n\tif (!tools) return [];\n\n\treturn tools.map((tool, index) => {\n\t\tconst strict = resolveJsonSchemaStrictSampling(tool, supportsStrictTools);\n\t\tconst parameters = getJsonSchemaToolParameters(tool, strict);\n\t\tconst schema = parameters as { properties?: unknown; required?: string[] };\n\t\tconst legacyInputSchema = {\n\t\t\ttype: \"object\" as const,\n\t\t\tproperties: schema.properties ?? {},\n\t\t\trequired: schema.required ?? [],\n\t\t};\n\t\tconst inputSchema =\n\t\t\tstrict === true\n\t\t\t\t? {\n\t\t\t\t\t\t...(parameters as Record<string, unknown>),\n\t\t\t\t\t\t...legacyInputSchema,\n\t\t\t\t\t}\n\t\t\t\t: legacyInputSchema;\n\n\t\treturn {\n\t\t\tname: isOAuthToken ? toClaudeCodeName(tool.name) : tool.name,\n\t\t\tdescription: tool.description,\n\t\t\t...(supportsEagerToolInputStreaming ? { eager_input_streaming: true } : {}),\n\t\t\t...(strict === true ? { strict: true } : {}),\n\t\t\tinput_schema: inputSchema,\n\t\t\t...(cacheControl && index === tools.length - 1 ? { cache_control: cacheControl } : {}),\n\t\t};\n\t});\n}\n\nfunction mapStopReason(\n\treason: BetaStopReason | string,\n\tstopDetails?: RefusalStopDetails | null,\n): { stopReason: StopReason; errorMessage?: string } {\n\tswitch (reason) {\n\t\tcase \"end_turn\":\n\t\t\treturn { stopReason: \"stop\" };\n\t\tcase \"max_tokens\":\n\t\t\treturn { stopReason: \"length\" };\n\t\tcase \"tool_use\":\n\t\t\treturn { stopReason: \"toolUse\" };\n\t\tcase \"refusal\":\n\t\t\treturn {\n\t\t\t\tstopReason: \"error\",\n\t\t\t\terrorMessage: stopDetails?.explanation || `The model refused to complete the request`,\n\t\t\t};\n\t\tcase \"pause_turn\": // Stop is good enough -> resubmit\n\t\t\treturn { stopReason: \"stop\" };\n\t\tcase \"stop_sequence\":\n\t\t\treturn { stopReason: \"stop\" }; // We don't supply stop sequences, so this should never happen\n\t\tcase \"sensitive\": // Content flagged by safety filters (not yet in SDK types)\n\t\t\treturn { stopReason: \"error\", errorMessage: \"Provider stopped with: sensitive\" };\n\t\tdefault:\n\t\t\t// Handle unknown stop reasons gracefully (API may add new values)\n\t\t\tthrow new Error(`Unhandled stop reason: ${reason}`);\n\t}\n}\n"]}
1
+ {"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAa1C,OAAO,KAAK,EASX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAMb,MAAM,aAAa,CAAC;AAiJrB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA2ChE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AAqOD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CAwUzE,CAAC;AA2BF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CA8ClF,CAAC"}
@@ -399,6 +399,7 @@ export const stream = (model, context, options) => {
399
399
  stream.push({ type: "start", partial: output });
400
400
  const blocks = output.content;
401
401
  for await (const event of iterateAnthropicEvents(response, options?.signal)) {
402
+ await options?.onProviderStreamEvent?.(event, model);
402
403
  if (event.type === "message_start") {
403
404
  output.responseId = event.message.id;
404
405
  const transformations = event.message.input_transformations;
@@ -587,6 +588,11 @@ export const stream = (model, context, options) => {
587
588
  if (event.usage.cache_creation_input_tokens != null) {
588
589
  output.usage.cacheWrite = event.usage.cache_creation_input_tokens;
589
590
  }
591
+ // Vercel AI Gateway includes the TTL breakdown in deltas, though the SDK only types it on message_start.
592
+ const cacheCreation = event.usage.cache_creation;
593
+ if (cacheCreation?.ephemeral_1h_input_tokens != null) {
594
+ output.usage.cacheWrite1h = cacheCreation.ephemeral_1h_input_tokens;
595
+ }
590
596
  // Anthropic reports reasoning tokens as a subset of output tokens.
591
597
  const thinkingTokens = event.usage.output_tokens_details?.thinking_tokens;
592
598
  if (thinkingTokens != null) {