@draht/ai 2026.10.31 → 2026.11.5-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 (303) hide show
  1. package/dist/api/anthropic-messages.d.ts.map +1 -1
  2. package/dist/api/anthropic-messages.js +279 -86
  3. package/dist/api/anthropic-messages.js.map +1 -1
  4. package/dist/api/azure-openai-responses.d.ts.map +1 -1
  5. package/dist/api/azure-openai-responses.js +1 -7
  6. package/dist/api/azure-openai-responses.js.map +1 -1
  7. package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  8. package/dist/api/bedrock-converse-stream.js +125 -103
  9. package/dist/api/bedrock-converse-stream.js.map +1 -1
  10. package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
  11. package/dist/api/cloudflare-workers-ai-system-one.js +8 -0
  12. package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -1
  13. package/dist/api/constrained-sampling.d.ts +15 -1
  14. package/dist/api/constrained-sampling.d.ts.map +1 -1
  15. package/dist/api/constrained-sampling.js +51 -18
  16. package/dist/api/constrained-sampling.js.map +1 -1
  17. package/dist/api/google-generative-ai.d.ts.map +1 -1
  18. package/dist/api/google-generative-ai.js +0 -6
  19. package/dist/api/google-generative-ai.js.map +1 -1
  20. package/dist/api/google-shared.d.ts +2 -1
  21. package/dist/api/google-shared.d.ts.map +1 -1
  22. package/dist/api/google-shared.js +57 -2
  23. package/dist/api/google-shared.js.map +1 -1
  24. package/dist/api/google-vertex.d.ts.map +1 -1
  25. package/dist/api/google-vertex.js +0 -6
  26. package/dist/api/google-vertex.js.map +1 -1
  27. package/dist/api/lazy.d.ts +1 -1
  28. package/dist/api/lazy.d.ts.map +1 -1
  29. package/dist/api/lazy.js +9 -8
  30. package/dist/api/lazy.js.map +1 -1
  31. package/dist/api/mistral-conversations.d.ts.map +1 -1
  32. package/dist/api/mistral-conversations.js +17 -44
  33. package/dist/api/mistral-conversations.js.map +1 -1
  34. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  35. package/dist/api/openai-codex-responses.js +7 -8
  36. package/dist/api/openai-codex-responses.js.map +1 -1
  37. package/dist/api/openai-completions.d.ts +2 -1
  38. package/dist/api/openai-completions.d.ts.map +1 -1
  39. package/dist/api/openai-completions.js +52 -57
  40. package/dist/api/openai-completions.js.map +1 -1
  41. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  42. package/dist/api/openai-responses-shared.js +58 -57
  43. package/dist/api/openai-responses-shared.js.map +1 -1
  44. package/dist/api/openai-responses.d.ts.map +1 -1
  45. package/dist/api/openai-responses.js +4 -8
  46. package/dist/api/openai-responses.js.map +1 -1
  47. package/dist/api/openrouter-images.js +1 -0
  48. package/dist/api/openrouter-images.js.map +1 -1
  49. package/dist/api/pi-messages.d.ts +2 -0
  50. package/dist/api/pi-messages.d.ts.map +1 -1
  51. package/dist/api/pi-messages.js +24 -9
  52. package/dist/api/pi-messages.js.map +1 -1
  53. package/dist/api/simple-options.d.ts +1 -0
  54. package/dist/api/simple-options.d.ts.map +1 -1
  55. package/dist/api/simple-options.js +16 -2
  56. package/dist/api/simple-options.js.map +1 -1
  57. package/dist/api/system-one-shared.d.ts +29 -2
  58. package/dist/api/system-one-shared.d.ts.map +1 -1
  59. package/dist/api/system-one-shared.js +55 -3
  60. package/dist/api/system-one-shared.js.map +1 -1
  61. package/dist/api/typesafe-system-one.d.ts.map +1 -1
  62. package/dist/api/typesafe-system-one.js +10 -0
  63. package/dist/api/typesafe-system-one.js.map +1 -1
  64. package/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  65. package/dist/auth/oauth/github-copilot.js +4 -11
  66. package/dist/auth/oauth/github-copilot.js.map +1 -1
  67. package/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
  68. package/dist/auth/oauth/kimi-coding.js +4 -22
  69. package/dist/auth/oauth/kimi-coding.js.map +1 -1
  70. package/dist/auth/oauth/meta.d.ts.map +1 -1
  71. package/dist/auth/oauth/meta.js +1 -14
  72. package/dist/auth/oauth/meta.js.map +1 -1
  73. package/dist/auth/oauth/radius.d.ts.map +1 -1
  74. package/dist/auth/oauth/radius.js +9 -3
  75. package/dist/auth/oauth/radius.js.map +1 -1
  76. package/dist/auth/oauth/trusted-url.d.ts +7 -0
  77. package/dist/auth/oauth/trusted-url.d.ts.map +1 -0
  78. package/dist/auth/oauth/trusted-url.js +12 -0
  79. package/dist/auth/oauth/trusted-url.js.map +1 -0
  80. package/dist/auth/types.d.ts +4 -0
  81. package/dist/auth/types.d.ts.map +1 -1
  82. package/dist/auth/types.js.map +1 -1
  83. package/dist/cli.js +6 -2
  84. package/dist/cli.js.map +1 -1
  85. package/dist/compat/extension-oauth-types.d.ts +4 -0
  86. package/dist/compat/extension-oauth-types.d.ts.map +1 -1
  87. package/dist/compat/extension-oauth-types.js.map +1 -1
  88. package/dist/env-api-keys.d.ts.map +1 -1
  89. package/dist/env-api-keys.js +1 -0
  90. package/dist/env-api-keys.js.map +1 -1
  91. package/dist/index.d.ts +2 -3
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +2 -2
  94. package/dist/index.js.map +1 -1
  95. package/dist/models.d.ts.map +1 -1
  96. package/dist/models.generated.d.ts +4 -0
  97. package/dist/models.generated.d.ts.map +1 -1
  98. package/dist/models.generated.js +5 -1
  99. package/dist/models.generated.js.map +1 -1
  100. package/dist/models.js +9 -8
  101. package/dist/models.js.map +1 -1
  102. package/dist/providers/all.d.ts.map +1 -1
  103. package/dist/providers/all.js +2 -0
  104. package/dist/providers/all.js.map +1 -1
  105. package/dist/providers/amazon-bedrock.models.js +1 -1
  106. package/dist/providers/amazon-bedrock.models.js.map +1 -1
  107. package/dist/providers/ant-ling.models.js +1 -1
  108. package/dist/providers/ant-ling.models.js.map +1 -1
  109. package/dist/providers/anthropic.models.js +1 -1
  110. package/dist/providers/anthropic.models.js.map +1 -1
  111. package/dist/providers/azure.models.js +1 -1
  112. package/dist/providers/azure.models.js.map +1 -1
  113. package/dist/providers/baseten.models.js +1 -1
  114. package/dist/providers/baseten.models.js.map +1 -1
  115. package/dist/providers/cerebras.models.js +1 -1
  116. package/dist/providers/cerebras.models.js.map +1 -1
  117. package/dist/providers/cloudflare-ai-gateway.models.js +1 -1
  118. package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
  119. package/dist/providers/cloudflare-workers-ai.models.js +1 -1
  120. package/dist/providers/cloudflare-workers-ai.models.js.map +1 -1
  121. package/dist/providers/compat-schema.d.ts +447 -444
  122. package/dist/providers/compat-schema.d.ts.map +1 -1
  123. package/dist/providers/compat-schema.js +173 -112
  124. package/dist/providers/compat-schema.js.map +1 -1
  125. package/dist/providers/data/.manifest.json +1 -1
  126. package/dist/providers/data/amazon-bedrock.json +1 -1
  127. package/dist/providers/data/anthropic.json +1 -1
  128. package/dist/providers/data/azure.json +1 -1
  129. package/dist/providers/data/baseten.json +1 -1
  130. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  131. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  132. package/dist/providers/data/fireworks.json +1 -1
  133. package/dist/providers/data/github-copilot.json +1 -1
  134. package/dist/providers/data/google-vertex.json +1 -1
  135. package/dist/providers/data/google.json +1 -1
  136. package/dist/providers/data/huggingface.json +1 -1
  137. package/dist/providers/data/kimi-coding.json +1 -1
  138. package/dist/providers/data/mistral.json +1 -1
  139. package/dist/providers/data/moonshotai-cn.json +1 -1
  140. package/dist/providers/data/moonshotai.json +1 -1
  141. package/dist/providers/data/nvidia.json +1 -1
  142. package/dist/providers/data/openai-codex.json +1 -1
  143. package/dist/providers/data/openai.json +1 -1
  144. package/dist/providers/data/opencode-go.json +1 -1
  145. package/dist/providers/data/opencode.json +1 -1
  146. package/dist/providers/data/openrouter.json +1 -1
  147. package/dist/providers/data/qwen-token-plan-cn.json +1 -1
  148. package/dist/providers/data/qwen-token-plan-individual-cn.json +1 -0
  149. package/dist/providers/data/qwen-token-plan-individual.json +1 -1
  150. package/dist/providers/data/qwen-token-plan.json +1 -1
  151. package/dist/providers/data/radius.json +1 -1
  152. package/dist/providers/data/together.json +1 -1
  153. package/dist/providers/data/typesafe.json +1 -1
  154. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  155. package/dist/providers/data/xai.json +1 -1
  156. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  157. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  158. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  159. package/dist/providers/data/xiaomi.json +1 -1
  160. package/dist/providers/data/zai-coding-cn.json +1 -1
  161. package/dist/providers/data/zai.json +1 -1
  162. package/dist/providers/deepseek.models.js +1 -1
  163. package/dist/providers/deepseek.models.js.map +1 -1
  164. package/dist/providers/faux.d.ts.map +1 -1
  165. package/dist/providers/faux.js +7 -2
  166. package/dist/providers/faux.js.map +1 -1
  167. package/dist/providers/fireworks.models.js +1 -1
  168. package/dist/providers/fireworks.models.js.map +1 -1
  169. package/dist/providers/github-copilot.d.ts +3 -1
  170. package/dist/providers/github-copilot.d.ts.map +1 -1
  171. package/dist/providers/github-copilot.js +13 -2
  172. package/dist/providers/github-copilot.js.map +1 -1
  173. package/dist/providers/github-copilot.models.js +1 -1
  174. package/dist/providers/github-copilot.models.js.map +1 -1
  175. package/dist/providers/google-vertex.models.js +1 -1
  176. package/dist/providers/google-vertex.models.js.map +1 -1
  177. package/dist/providers/google.models.js +1 -1
  178. package/dist/providers/google.models.js.map +1 -1
  179. package/dist/providers/groq.models.js +1 -1
  180. package/dist/providers/groq.models.js.map +1 -1
  181. package/dist/providers/huggingface.models.js +1 -1
  182. package/dist/providers/huggingface.models.js.map +1 -1
  183. package/dist/providers/kimi-coding.models.js +1 -1
  184. package/dist/providers/kimi-coding.models.js.map +1 -1
  185. package/dist/providers/meta.models.js +1 -1
  186. package/dist/providers/meta.models.js.map +1 -1
  187. package/dist/providers/minimax-cn.models.js +1 -1
  188. package/dist/providers/minimax-cn.models.js.map +1 -1
  189. package/dist/providers/minimax.models.js +1 -1
  190. package/dist/providers/minimax.models.js.map +1 -1
  191. package/dist/providers/mistral.models.js +1 -1
  192. package/dist/providers/mistral.models.js.map +1 -1
  193. package/dist/providers/model-schema.d.ts +65 -65
  194. package/dist/providers/model-schema.d.ts.map +1 -1
  195. package/dist/providers/model-schema.js +41 -30
  196. package/dist/providers/model-schema.js.map +1 -1
  197. package/dist/providers/moonshotai-cn.models.js +1 -1
  198. package/dist/providers/moonshotai-cn.models.js.map +1 -1
  199. package/dist/providers/moonshotai.models.js +1 -1
  200. package/dist/providers/moonshotai.models.js.map +1 -1
  201. package/dist/providers/nvidia.models.js +1 -1
  202. package/dist/providers/nvidia.models.js.map +1 -1
  203. package/dist/providers/openai-codex.models.js +1 -1
  204. package/dist/providers/openai-codex.models.js.map +1 -1
  205. package/dist/providers/openai.models.js +1 -1
  206. package/dist/providers/openai.models.js.map +1 -1
  207. package/dist/providers/opencode-go.models.js +1 -1
  208. package/dist/providers/opencode-go.models.js.map +1 -1
  209. package/dist/providers/opencode.js +1 -1
  210. package/dist/providers/opencode.js.map +1 -1
  211. package/dist/providers/opencode.models.js +1 -1
  212. package/dist/providers/opencode.models.js.map +1 -1
  213. package/dist/providers/openrouter.models.js +1 -1
  214. package/dist/providers/openrouter.models.js.map +1 -1
  215. package/dist/providers/qwen-token-plan-cn.models.js +1 -1
  216. package/dist/providers/qwen-token-plan-cn.models.js.map +1 -1
  217. package/dist/providers/qwen-token-plan-individual-cn.d.ts +3 -0
  218. package/dist/providers/qwen-token-plan-individual-cn.d.ts.map +1 -0
  219. package/dist/providers/qwen-token-plan-individual-cn.js +15 -0
  220. package/dist/providers/qwen-token-plan-individual-cn.js.map +1 -0
  221. package/dist/providers/qwen-token-plan-individual-cn.models.d.ts +6 -0
  222. package/dist/providers/qwen-token-plan-individual-cn.models.d.ts.map +1 -0
  223. package/dist/providers/qwen-token-plan-individual-cn.models.js +8 -0
  224. package/dist/providers/qwen-token-plan-individual-cn.models.js.map +1 -0
  225. package/dist/providers/qwen-token-plan-individual.models.js +1 -1
  226. package/dist/providers/qwen-token-plan-individual.models.js.map +1 -1
  227. package/dist/providers/qwen-token-plan.models.js +1 -1
  228. package/dist/providers/qwen-token-plan.models.js.map +1 -1
  229. package/dist/providers/radius-config.d.ts.map +1 -1
  230. package/dist/providers/radius-config.js +2 -1
  231. package/dist/providers/radius-config.js.map +1 -1
  232. package/dist/providers/radius.models.js +1 -1
  233. package/dist/providers/radius.models.js.map +1 -1
  234. package/dist/providers/schema-builders.d.ts +10 -0
  235. package/dist/providers/schema-builders.d.ts.map +1 -0
  236. package/dist/providers/schema-builders.js +18 -0
  237. package/dist/providers/schema-builders.js.map +1 -0
  238. package/dist/providers/together.models.js +1 -1
  239. package/dist/providers/together.models.js.map +1 -1
  240. package/dist/providers/typesafe.models.js +1 -1
  241. package/dist/providers/typesafe.models.js.map +1 -1
  242. package/dist/providers/vercel-ai-gateway.models.js +1 -1
  243. package/dist/providers/vercel-ai-gateway.models.js.map +1 -1
  244. package/dist/providers/xai.models.js +1 -1
  245. package/dist/providers/xai.models.js.map +1 -1
  246. package/dist/providers/xiaomi-token-plan-ams.models.js +1 -1
  247. package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
  248. package/dist/providers/xiaomi-token-plan-cn.models.js +1 -1
  249. package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
  250. package/dist/providers/xiaomi-token-plan-sgp.models.js +1 -1
  251. package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
  252. package/dist/providers/xiaomi.models.js +1 -1
  253. package/dist/providers/xiaomi.models.js.map +1 -1
  254. package/dist/providers/zai-coding-cn.models.js +1 -1
  255. package/dist/providers/zai-coding-cn.models.js.map +1 -1
  256. package/dist/providers/zai.models.js +1 -1
  257. package/dist/providers/zai.models.js.map +1 -1
  258. package/dist/telemetry.d.ts.map +1 -1
  259. package/dist/telemetry.js +2 -1
  260. package/dist/telemetry.js.map +1 -1
  261. package/dist/types.d.ts +24 -5
  262. package/dist/types.d.ts.map +1 -1
  263. package/dist/types.js.map +1 -1
  264. package/dist/utils/error-body.d.ts +8 -0
  265. package/dist/utils/error-body.d.ts.map +1 -1
  266. package/dist/utils/error-body.js +30 -2
  267. package/dist/utils/error-body.js.map +1 -1
  268. package/dist/utils/openrouter-cost.d.ts +24 -0
  269. package/dist/utils/openrouter-cost.d.ts.map +1 -0
  270. package/dist/utils/openrouter-cost.js +34 -0
  271. package/dist/utils/openrouter-cost.js.map +1 -0
  272. package/dist/utils/overflow.d.ts.map +1 -1
  273. package/dist/utils/overflow.js +2 -0
  274. package/dist/utils/overflow.js.map +1 -1
  275. package/dist/utils/retry.d.ts.map +1 -1
  276. package/dist/utils/retry.js +3 -0
  277. package/dist/utils/retry.js.map +1 -1
  278. package/dist/utils/streaming-json.d.ts +84 -0
  279. package/dist/utils/streaming-json.d.ts.map +1 -0
  280. package/dist/utils/streaming-json.js +513 -0
  281. package/dist/utils/streaming-json.js.map +1 -0
  282. package/dist/utils/{typebox-helpers.d.ts → string-enum.d.ts} +5 -5
  283. package/dist/utils/string-enum.d.ts.map +1 -0
  284. package/dist/utils/{typebox-helpers.js → string-enum.js} +5 -6
  285. package/dist/utils/string-enum.js.map +1 -0
  286. package/dist/utils/text.d.ts +7 -0
  287. package/dist/utils/text.d.ts.map +1 -1
  288. package/dist/utils/text.js +9 -0
  289. package/dist/utils/text.js.map +1 -1
  290. package/dist/utils/transcript.d.ts +1 -1
  291. package/dist/utils/transcript.js +1 -1
  292. package/dist/utils/transcript.js.map +1 -1
  293. package/dist/utils/validation.d.ts +5 -3
  294. package/dist/utils/validation.d.ts.map +1 -1
  295. package/dist/utils/validation.js +27 -277
  296. package/dist/utils/validation.js.map +1 -1
  297. package/dist/utils/version.d.ts +1 -1
  298. package/dist/utils/version.d.ts.map +1 -1
  299. package/dist/utils/version.js +1 -1
  300. package/dist/utils/version.js.map +1 -1
  301. package/package.json +8 -7
  302. package/dist/utils/typebox-helpers.d.ts.map +0 -1
  303. package/dist/utils/typebox-helpers.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAiC,MAAM,mBAAmB,CAAC;AAoBlE,OAAO,KAAK,EASX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAMb,MAAM,aAAa,CAAC;AAuJrB,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;AAoRD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CA2WzE,CAAC;AA2BF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CAgDlF,CAAC"}
1
+ {"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAiC,MAAM,mBAAmB,CAAC;AAqBlE,OAAO,KAAK,EASX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAIb,MAAM,aAAa,CAAC;AAiKrB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA6QhE,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;AAoRD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CAgXzE,CAAC;AA2BF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CAgDlF,CAAC"}
@@ -1,17 +1,22 @@
1
1
  import Anthropic, {} from "@anthropic-ai/sdk";
2
2
  import { ANTHROPIC_FEDERATION_RULE_ID_ENV, ANTHROPIC_IDENTITY_TOKEN_FILE_ENV, ANTHROPIC_ORGANIZATION_ID_ENV, ANTHROPIC_SERVICE_ACCOUNT_ID_ENV, ANTHROPIC_WORKSPACE_ID_ENV, } from "../env-api-keys.js";
3
3
  import { calculateCost } from "../models.js";
4
+ import { registerSessionResourceCleanup } from "../session-resources.js";
4
5
  import { appendAssistantMessageDiagnostic } from "../utils/diagnostics.js";
6
+ import { withCauseDetail } from "../utils/error-body.js";
5
7
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
8
+ import { shortHash } from "../utils/hash.js";
6
9
  import { headersToRecord } from "../utils/headers.js";
7
10
  import { getIdentityHeaders } from "../utils/identity.js";
8
- import { parseJsonWithRepair, parseStreamingJson, parseToolCallArguments } from "../utils/json-parse.js";
11
+ import { parseJsonWithRepair } from "../utils/json-parse.js";
12
+ import { applyOpenRouterReportedCost, isOpenRouterModel, } from "../utils/openrouter-cost.js";
9
13
  import { getProviderEnvValue } from "../utils/provider-env.js";
10
14
  import { retryProviderRequest } from "../utils/provider-retry.js";
11
15
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
16
+ import { ToolCallArgumentsStream } from "../utils/streaming-json.js";
12
17
  import { getSystemMessageText, renderSystemMessageUpdate } from "../utils/text.js";
13
18
  import { getCurrentTools, getInitialSystemMessage, resolveTranscript, } from "../utils/transcript.js";
14
- import { getJsonSchemaToolParameters, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
19
+ import { getJsonSchemaToolParameters, isStrictToolLimitErrorMessage, prefersJsonSchemaStrictSampling, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
15
20
  import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copilot-headers.js";
16
21
  import { adjustMaxTokensForThinking, buildBaseOptions, clampMaxTokensToContext } from "./simple-options.js";
17
22
  import { splitErrorToolResultImages, transformMessages } from "./transform-messages.js";
@@ -120,6 +125,7 @@ const SERVER_SIDE_FALLBACK_BETA = "server-side-fallback-2026-07-01";
120
125
  const MID_CONVERSATION_OUTPUT_CONFIG_BETA = "mid-conversation-output-config-2026-07-01";
121
126
  const THINKING_BINDING_CONTROLS_BETA = "thinking-binding-controls-2026-08-01";
122
127
  const INLINE_TOOLS_BETA = "inline-tools-2026-09-15";
128
+ const MESSAGE_THREADS_BETA = "message-threads-2026-08-12";
123
129
  /**
124
130
  * Stable deferred tool declared whenever native tool changes are in use. Anthropic adds
125
131
  * hidden prompt scaffolding for mid-conversation tool changes; declaring this placeholder
@@ -149,8 +155,178 @@ function getAnthropicCompat(model) {
149
155
  supportsStrictTools: model.compat?.supportsStrictTools ?? false,
150
156
  supportsMidConvoSystemMessages: model.compat?.supportsMidConvoSystemMessages ?? false,
151
157
  supportsMidConvoToolChanges: model.compat?.supportsMidConvoToolChanges ?? false,
158
+ // Gateways that drop unknown request fields would silently turn a `continue` into a request
159
+ // without the earlier conversation, so the default is limited to Anthropic's own endpoint.
160
+ // Widen or remove this restriction once proxies generally forward the `thread` field.
161
+ supportsMessageThreads: model.compat?.supportsMessageThreads ??
162
+ (model.provider === "anthropic" && model.baseUrl.includes("api.anthropic.com")),
152
163
  };
153
164
  }
165
+ /** Per session: where the next request can continue, or "off" after the endpoint rejected threads. */
166
+ const threadState = new Map();
167
+ const MAX_THREAD_STATE_ENTRIES = 10_000;
168
+ /** Rejections of a `continue` that a new thread fixes: expired, raced, or a fixed setting changed. */
169
+ const NEW_THREAD_ERRORS = new Set([
170
+ "thread_not_found",
171
+ "thread_already_continued",
172
+ "thread_continue_in_flight",
173
+ "thread_fingerprint_mismatch",
174
+ ]);
175
+ /**
176
+ * Sets a session's thread state, keeping at most `MAX_THREAD_STATE_ENTRIES` sessions.
177
+ *
178
+ * State is normally freed by `cleanupSessionResources(sessionId)` when a session ends, as draht's
179
+ * coding agent does. The cap bounds memory for library users that never call it, for example a
180
+ * server passing a new `sessionId` per conversation. Dropping the least recently updated session
181
+ * is cheap: its thread has most likely expired on the server already (after a few minutes idle),
182
+ * and otherwise its next request just starts a new thread.
183
+ */
184
+ function setThreadState(sessionId, value) {
185
+ // Re-insert so Map order stays oldest-first for the cap.
186
+ threadState.delete(sessionId);
187
+ threadState.set(sessionId, value);
188
+ if (threadState.size > MAX_THREAD_STATE_ENTRIES) {
189
+ const oldest = threadState.keys().next().value;
190
+ if (oldest !== undefined)
191
+ threadState.delete(oldest);
192
+ }
193
+ }
194
+ registerSessionResourceCleanup((sessionId) => {
195
+ if (sessionId)
196
+ threadState.delete(sessionId);
197
+ else
198
+ threadState.clear();
199
+ });
200
+ /**
201
+ * Hash of everything a `continue` builds on: the settings that must stay the same within a thread
202
+ * and the messages the server already holds.
203
+ *
204
+ * - Settings, as sent: the endpoint (a thread exists on one endpoint only), and the fixed request
205
+ * fields. A `continue` omits `system` and `tools` and the server keeps the thread's own, so only
206
+ * this check can notice a change to them, including their cache markers. The server rejects
207
+ * changes to the other fields with `thread_fingerprint_mismatch`; checking them here saves that
208
+ * rejected request. A fixed field missing here costs only that rejection, which also starts a
209
+ * new thread.
210
+ * - Messages, normalized so messages that mean the same to the API hash the same.
211
+ */
212
+ function hashThreadPrefix(model, params, messages) {
213
+ const { system, tools, thinking, output_config, tool_choice, betas, fallbacks } = params;
214
+ const settings = JSON.stringify([
215
+ model.provider,
216
+ model.baseUrl,
217
+ params.model,
218
+ system,
219
+ tools,
220
+ thinking,
221
+ output_config,
222
+ tool_choice,
223
+ betas,
224
+ fallbacks,
225
+ ]);
226
+ const conversation = JSON.stringify(messages, (key, value) => {
227
+ // Cache markers don't change what a message means, but they move: draht puts one on the newest
228
+ // message, so the message that had it in the last request has none in this one. Extensions
229
+ // may add markers anywhere too.
230
+ if (key === "cache_control")
231
+ return undefined;
232
+ // To attach that marker, the newest user message's string content is sent as one text block;
233
+ // the next request sends the same message as a string: "hi" vs [{ type: "text", text: "hi" }].
234
+ if (key === "content" && typeof value === "string")
235
+ return [{ type: "text", text: value }];
236
+ return value;
237
+ });
238
+ return shortHash(settings + conversation);
239
+ }
240
+ function createThreadRequest(sessionId, params) {
241
+ return { sessionId, body: { ...params, thread: { type: "create" } } };
242
+ }
243
+ function prepareThreadRequest(model, sessionId, params) {
244
+ const state = threadState.get(sessionId);
245
+ if (state === "off")
246
+ return undefined;
247
+ if (state !== undefined &&
248
+ params.messages.length > state.prefixCount &&
249
+ hashThreadPrefix(model, params, params.messages.slice(0, state.prefixCount)) === state.prefixHash) {
250
+ // Each reply can be continued once; deleting also keeps a concurrent request from trying.
251
+ threadState.delete(sessionId);
252
+ const { system: _system, tools: _tools, ...rest } = params;
253
+ return {
254
+ body: {
255
+ ...rest,
256
+ messages: params.messages.slice(state.prefixCount),
257
+ thread: { type: "continue", previous_message_id: state.replyId },
258
+ },
259
+ sessionId,
260
+ };
261
+ }
262
+ return createThreadRequest(sessionId, params);
263
+ }
264
+ /** The `error_code` Anthropic puts in `error.details` of thread rejections. */
265
+ function getThreadErrorCode(error) {
266
+ const body = error.error;
267
+ const code = body?.error?.details?.error_code;
268
+ return typeof code === "string" ? code : undefined;
269
+ }
270
+ /**
271
+ * Sends a thread request. A rejection about the thread (never a started stream) is resent once in
272
+ * full: as a new thread when the old one can't be continued, or without `thread` when the endpoint
273
+ * can't use threads, which also turns threads off for the session. Other errors are passed on.
274
+ * Returns the response and the thread request that produced it, if any.
275
+ */
276
+ async function sendThreadRequest(thread, params, send, output, signal) {
277
+ try {
278
+ return { response: await send(thread.body), thread };
279
+ }
280
+ catch (error) {
281
+ if (signal?.aborted || !(error instanceof Anthropic.APIError))
282
+ throw error;
283
+ const errorCode = getThreadErrorCode(error);
284
+ const startNewThread = errorCode !== undefined && NEW_THREAD_ERRORS.has(errorCode);
285
+ // The endpoint can't use threads: it can't thread this request, or it doesn't know the
286
+ // `thread` field and rejects it without a code ("thread: Extra inputs are not permitted"),
287
+ // e.g. with an `anthropic-beta` header that lacks the threads beta.
288
+ const threadsUnsupported = errorCode === "thread_unsupported_request" ||
289
+ (errorCode === undefined && error.status === 400 && error.message.includes("thread"));
290
+ // Any other error (prompt too long, unknown model, strict tool limits, ...) is not about the
291
+ // thread, and resending would only fail again.
292
+ if (!startNewThread && !threadsUnsupported)
293
+ throw error;
294
+ // Recorded before the resend, so a failed resend keeps it.
295
+ appendAssistantMessageDiagnostic(output, {
296
+ type: "anthropic_thread_fallback",
297
+ timestamp: Date.now(),
298
+ error: { message: error.message },
299
+ details: { reason: errorCode ?? String(error.status) },
300
+ });
301
+ if (startNewThread) {
302
+ const created = createThreadRequest(thread.sessionId, params);
303
+ return { response: await send(created.body), thread: created };
304
+ }
305
+ // With the threads beta, a request without `thread` is a normal request.
306
+ const response = await send(params);
307
+ setThreadState(thread.sessionId, "off");
308
+ return { response, thread: undefined };
309
+ }
310
+ }
311
+ /**
312
+ * Records where the session's next request can continue: after `output`, the clean reply to the
313
+ * thread request `thread`, whose full request was `params`.
314
+ */
315
+ function recordThreadReply(model, thread, params, output, isOAuth, allowEmptySignature) {
316
+ // `responseModel` is set only when a fallback model answered; the server does not store those
317
+ // replies, so they cannot be continued.
318
+ if (output.responseId === undefined || output.responseModel !== undefined)
319
+ return;
320
+ // Convert the reply like the next request will, keeping only the assistant message
321
+ // (transformMessages adds placeholder results for trailing tool calls).
322
+ const reply = convertMessages(transformMessages([output], model, normalizeToolCallId), isOAuth, undefined, allowEmptySignature).messages.filter((message) => message.role === "assistant");
323
+ const prefix = [...params.messages, ...reply];
324
+ setThreadState(thread.sessionId, {
325
+ prefixCount: prefix.length,
326
+ prefixHash: hashThreadPrefix(model, params, prefix),
327
+ replyId: output.responseId,
328
+ });
329
+ }
154
330
  function mergeHeaders(...headerSources) {
155
331
  const merged = {};
156
332
  for (const headers of headerSources) {
@@ -375,7 +551,8 @@ async function* iterateAnthropicEvents(response, signal) {
375
551
  }
376
552
  export const stream = (model, context, options) => {
377
553
  const stream = new AssistantMessageEventStream();
378
- const normalizedContext = resolveTranscript(context, getAnthropicCompat(model).supportsMidConvoSystemMessages);
554
+ const compat = getAnthropicCompat(model);
555
+ const normalizedContext = resolveTranscript(context, compat.supportsMidConvoSystemMessages);
379
556
  const currentTools = getCurrentTools(normalizedContext.messages);
380
557
  (async () => {
381
558
  const output = {
@@ -400,6 +577,12 @@ export const stream = (model, context, options) => {
400
577
  let isOAuth;
401
578
  let usageModel = model;
402
579
  let inputTransformations;
580
+ const cacheRetention = resolveCacheRetention(options?.cacheRetention, options?.env);
581
+ const cacheSessionId = cacheRetention === "none" ? undefined : options?.sessionId;
582
+ // Session whose requests may use threads; undefined when threads don't apply. Sessions where
583
+ // the endpoint rejected threads ("off") still send the threads beta, which the resend that
584
+ // turned them off was accepted with; `prepareThreadRequest()` just sends no `thread`.
585
+ const threadSessionId = compat.supportsMessageThreads ? cacheSessionId : undefined;
403
586
  if (options?.client) {
404
587
  client = options.client;
405
588
  isOAuth = false;
@@ -417,8 +600,6 @@ export const stream = (model, context, options) => {
417
600
  hasImages,
418
601
  });
419
602
  }
420
- const cacheRetention = resolveCacheRetention(options?.cacheRetention, options?.env);
421
- const cacheSessionId = cacheRetention === "none" ? undefined : options?.sessionId;
422
603
  const created = createClient(model, apiKey, options?.headers, options?.fetch, copilotDynamicHeaders, cacheSessionId, federation, options?.identity);
423
604
  client = created.client;
424
605
  isOAuth = created.isOAuthToken;
@@ -428,24 +609,38 @@ export const stream = (model, context, options) => {
428
609
  ...(options?.timeoutMs !== undefined ? { timeout: options.timeoutMs } : {}),
429
610
  maxRetries: 0,
430
611
  };
612
+ const send = (body) => retryProviderRequest(() => client.beta.messages.create(body, requestOptions).asResponse(), {
613
+ maxRetries: options?.maxRetries,
614
+ maxRetryDelayMs: options?.maxRetryDelayMs,
615
+ signal: options?.signal,
616
+ });
431
617
  let disablePreferredStrictTools = false;
432
618
  let response;
619
+ let params;
620
+ // The thread request that produced the response; undefined when no `thread` was sent.
621
+ let thread;
433
622
  for (;;) {
434
623
  options?.signal?.throwIfAborted();
435
- const built = buildParams(model, normalizedContext, isOAuth, options, disablePreferredStrictTools);
624
+ const built = buildParams(model, normalizedContext, isOAuth, options, {
625
+ disablePreferredStrictTools,
626
+ messageThreads: threadSessionId !== undefined,
627
+ });
436
628
  if (built.providerThinkingLevel)
437
629
  output.providerThinkingLevel = built.providerThinkingLevel;
438
- let params = built.params;
630
+ params = built.params;
439
631
  const nextParams = await options?.onPayload?.(params, model);
440
632
  if (nextParams !== undefined) {
441
633
  params = { ...nextParams, stream: true };
442
634
  }
635
+ const prepared = threadSessionId !== undefined && options?.disableContinuation !== true
636
+ ? prepareThreadRequest(model, threadSessionId, params)
637
+ : undefined;
443
638
  try {
444
- response = await retryProviderRequest(() => client.beta.messages.create(params, requestOptions).asResponse(), {
445
- maxRetries: options?.maxRetries,
446
- maxRetryDelayMs: options?.maxRetryDelayMs,
447
- signal: options?.signal,
448
- });
639
+ const sent = prepared
640
+ ? await sendThreadRequest(prepared, params, send, output, options?.signal)
641
+ : { response: await send(params), thread: undefined };
642
+ response = sent.response;
643
+ thread = sent.thread;
449
644
  break;
450
645
  }
451
646
  catch (error) {
@@ -456,11 +651,7 @@ export const stream = (model, context, options) => {
456
651
  !(error instanceof Anthropic.APIError) ||
457
652
  error.status !== 400 ||
458
653
  error.type !== "invalid_request_error" ||
459
- ![
460
- "The compiled grammar is too large",
461
- "Too many strict tools (",
462
- "Schemas contains too many parameters with union types (",
463
- ].some((message) => error.message.includes(message))) {
654
+ !isStrictToolLimitErrorMessage(error.message)) {
464
655
  throw error;
465
656
  }
466
657
  disablePreferredStrictTools = true;
@@ -474,7 +665,12 @@ export const stream = (model, context, options) => {
474
665
  }
475
666
  await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
476
667
  stream.push({ type: "start", partial: output });
477
- const blocks = output.content;
668
+ // Stream state per Anthropic block index, kept off the content blocks so it never reaches partials.
669
+ const openBlocks = new Map();
670
+ const openBlock = (anthropicIndex) => {
671
+ const open = openBlocks.get(anthropicIndex);
672
+ return open === undefined ? undefined : { ...open, block: output.content[open.contentIndex] };
673
+ };
478
674
  for await (const event of iterateAnthropicEvents(response, options?.signal)) {
479
675
  await options?.onProviderStreamEvent?.(event, model);
480
676
  if (event.type === "message_start") {
@@ -509,105 +705,92 @@ export const stream = (model, context, options) => {
509
705
  continue;
510
706
  }
511
707
  if (event.content_block.type === "text") {
512
- const block = {
513
- type: "text",
514
- text: event.content_block.text ?? "",
515
- index: event.index,
516
- };
517
- output.content.push(block);
708
+ output.content.push({ type: "text", text: event.content_block.text ?? "" });
709
+ openBlocks.set(event.index, { contentIndex: output.content.length - 1 });
518
710
  stream.push({ type: "text_start", contentIndex: output.content.length - 1, partial: output });
519
711
  }
520
712
  else if (event.content_block.type === "thinking") {
521
- const block = {
713
+ output.content.push({
522
714
  type: "thinking",
523
715
  thinking: event.content_block.thinking ?? "",
524
716
  thinkingSignature: event.content_block.signature ?? "",
525
- index: event.index,
526
- };
527
- output.content.push(block);
717
+ });
718
+ openBlocks.set(event.index, { contentIndex: output.content.length - 1 });
528
719
  stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
529
720
  }
530
721
  else if (event.content_block.type === "redacted_thinking") {
531
- const block = {
722
+ output.content.push({
532
723
  type: "thinking",
533
724
  thinking: "[Reasoning redacted]",
534
725
  thinkingSignature: event.content_block.data,
535
726
  redacted: true,
536
- index: event.index,
537
- };
538
- output.content.push(block);
727
+ });
728
+ openBlocks.set(event.index, { contentIndex: output.content.length - 1 });
539
729
  stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
540
730
  }
541
731
  else if (event.content_block.type === "tool_use") {
542
- const block = {
732
+ output.content.push({
543
733
  type: "toolCall",
544
734
  id: event.content_block.id,
545
735
  name: isOAuth
546
736
  ? fromClaudeCodeName(event.content_block.name, currentTools)
547
737
  : event.content_block.name,
548
738
  arguments: event.content_block.input ?? {},
549
- partialJson: "",
550
- index: event.index,
551
- };
552
- output.content.push(block);
739
+ });
740
+ openBlocks.set(event.index, {
741
+ contentIndex: output.content.length - 1,
742
+ arguments: new ToolCallArgumentsStream(),
743
+ });
553
744
  stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
554
745
  }
555
746
  }
556
747
  else if (event.type === "content_block_delta") {
748
+ const open = openBlock(event.index);
557
749
  if (event.delta.type === "text_delta") {
558
- const index = blocks.findIndex((b) => b.index === event.index);
559
- const block = blocks[index];
560
- if (block && block.type === "text") {
561
- block.text += event.delta.text;
750
+ if (open?.block.type === "text") {
751
+ open.block.text += event.delta.text;
562
752
  stream.push({
563
753
  type: "text_delta",
564
- contentIndex: index,
754
+ contentIndex: open.contentIndex,
565
755
  delta: event.delta.text,
566
756
  partial: output,
567
757
  });
568
758
  }
569
759
  }
570
760
  else if (event.delta.type === "thinking_delta") {
571
- const index = blocks.findIndex((b) => b.index === event.index);
572
- const block = blocks[index];
573
- if (block && block.type === "thinking") {
574
- block.thinking += event.delta.thinking;
761
+ if (open?.block.type === "thinking") {
762
+ open.block.thinking += event.delta.thinking;
575
763
  stream.push({
576
764
  type: "thinking_delta",
577
- contentIndex: index,
765
+ contentIndex: open.contentIndex,
578
766
  delta: event.delta.thinking,
579
767
  partial: output,
580
768
  });
581
769
  }
582
770
  }
583
771
  else if (event.delta.type === "input_json_delta") {
584
- const index = blocks.findIndex((b) => b.index === event.index);
585
- const block = blocks[index];
586
- if (block && block.type === "toolCall") {
587
- block.partialJson += event.delta.partial_json;
588
- block.arguments = parseStreamingJson(block.partialJson);
772
+ if (open?.block.type === "toolCall" && open.arguments !== undefined) {
773
+ open.block.arguments = open.arguments.append(event.delta.partial_json);
589
774
  stream.push({
590
775
  type: "toolcall_delta",
591
- contentIndex: index,
776
+ contentIndex: open.contentIndex,
592
777
  delta: event.delta.partial_json,
593
778
  partial: output,
594
779
  });
595
780
  }
596
781
  }
597
782
  else if (event.delta.type === "signature_delta") {
598
- const index = blocks.findIndex((b) => b.index === event.index);
599
- const block = blocks[index];
600
- if (block && block.type === "thinking") {
601
- block.thinkingSignature = block.thinkingSignature || "";
602
- block.thinkingSignature += event.delta.signature;
783
+ if (open?.block.type === "thinking") {
784
+ open.block.thinkingSignature = open.block.thinkingSignature || "";
785
+ open.block.thinkingSignature += event.delta.signature;
603
786
  }
604
787
  }
605
788
  }
606
789
  else if (event.type === "content_block_stop") {
607
- const index = blocks.findIndex((b) => b.index === event.index);
608
- const block = blocks[index];
609
- if (block) {
610
- delete block.index;
790
+ const open = openBlock(event.index);
791
+ openBlocks.delete(event.index);
792
+ if (open) {
793
+ const { block, contentIndex: index } = open;
611
794
  if (block.type === "text") {
612
795
  stream.push({
613
796
  type: "text_end",
@@ -624,11 +807,8 @@ export const stream = (model, context, options) => {
624
807
  partial: output,
625
808
  });
626
809
  }
627
- else if (block.type === "toolCall") {
628
- Object.assign(block, parseToolCallArguments(block.partialJson));
629
- // Finalize in-place and strip the scratch buffer so replay only
630
- // carries parsed arguments.
631
- delete block.partialJson;
810
+ else if (block.type === "toolCall" && open.arguments !== undefined) {
811
+ Object.assign(block, open.arguments.finish());
632
812
  stream.push({
633
813
  type: "toolcall_end",
634
814
  contentIndex: index,
@@ -680,6 +860,11 @@ export const stream = (model, context, options) => {
680
860
  output.usage.totalTokens =
681
861
  output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
682
862
  calculateCost(usageModel, output.usage);
863
+ if (isOpenRouterModel(model) && event.usage) {
864
+ // OpenRouter adds billing fields to streamed message_delta usage at runtime,
865
+ // although its OpenAPI schema and the Anthropic SDK do not type those fields.
866
+ applyOpenRouterReportedCost(output.usage, event.usage);
867
+ }
683
868
  }
684
869
  }
685
870
  if (options?.signal?.aborted) {
@@ -691,6 +876,8 @@ export const stream = (model, context, options) => {
691
876
  if (output.stopReason === "aborted" || output.stopReason === "error") {
692
877
  throw new Error(output.errorMessage || "An unknown error occurred");
693
878
  }
879
+ if (thread)
880
+ recordThreadReply(model, thread, params, output, isOAuth, compat.allowEmptySignature);
694
881
  if (inputTransformations && inputTransformations.length > 0) {
695
882
  appendAssistantMessageDiagnostic(output, {
696
883
  type: "anthropic_input_transformations",
@@ -708,13 +895,8 @@ export const stream = (model, context, options) => {
708
895
  stream.end();
709
896
  }
710
897
  catch (error) {
711
- for (const block of output.content) {
712
- delete block.index;
713
- // partialJson is only a streaming scratch buffer; never persist it.
714
- delete block.partialJson;
715
- }
716
898
  output.stopReason = options?.signal?.aborted ? "aborted" : "error";
717
- output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
899
+ output.errorMessage = error instanceof Error ? withCauseDetail(error) : JSON.stringify(error);
718
900
  stream.push({ type: "error", reason: output.stopReason, error: output });
719
901
  stream.end();
720
902
  }
@@ -849,7 +1031,7 @@ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sess
849
1031
  });
850
1032
  return { client, isOAuthToken: false };
851
1033
  }
852
- function getBetaFeatures(model, context, isOAuthToken, nativeToolChanges, options) {
1034
+ function getBetaFeatures(model, context, options, { isOAuthToken, nativeToolChanges, messageThreads }) {
853
1035
  let configuredFeatures;
854
1036
  for (const headers of [model.headers, options?.headers]) {
855
1037
  for (const [name, value] of Object.entries(headers ?? {})) {
@@ -885,9 +1067,11 @@ function getBetaFeatures(model, context, isOAuthToken, nativeToolChanges, option
885
1067
  }
886
1068
  if (nativeToolChanges)
887
1069
  features.push(INLINE_TOOLS_BETA);
1070
+ if (messageThreads)
1071
+ features.push(MESSAGE_THREADS_BETA);
888
1072
  return [...new Set(features)];
889
1073
  }
890
- function buildParams(model, context, isOAuthToken, options, disablePreferredStrictTools = false) {
1074
+ function buildParams(model, context, isOAuthToken, options, flags) {
891
1075
  const { cacheControl } = getCacheControl(model, options?.cacheRetention, options?.env);
892
1076
  const compat = getAnthropicCompat(model);
893
1077
  const initialSystemMessage = getInitialSystemMessage(context.messages);
@@ -900,11 +1084,8 @@ function buildParams(model, context, isOAuthToken, options, disablePreferredStri
900
1084
  // active tool to anchor the placeholder. Otherwise the current tool list is sent.
901
1085
  let hasPreferredStrictTools = false;
902
1086
  const convertToolDefinitions = (tools, toolCacheControl) => {
903
- const definitions = convertTools(tools, isOAuthToken, compat.supportsEagerToolInputStreaming, compat.supportsStrictTools, toolCacheControl, disablePreferredStrictTools);
904
- if (definitions.some((definition, index) => {
905
- const config = tools[index].constrainedSampling;
906
- return definition.strict === true && config && config.type === "json_schema" && config.strict === "prefer";
907
- })) {
1087
+ const definitions = convertTools(tools, isOAuthToken, compat.supportsEagerToolInputStreaming, compat.supportsStrictTools, toolCacheControl, flags.disablePreferredStrictTools);
1088
+ if (definitions.some((definition, index) => definition.strict === true && prefersJsonSchemaStrictSampling(tools[index]))) {
908
1089
  hasPreferredStrictTools = true;
909
1090
  }
910
1091
  return definitions;
@@ -916,7 +1097,11 @@ function buildParams(model, context, isOAuthToken, options, disablePreferredStri
916
1097
  const effortMessages = model.compat?.supportsMidConvoEffort === true && !thinkingDisabled;
917
1098
  const converted = convertMessages(conversationMessages, isOAuthToken, cacheControl, compat.allowEmptySignature, effortMessages ? model.provider : undefined, nativeToolChanges ? convertToolDefinitions : undefined);
918
1099
  const activeEffort = options?.effort ?? "high";
919
- const betaFeatures = getBetaFeatures(model, context, isOAuthToken, nativeToolChanges, options);
1100
+ const betaFeatures = getBetaFeatures(model, context, options, {
1101
+ isOAuthToken,
1102
+ nativeToolChanges,
1103
+ messageThreads: flags.messageThreads,
1104
+ });
920
1105
  const params = {
921
1106
  model: model.id,
922
1107
  messages: effortMessages ? insertThinkingLevelMessages(converted, activeEffort) : converted.messages,
@@ -985,7 +1170,10 @@ function buildParams(model, context, isOAuthToken, options, disablePreferredStri
985
1170
  display: options?.thinkingDisplay ?? "summarized",
986
1171
  block_binding: { prefix_mismatch_behavior: "drop_block" },
987
1172
  };
988
- params.output_config = { effort: "high" };
1173
+ // Effort markers set the level for every reply. The top-level effort is part of the cached
1174
+ // prompt, so it stays fixed. It is "low" because organizations can cap effort and reject
1175
+ // requests containing any level above the cap.
1176
+ params.output_config = { effort: "low" };
989
1177
  }
990
1178
  else if (model.reasoning && options?.thinkingEnabled) {
991
1179
  // Default to "summarized" so Opus 4.7 and Mythos Preview behave like
@@ -1045,7 +1233,9 @@ convertToolDefinitions) {
1045
1233
  const params = [];
1046
1234
  const assistantLevels = new Map();
1047
1235
  // Later system messages are held back and emitted directly before the next assistant
1048
- // message (or at the end of the transcript). Anthropic requires `tool_result` blocks to
1236
+ // message that is sent (or at the end of the transcript). Assistant messages that convert to
1237
+ // no blocks are dropped, so holding past them keeps an update from landing before a user
1238
+ // message (#11091). Anthropic requires `tool_result` blocks to
1049
1239
  // immediately follow their `tool_use`, so a system message between them is rejected; this
1050
1240
  // also mirrors where the managed-effort system messages are inserted. As a result an
1051
1241
  // update placed before a user message in the transcript lands after it on the wire.
@@ -1118,7 +1308,6 @@ convertToolDefinitions) {
1118
1308
  }
1119
1309
  }
1120
1310
  else if (msg.role === "assistant") {
1121
- flushPendingSystemMessages();
1122
1311
  const blocks = [];
1123
1312
  for (const block of msg.content) {
1124
1313
  if (block.type === "text") {
@@ -1176,6 +1365,7 @@ convertToolDefinitions) {
1176
1365
  }
1177
1366
  if (blocks.length === 0)
1178
1367
  continue;
1368
+ flushPendingSystemMessages();
1179
1369
  const messageIndex = params.length;
1180
1370
  params.push({
1181
1371
  role: "assistant",
@@ -1299,16 +1489,19 @@ function convertTools(tools, isOAuthToken, supportsEagerToolInputStreaming, supp
1299
1489
  if (!tools)
1300
1490
  return [];
1301
1491
  return tools.map((tool, index) => {
1302
- const config = tool.constrainedSampling;
1303
- const strict = disablePreferredStrictTools && config && config.type === "json_schema" && config.strict === "prefer"
1492
+ const strict = disablePreferredStrictTools && prefersJsonSchemaStrictSampling(tool)
1304
1493
  ? undefined
1305
1494
  : resolveJsonSchemaStrictSampling(tool, supportsStrictTools, isAnthropicStrictUnsupportedKeyword);
1306
1495
  const parameters = getJsonSchemaToolParameters(tool, strict);
1307
1496
  const schema = parameters;
1497
+ // Keep shared definitions so property $refs still resolve. Other root keywords stay out:
1498
+ // Anthropic rejects root anyOf/oneOf/allOf with a 400.
1308
1499
  const legacyInputSchema = {
1309
1500
  type: "object",
1310
1501
  properties: schema.properties ?? {},
1311
1502
  required: schema.required ?? [],
1503
+ ...(schema.$defs === undefined ? {} : { $defs: schema.$defs }),
1504
+ ...(schema.definitions === undefined ? {} : { definitions: schema.definitions }),
1312
1505
  };
1313
1506
  const inputSchema = strict === true
1314
1507
  ? {