@aryee337/aery-ai 0.2.27 → 0.2.29

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 (417) hide show
  1. package/CHANGELOG.md +2914 -0
  2. package/README.md +614 -813
  3. package/package.json +140 -105
  4. package/src/api-registry.ts +96 -0
  5. package/src/auth-broker/client.ts +358 -0
  6. package/src/auth-broker/index.ts +5 -0
  7. package/src/auth-broker/refresher.ts +117 -0
  8. package/src/auth-broker/remote-store.ts +623 -0
  9. package/src/auth-broker/server.ts +644 -0
  10. package/src/auth-broker/types.ts +127 -0
  11. package/src/auth-broker/wire-schemas.ts +200 -0
  12. package/src/auth-gateway/http.ts +194 -0
  13. package/src/auth-gateway/index.ts +3 -0
  14. package/src/auth-gateway/server.ts +818 -0
  15. package/src/auth-gateway/types.ts +143 -0
  16. package/src/auth-storage.ts +4422 -0
  17. package/src/index.ts +54 -0
  18. package/src/model-cache.ts +129 -0
  19. package/src/model-manager.ts +469 -0
  20. package/src/model-thinking.ts +782 -0
  21. package/src/models.json +83530 -0
  22. package/src/models.json.d.ts +9 -0
  23. package/src/models.ts +56 -0
  24. package/src/prompts/turn-aborted-guidance.md +4 -0
  25. package/src/provider-details.ts +90 -0
  26. package/src/provider-models/bundled-references.ts +38 -0
  27. package/src/provider-models/descriptors.ts +355 -0
  28. package/src/provider-models/google.ts +88 -0
  29. package/src/provider-models/index.ts +5 -0
  30. package/src/provider-models/ollama.ts +153 -0
  31. package/src/provider-models/openai-compat.ts +2817 -0
  32. package/src/provider-models/special.ts +67 -0
  33. package/src/providers/aery-native-client.ts +228 -0
  34. package/src/providers/aery-native-server.ts +212 -0
  35. package/src/providers/amazon-bedrock.ts +873 -0
  36. package/src/providers/anthropic-client.ts +318 -0
  37. package/src/providers/anthropic-messages-server-schema.ts +243 -0
  38. package/src/providers/anthropic-messages-server.ts +683 -0
  39. package/src/providers/anthropic-wire.ts +268 -0
  40. package/src/providers/anthropic.ts +3094 -0
  41. package/src/providers/aws-credentials.ts +501 -0
  42. package/src/providers/aws-eventstream.ts +185 -0
  43. package/src/providers/aws-sigv4.ts +218 -0
  44. package/src/providers/azure-openai-responses.ts +361 -0
  45. package/src/providers/cursor/gen/agent_pb.ts +15274 -0
  46. package/src/providers/cursor/proto/agent.proto +3526 -0
  47. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  48. package/src/providers/cursor/proto/buf.yaml +17 -0
  49. package/src/providers/cursor.ts +2621 -0
  50. package/src/providers/error-message.ts +21 -0
  51. package/src/providers/github-copilot-headers.ts +140 -0
  52. package/src/providers/gitlab-duo.ts +372 -0
  53. package/src/providers/google-auth.ts +252 -0
  54. package/src/providers/google-gemini-cli.ts +809 -0
  55. package/src/providers/google-gemini-headers.ts +41 -0
  56. package/src/providers/google-shared.ts +917 -0
  57. package/src/providers/google-types.ts +167 -0
  58. package/src/providers/google-vertex.ts +91 -0
  59. package/src/providers/google.ts +41 -0
  60. package/src/providers/grammar.ts +70 -0
  61. package/src/providers/kimi.ts +52 -0
  62. package/src/providers/mock.ts +496 -0
  63. package/src/providers/ollama.ts +644 -0
  64. package/src/providers/openai-anthropic-shim.ts +138 -0
  65. package/src/providers/openai-chat-server-schema.ts +252 -0
  66. package/src/providers/openai-chat-server.ts +647 -0
  67. package/src/providers/openai-codex/constants.ts +43 -0
  68. package/src/providers/openai-codex/request-transformer.ts +161 -0
  69. package/src/providers/openai-codex/response-handler.ts +81 -0
  70. package/src/providers/openai-codex-responses.ts +3018 -0
  71. package/src/providers/openai-completions-compat.ts +300 -0
  72. package/src/providers/openai-completions.ts +1979 -0
  73. package/src/providers/openai-responses-server-schema.ts +290 -0
  74. package/src/providers/openai-responses-server.ts +1183 -0
  75. package/src/providers/openai-responses-shared.ts +873 -0
  76. package/src/providers/openai-responses.ts +679 -0
  77. package/src/providers/register-builtins.ts +436 -0
  78. package/src/providers/synthetic.ts +50 -0
  79. package/src/providers/transform-messages.ts +382 -0
  80. package/src/providers/vision-guard.ts +31 -0
  81. package/src/providers/xai-responses.ts +82 -0
  82. package/src/rate-limit-utils.ts +84 -0
  83. package/src/stream.ts +1065 -0
  84. package/src/types.ts +944 -0
  85. package/src/usage/claude.ts +482 -0
  86. package/src/usage/gemini.ts +250 -0
  87. package/src/usage/github-copilot.ts +421 -0
  88. package/src/usage/google-antigravity.ts +201 -0
  89. package/src/usage/kimi.ts +271 -0
  90. package/src/usage/minimax-code.ts +31 -0
  91. package/src/usage/openai-codex.ts +503 -0
  92. package/src/usage/shared.ts +10 -0
  93. package/src/usage/zai.ts +247 -0
  94. package/src/usage.ts +185 -0
  95. package/src/utils/abort.ts +51 -0
  96. package/src/utils/abortable-iterator.ts +69 -0
  97. package/src/utils/anthropic-auth.ts +93 -0
  98. package/src/utils/discovery/antigravity.ts +261 -0
  99. package/src/utils/discovery/codex.ts +371 -0
  100. package/src/utils/discovery/cursor.ts +306 -0
  101. package/src/utils/discovery/gemini.ts +248 -0
  102. package/src/utils/discovery/index.ts +4 -0
  103. package/src/utils/discovery/openai-compatible.ts +224 -0
  104. package/src/utils/event-stream.ts +142 -0
  105. package/src/utils/fireworks-model-id.ts +30 -0
  106. package/src/utils/foundry.ts +8 -0
  107. package/src/utils/http-inspector.ts +176 -0
  108. package/src/utils/idle-iterator.ts +267 -0
  109. package/src/utils/json-parse.ts +182 -0
  110. package/src/utils/oauth/__tests__/xai-oauth.test.ts +107 -0
  111. package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
  112. package/src/utils/oauth/anthropic.ts +273 -0
  113. package/src/utils/oauth/api-key-login.ts +87 -0
  114. package/src/utils/oauth/api-key-validation.ts +92 -0
  115. package/src/utils/oauth/callback-server.ts +276 -0
  116. package/src/utils/oauth/cerebras.ts +16 -0
  117. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  118. package/src/utils/oauth/cursor.ts +157 -0
  119. package/src/utils/oauth/deepseek.ts +53 -0
  120. package/src/utils/oauth/firepass.ts +24 -0
  121. package/src/utils/oauth/fireworks.ts +15 -0
  122. package/src/utils/oauth/github-copilot.ts +362 -0
  123. package/src/utils/oauth/gitlab-duo.ts +123 -0
  124. package/src/utils/oauth/google-antigravity.ts +200 -0
  125. package/src/utils/oauth/google-gemini-cli.ts +256 -0
  126. package/src/utils/oauth/google-oauth-shared.ts +110 -0
  127. package/src/utils/oauth/huggingface.ts +62 -0
  128. package/src/utils/oauth/index.ts +484 -0
  129. package/src/utils/oauth/kagi.ts +47 -0
  130. package/src/utils/oauth/kilo.ts +87 -0
  131. package/src/utils/oauth/kimi.ts +254 -0
  132. package/src/utils/oauth/litellm.ts +47 -0
  133. package/src/utils/oauth/lm-studio.ts +38 -0
  134. package/src/utils/oauth/minimax-code.ts +78 -0
  135. package/src/utils/oauth/moonshot.ts +23 -0
  136. package/src/utils/oauth/nanogpt.ts +15 -0
  137. package/src/utils/oauth/nvidia.ts +70 -0
  138. package/src/utils/oauth/oauth.html +203 -0
  139. package/src/utils/oauth/ollama-cloud.ts +28 -0
  140. package/src/utils/oauth/ollama.ts +47 -0
  141. package/src/utils/oauth/openai-codex.ts +299 -0
  142. package/src/utils/oauth/opencode.ts +49 -0
  143. package/src/utils/oauth/openrouter.ts +20 -0
  144. package/src/utils/oauth/parallel.ts +46 -0
  145. package/src/utils/oauth/perplexity.ts +206 -0
  146. package/src/utils/oauth/pkce.ts +18 -0
  147. package/src/utils/oauth/qianfan.ts +58 -0
  148. package/src/utils/oauth/qwen-portal.ts +60 -0
  149. package/src/utils/oauth/synthetic.ts +15 -0
  150. package/src/utils/oauth/tavily.ts +46 -0
  151. package/src/utils/oauth/together.ts +16 -0
  152. package/src/utils/oauth/types.ts +99 -0
  153. package/src/utils/oauth/venice.ts +59 -0
  154. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  155. package/src/utils/oauth/vllm.ts +40 -0
  156. package/src/utils/oauth/wafer.ts +50 -0
  157. package/src/utils/oauth/xai-oauth.ts +342 -0
  158. package/src/utils/oauth/xiaomi.ts +139 -0
  159. package/src/utils/oauth/zai.ts +60 -0
  160. package/src/utils/oauth/zenmux.ts +15 -0
  161. package/src/utils/oauth/zhipu.ts +60 -0
  162. package/src/utils/overflow.ts +137 -0
  163. package/src/utils/parse-bind.ts +54 -0
  164. package/src/utils/provider-response.ts +30 -0
  165. package/src/utils/request-debug.ts +336 -0
  166. package/src/utils/retry-after.ts +110 -0
  167. package/src/utils/retry.ts +54 -0
  168. package/src/utils/schema/CONSTRAINTS.md +164 -0
  169. package/src/utils/schema/adapt.ts +36 -0
  170. package/src/utils/schema/compatibility.ts +435 -0
  171. package/src/utils/schema/dereference.ts +98 -0
  172. package/src/utils/schema/draft.ts +341 -0
  173. package/src/utils/schema/equality.ts +97 -0
  174. package/src/utils/schema/fields.ts +191 -0
  175. package/src/utils/schema/index.ts +13 -0
  176. package/src/utils/schema/json-schema-validator.ts +577 -0
  177. package/src/utils/schema/meta-validator.ts +167 -0
  178. package/src/utils/schema/normalize.ts +1588 -0
  179. package/src/utils/schema/spill.ts +43 -0
  180. package/src/utils/schema/stamps.ts +97 -0
  181. package/src/utils/schema/types.ts +10 -0
  182. package/src/utils/schema/wire.ts +293 -0
  183. package/src/utils/schema/zod-decontaminate.ts +331 -0
  184. package/src/utils/sdk-stream-timeout.ts +43 -0
  185. package/src/utils/sse-debug.ts +289 -0
  186. package/src/utils/stream-markup-healing.ts +612 -0
  187. package/src/utils/tool-choice.ts +99 -0
  188. package/src/utils/validation.ts +1024 -0
  189. package/src/utils.ts +166 -0
  190. package/dist/api-registry.d.ts +0 -20
  191. package/dist/api-registry.d.ts.map +0 -1
  192. package/dist/api-registry.js +0 -44
  193. package/dist/api-registry.js.map +0 -1
  194. package/dist/bedrock-provider.d.ts +0 -5
  195. package/dist/bedrock-provider.d.ts.map +0 -1
  196. package/dist/bedrock-provider.js +0 -6
  197. package/dist/bedrock-provider.js.map +0 -1
  198. package/dist/cli.d.ts +0 -3
  199. package/dist/cli.d.ts.map +0 -1
  200. package/dist/cli.js +0 -130
  201. package/dist/cli.js.map +0 -1
  202. package/dist/env-api-keys.d.ts +0 -18
  203. package/dist/env-api-keys.d.ts.map +0 -1
  204. package/dist/env-api-keys.js +0 -178
  205. package/dist/env-api-keys.js.map +0 -1
  206. package/dist/image-models.d.ts +0 -10
  207. package/dist/image-models.d.ts.map +0 -1
  208. package/dist/image-models.generated.d.ts +0 -440
  209. package/dist/image-models.generated.d.ts.map +0 -1
  210. package/dist/image-models.generated.js +0 -442
  211. package/dist/image-models.generated.js.map +0 -1
  212. package/dist/image-models.js +0 -23
  213. package/dist/image-models.js.map +0 -1
  214. package/dist/images-api-registry.d.ts +0 -14
  215. package/dist/images-api-registry.d.ts.map +0 -1
  216. package/dist/images-api-registry.js +0 -22
  217. package/dist/images-api-registry.js.map +0 -1
  218. package/dist/images.d.ts +0 -4
  219. package/dist/images.d.ts.map +0 -1
  220. package/dist/images.js +0 -14
  221. package/dist/images.js.map +0 -1
  222. package/dist/index.d.ts +0 -32
  223. package/dist/index.d.ts.map +0 -1
  224. package/dist/index.js +0 -20
  225. package/dist/index.js.map +0 -1
  226. package/dist/models.d.ts +0 -18
  227. package/dist/models.d.ts.map +0 -1
  228. package/dist/models.generated.d.ts +0 -17707
  229. package/dist/models.generated.d.ts.map +0 -1
  230. package/dist/models.generated.js +0 -16561
  231. package/dist/models.generated.js.map +0 -1
  232. package/dist/models.js +0 -71
  233. package/dist/models.js.map +0 -1
  234. package/dist/oauth.d.ts +0 -2
  235. package/dist/oauth.d.ts.map +0 -1
  236. package/dist/oauth.js +0 -2
  237. package/dist/oauth.js.map +0 -1
  238. package/dist/providers/aery-error-formatting.d.ts +0 -13
  239. package/dist/providers/aery-error-formatting.d.ts.map +0 -1
  240. package/dist/providers/aery-error-formatting.js +0 -112
  241. package/dist/providers/aery-error-formatting.js.map +0 -1
  242. package/dist/providers/amazon-bedrock.d.ts +0 -38
  243. package/dist/providers/amazon-bedrock.d.ts.map +0 -1
  244. package/dist/providers/amazon-bedrock.js +0 -763
  245. package/dist/providers/amazon-bedrock.js.map +0 -1
  246. package/dist/providers/anthropic.d.ts +0 -71
  247. package/dist/providers/anthropic.d.ts.map +0 -1
  248. package/dist/providers/anthropic.js +0 -949
  249. package/dist/providers/anthropic.js.map +0 -1
  250. package/dist/providers/azure-openai-responses.d.ts +0 -15
  251. package/dist/providers/azure-openai-responses.d.ts.map +0 -1
  252. package/dist/providers/azure-openai-responses.js +0 -225
  253. package/dist/providers/azure-openai-responses.js.map +0 -1
  254. package/dist/providers/cloudflare.d.ts +0 -13
  255. package/dist/providers/cloudflare.d.ts.map +0 -1
  256. package/dist/providers/cloudflare.js +0 -26
  257. package/dist/providers/cloudflare.js.map +0 -1
  258. package/dist/providers/faux.d.ts +0 -56
  259. package/dist/providers/faux.d.ts.map +0 -1
  260. package/dist/providers/faux.js +0 -368
  261. package/dist/providers/faux.js.map +0 -1
  262. package/dist/providers/github-copilot-headers.d.ts +0 -8
  263. package/dist/providers/github-copilot-headers.d.ts.map +0 -1
  264. package/dist/providers/github-copilot-headers.js +0 -29
  265. package/dist/providers/github-copilot-headers.js.map +0 -1
  266. package/dist/providers/google-gemini-cli.d.ts +0 -74
  267. package/dist/providers/google-gemini-cli.d.ts.map +0 -1
  268. package/dist/providers/google-gemini-cli.js +0 -779
  269. package/dist/providers/google-gemini-cli.js.map +0 -1
  270. package/dist/providers/google-shared.d.ts +0 -70
  271. package/dist/providers/google-shared.d.ts.map +0 -1
  272. package/dist/providers/google-shared.js +0 -329
  273. package/dist/providers/google-shared.js.map +0 -1
  274. package/dist/providers/google-vertex.d.ts +0 -15
  275. package/dist/providers/google-vertex.d.ts.map +0 -1
  276. package/dist/providers/google-vertex.js +0 -442
  277. package/dist/providers/google-vertex.js.map +0 -1
  278. package/dist/providers/google.d.ts +0 -13
  279. package/dist/providers/google.d.ts.map +0 -1
  280. package/dist/providers/google.js +0 -400
  281. package/dist/providers/google.js.map +0 -1
  282. package/dist/providers/images/openrouter.d.ts +0 -3
  283. package/dist/providers/images/openrouter.d.ts.map +0 -1
  284. package/dist/providers/images/openrouter.js +0 -129
  285. package/dist/providers/images/openrouter.js.map +0 -1
  286. package/dist/providers/images/register-builtins.d.ts +0 -4
  287. package/dist/providers/images/register-builtins.d.ts.map +0 -1
  288. package/dist/providers/images/register-builtins.js +0 -34
  289. package/dist/providers/images/register-builtins.js.map +0 -1
  290. package/dist/providers/mistral.d.ts +0 -25
  291. package/dist/providers/mistral.d.ts.map +0 -1
  292. package/dist/providers/mistral.js +0 -535
  293. package/dist/providers/mistral.js.map +0 -1
  294. package/dist/providers/openai-codex-responses.d.ts +0 -30
  295. package/dist/providers/openai-codex-responses.d.ts.map +0 -1
  296. package/dist/providers/openai-codex-responses.js +0 -1090
  297. package/dist/providers/openai-codex-responses.js.map +0 -1
  298. package/dist/providers/openai-completions.d.ts +0 -19
  299. package/dist/providers/openai-completions.d.ts.map +0 -1
  300. package/dist/providers/openai-completions.js +0 -950
  301. package/dist/providers/openai-completions.js.map +0 -1
  302. package/dist/providers/openai-prompt-cache.d.ts +0 -3
  303. package/dist/providers/openai-prompt-cache.d.ts.map +0 -1
  304. package/dist/providers/openai-prompt-cache.js +0 -10
  305. package/dist/providers/openai-prompt-cache.js.map +0 -1
  306. package/dist/providers/openai-responses-shared.d.ts +0 -18
  307. package/dist/providers/openai-responses-shared.d.ts.map +0 -1
  308. package/dist/providers/openai-responses-shared.js +0 -492
  309. package/dist/providers/openai-responses-shared.js.map +0 -1
  310. package/dist/providers/openai-responses.d.ts +0 -13
  311. package/dist/providers/openai-responses.d.ts.map +0 -1
  312. package/dist/providers/openai-responses.js +0 -237
  313. package/dist/providers/openai-responses.js.map +0 -1
  314. package/dist/providers/register-builtins.d.ts +0 -38
  315. package/dist/providers/register-builtins.d.ts.map +0 -1
  316. package/dist/providers/register-builtins.js +0 -278
  317. package/dist/providers/register-builtins.js.map +0 -1
  318. package/dist/providers/simple-options.d.ts +0 -8
  319. package/dist/providers/simple-options.d.ts.map +0 -1
  320. package/dist/providers/simple-options.js +0 -41
  321. package/dist/providers/simple-options.js.map +0 -1
  322. package/dist/providers/transform-messages.d.ts +0 -8
  323. package/dist/providers/transform-messages.d.ts.map +0 -1
  324. package/dist/providers/transform-messages.js +0 -184
  325. package/dist/providers/transform-messages.js.map +0 -1
  326. package/dist/session-resources.d.ts +0 -4
  327. package/dist/session-resources.d.ts.map +0 -1
  328. package/dist/session-resources.js +0 -22
  329. package/dist/session-resources.js.map +0 -1
  330. package/dist/stream.d.ts +0 -8
  331. package/dist/stream.d.ts.map +0 -1
  332. package/dist/stream.js +0 -27
  333. package/dist/stream.js.map +0 -1
  334. package/dist/types.d.ts +0 -498
  335. package/dist/types.d.ts.map +0 -1
  336. package/dist/types.js +0 -2
  337. package/dist/types.js.map +0 -1
  338. package/dist/utils/diagnostics.d.ts +0 -19
  339. package/dist/utils/diagnostics.d.ts.map +0 -1
  340. package/dist/utils/diagnostics.js +0 -25
  341. package/dist/utils/diagnostics.js.map +0 -1
  342. package/dist/utils/event-stream.d.ts +0 -21
  343. package/dist/utils/event-stream.d.ts.map +0 -1
  344. package/dist/utils/event-stream.js +0 -81
  345. package/dist/utils/event-stream.js.map +0 -1
  346. package/dist/utils/hash.d.ts +0 -3
  347. package/dist/utils/hash.d.ts.map +0 -1
  348. package/dist/utils/hash.js +0 -14
  349. package/dist/utils/hash.js.map +0 -1
  350. package/dist/utils/headers.d.ts +0 -2
  351. package/dist/utils/headers.d.ts.map +0 -1
  352. package/dist/utils/headers.js +0 -8
  353. package/dist/utils/headers.js.map +0 -1
  354. package/dist/utils/json-parse.d.ts +0 -16
  355. package/dist/utils/json-parse.d.ts.map +0 -1
  356. package/dist/utils/json-parse.js +0 -113
  357. package/dist/utils/json-parse.js.map +0 -1
  358. package/dist/utils/node-http-proxy.d.ts +0 -10
  359. package/dist/utils/node-http-proxy.d.ts.map +0 -1
  360. package/dist/utils/node-http-proxy.js +0 -97
  361. package/dist/utils/node-http-proxy.js.map +0 -1
  362. package/dist/utils/oauth/anthropic.d.ts +0 -25
  363. package/dist/utils/oauth/anthropic.d.ts.map +0 -1
  364. package/dist/utils/oauth/anthropic.js +0 -335
  365. package/dist/utils/oauth/anthropic.js.map +0 -1
  366. package/dist/utils/oauth/device-code.d.ts +0 -19
  367. package/dist/utils/oauth/device-code.d.ts.map +0 -1
  368. package/dist/utils/oauth/device-code.js +0 -55
  369. package/dist/utils/oauth/device-code.js.map +0 -1
  370. package/dist/utils/oauth/github-copilot.d.ts +0 -30
  371. package/dist/utils/oauth/github-copilot.d.ts.map +0 -1
  372. package/dist/utils/oauth/github-copilot.js +0 -268
  373. package/dist/utils/oauth/github-copilot.js.map +0 -1
  374. package/dist/utils/oauth/google-antigravity.d.ts +0 -26
  375. package/dist/utils/oauth/google-antigravity.d.ts.map +0 -1
  376. package/dist/utils/oauth/google-antigravity.js +0 -377
  377. package/dist/utils/oauth/google-antigravity.js.map +0 -1
  378. package/dist/utils/oauth/google-gemini-cli.d.ts +0 -26
  379. package/dist/utils/oauth/google-gemini-cli.d.ts.map +0 -1
  380. package/dist/utils/oauth/google-gemini-cli.js +0 -482
  381. package/dist/utils/oauth/google-gemini-cli.js.map +0 -1
  382. package/dist/utils/oauth/index.d.ts +0 -63
  383. package/dist/utils/oauth/index.d.ts.map +0 -1
  384. package/dist/utils/oauth/index.js +0 -131
  385. package/dist/utils/oauth/index.js.map +0 -1
  386. package/dist/utils/oauth/oauth-page.d.ts +0 -3
  387. package/dist/utils/oauth/oauth-page.d.ts.map +0 -1
  388. package/dist/utils/oauth/oauth-page.js +0 -105
  389. package/dist/utils/oauth/oauth-page.js.map +0 -1
  390. package/dist/utils/oauth/openai-codex.d.ts +0 -34
  391. package/dist/utils/oauth/openai-codex.d.ts.map +0 -1
  392. package/dist/utils/oauth/openai-codex.js +0 -385
  393. package/dist/utils/oauth/openai-codex.js.map +0 -1
  394. package/dist/utils/oauth/pkce.d.ts +0 -13
  395. package/dist/utils/oauth/pkce.d.ts.map +0 -1
  396. package/dist/utils/oauth/pkce.js +0 -31
  397. package/dist/utils/oauth/pkce.js.map +0 -1
  398. package/dist/utils/oauth/types.d.ts +0 -64
  399. package/dist/utils/oauth/types.d.ts.map +0 -1
  400. package/dist/utils/oauth/types.js +0 -2
  401. package/dist/utils/oauth/types.js.map +0 -1
  402. package/dist/utils/overflow.d.ts +0 -56
  403. package/dist/utils/overflow.d.ts.map +0 -1
  404. package/dist/utils/overflow.js +0 -151
  405. package/dist/utils/overflow.js.map +0 -1
  406. package/dist/utils/sanitize-unicode.d.ts +0 -22
  407. package/dist/utils/sanitize-unicode.d.ts.map +0 -1
  408. package/dist/utils/sanitize-unicode.js +0 -26
  409. package/dist/utils/sanitize-unicode.js.map +0 -1
  410. package/dist/utils/typebox-helpers.d.ts +0 -17
  411. package/dist/utils/typebox-helpers.d.ts.map +0 -1
  412. package/dist/utils/typebox-helpers.js +0 -21
  413. package/dist/utils/typebox-helpers.js.map +0 -1
  414. package/dist/utils/validation.d.ts +0 -18
  415. package/dist/utils/validation.d.ts.map +0 -1
  416. package/dist/utils/validation.js +0 -281
  417. package/dist/utils/validation.js.map +0 -1
@@ -0,0 +1,873 @@
1
+ /**
2
+ * Amazon Bedrock Converse Stream provider.
3
+ *
4
+ * Talks directly to `bedrock-runtime.{region}.amazonaws.com` over HTTPS with
5
+ * SigV4 signing and decodes the `application/vnd.amazon.eventstream` response.
6
+ * No `@aws-sdk/*`, no `@smithy/*`, no `proxy-agent`. Proxies are honored via
7
+ * Bun's native `HTTPS_PROXY` support.
8
+ */
9
+
10
+ import { $env, $flag, extractHttpStatusFromError, fetchWithRetry } from "@aryee337/aery-utils";
11
+ import type { Effort } from "../model-thinking";
12
+ import { mapEffortToAnthropicAdaptiveEffort, requireSupportedEffort } from "../model-thinking";
13
+ import { calculateCost } from "../models";
14
+ import type {
15
+ Api,
16
+ AssistantMessage,
17
+ CacheRetention,
18
+ Context,
19
+ Model,
20
+ StopReason,
21
+ StreamFunction,
22
+ StreamOptions,
23
+ TextContent,
24
+ ThinkingBudgets,
25
+ ThinkingContent,
26
+ Tool,
27
+ ToolCall,
28
+ ToolResultMessage,
29
+ } from "../types";
30
+ import { normalizeToolCallId, resolveCacheRetention } from "../utils";
31
+ import { AssistantMessageEventStream } from "../utils/event-stream";
32
+ import { appendRawHttpRequestDumpFor400, type RawHttpRequestDump, withHttpStatus } from "../utils/http-inspector";
33
+ import { parseStreamingJson, parseStreamingJsonThrottled } from "../utils/json-parse";
34
+ import { toolWireSchema } from "../utils/schema/wire";
35
+ import { resolveAwsCredentials } from "./aws-credentials";
36
+ import { decodeEventStream } from "./aws-eventstream";
37
+ import { signRequest } from "./aws-sigv4";
38
+ import { transformMessages } from "./transform-messages";
39
+
40
+ export type BedrockThinkingDisplay = "summarized" | "omitted";
41
+
42
+ export interface BedrockOptions extends StreamOptions {
43
+ region?: string;
44
+ profile?: string;
45
+ /** Amazon Bedrock API key sent as `Authorization: Bearer`, ahead of SigV4 credential resolution. */
46
+ bearerToken?: string;
47
+ toolChoice?: "auto" | "any" | "none" | { type: "tool"; name: string };
48
+ /* See https://docs.aws.amazon.com/bedrock/latest/userguide/inference-reasoning.html for supported models. */
49
+ reasoning?: Effort;
50
+ /* Custom token budgets per thinking level. Overrides default budgets. */
51
+ thinkingBudgets?: ThinkingBudgets;
52
+ /* Only supported by Claude 4.x models, see https://docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-extended-thinking.html#claude-messages-extended-thinking-tool-use-interleaved */
53
+ interleavedThinking?: boolean;
54
+ /**
55
+ * Controls how Claude returns thinking content in Bedrock responses.
56
+ * - `"summarized"`: thinking blocks include human-readable summaries (default here).
57
+ * - `"omitted"`: thinking content is suppressed; the encrypted signature still
58
+ * travels back for multi-turn continuity.
59
+ *
60
+ * Starting with Claude Opus 4.7 the Anthropic API default is `"omitted"`, which
61
+ * leaves callers waiting on a silent stream during long reasoning runs (issue
62
+ * #1373). We default to `"summarized"` so adaptive-thinking models that accept
63
+ * the field keep producing visible thinking deltas. Older adaptive-thinking
64
+ * models (Opus 4.6, Sonnet 4.6+) reject the field, so we omit it for them.
65
+ */
66
+ thinkingDisplay?: BedrockThinkingDisplay;
67
+ }
68
+ const AUTHENTICATED_API_KEY_SENTINEL = "<authenticated>";
69
+
70
+ function resolveBearerToken(options: BedrockOptions): string | undefined {
71
+ const apiKey = options.apiKey === AUTHENTICATED_API_KEY_SENTINEL ? undefined : options.apiKey;
72
+ return options.bearerToken || apiKey || $env.AWS_BEARER_TOKEN_BEDROCK;
73
+ }
74
+
75
+ type Block = (TextContent | ThinkingContent | ToolCall) & {
76
+ index?: number;
77
+ partialJson?: string;
78
+ lastParseLen?: number;
79
+ };
80
+
81
+ // ---------- Bedrock wire-format types ----------
82
+ // Mirrors only what we actually consume from `ConverseStreamRequest` /
83
+ // `ConverseStreamOutput`. Keeps us decoupled from `@aws-sdk/client-bedrock-runtime`.
84
+
85
+ interface CachePoint {
86
+ cachePoint: { type: "default"; ttl?: "5m" | "1h" };
87
+ }
88
+ interface TextBlockWire {
89
+ text: string;
90
+ }
91
+ interface ImageBlockWire {
92
+ image: { format: "jpeg" | "png" | "gif" | "webp"; source: { bytes: string } };
93
+ }
94
+ interface ToolUseBlockWire {
95
+ toolUse: { toolUseId: string; name: string; input: unknown };
96
+ }
97
+ interface ToolResultBlockWire {
98
+ toolResult: {
99
+ toolUseId: string;
100
+ content: Array<TextBlockWire | ImageBlockWire>;
101
+ status: "success" | "error";
102
+ };
103
+ }
104
+ interface ReasoningBlockWire {
105
+ reasoningContent: { reasoningText: { text: string; signature?: string } };
106
+ }
107
+
108
+ type UserContent = TextBlockWire | ImageBlockWire | ToolResultBlockWire | CachePoint;
109
+ type AssistantContent = TextBlockWire | ToolUseBlockWire | ReasoningBlockWire;
110
+ type SystemContent = TextBlockWire | CachePoint;
111
+
112
+ interface WireMessage {
113
+ role: "user" | "assistant";
114
+ content: Array<UserContent | AssistantContent>;
115
+ }
116
+
117
+ interface WireToolSpec {
118
+ toolSpec: { name: string; description: string; inputSchema: { json: unknown } };
119
+ }
120
+ interface WireToolChoice {
121
+ auto?: Record<string, never>;
122
+ any?: Record<string, never>;
123
+ tool?: { name: string };
124
+ }
125
+ interface WireToolConfig {
126
+ tools: WireToolSpec[];
127
+ toolChoice?: WireToolChoice;
128
+ }
129
+
130
+ interface ConverseStreamRequest {
131
+ messages: WireMessage[];
132
+ system?: SystemContent[];
133
+ inferenceConfig?: { maxTokens?: number; temperature?: number; topP?: number };
134
+ toolConfig?: WireToolConfig;
135
+ additionalModelRequestFields?: Record<string, unknown>;
136
+ }
137
+
138
+ // Streaming events (snake_case matches the JSON envelope key, but Bedrock uses camelCase).
139
+ interface MessageStartEvent {
140
+ role: "user" | "assistant";
141
+ }
142
+ interface ContentBlockStartEvent {
143
+ contentBlockIndex: number;
144
+ start?: { toolUse?: { toolUseId?: string; name?: string } };
145
+ }
146
+ interface ContentBlockDeltaEvent {
147
+ contentBlockIndex: number;
148
+ delta?: {
149
+ text?: string;
150
+ toolUse?: { input?: string };
151
+ reasoningContent?: { text?: string; signature?: string };
152
+ };
153
+ }
154
+ interface ContentBlockStopEvent {
155
+ contentBlockIndex: number;
156
+ }
157
+ interface MessageStopEvent {
158
+ stopReason?: string;
159
+ }
160
+ interface MetadataEvent {
161
+ usage?: {
162
+ inputTokens?: number;
163
+ outputTokens?: number;
164
+ cacheReadInputTokens?: number;
165
+ cacheWriteInputTokens?: number;
166
+ totalTokens?: number;
167
+ };
168
+ }
169
+
170
+ export const streamBedrock: StreamFunction<"bedrock-converse-stream"> = (
171
+ model: Model<"bedrock-converse-stream">,
172
+ context: Context,
173
+ options: BedrockOptions,
174
+ ): AssistantMessageEventStream => {
175
+ const stream = new AssistantMessageEventStream();
176
+
177
+ (async () => {
178
+ const startTime = Date.now();
179
+ let firstTokenTime: number | undefined;
180
+
181
+ const output: AssistantMessage = {
182
+ role: "assistant",
183
+ content: [],
184
+ api: "bedrock-converse-stream" as Api,
185
+ provider: model.provider,
186
+ model: model.id,
187
+ usage: {
188
+ input: 0,
189
+ output: 0,
190
+ cacheRead: 0,
191
+ cacheWrite: 0,
192
+ totalTokens: 0,
193
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
194
+ },
195
+ stopReason: "stop",
196
+ timestamp: Date.now(),
197
+ };
198
+
199
+ const blocks = output.content as Block[];
200
+ let rawRequestDump: RawHttpRequestDump | undefined;
201
+ const region = options.region || $env.AWS_REGION || $env.AWS_DEFAULT_REGION || "us-east-1";
202
+
203
+ try {
204
+ const cacheRetention = resolveCacheRetention(options.cacheRetention);
205
+ const toolConfig = convertToolConfig(context.tools, options.toolChoice);
206
+ let additionalModelRequestFields = buildAdditionalModelRequestFields(model, options);
207
+
208
+ // Bedrock rejects thinking + forced tool_choice ("any" or specific tool).
209
+ // When tool_choice forces tool use, disable thinking to avoid API errors.
210
+ if (toolConfig?.toolChoice && additionalModelRequestFields) {
211
+ const tc = toolConfig.toolChoice;
212
+ if (tc.any || tc.tool) additionalModelRequestFields = undefined;
213
+ }
214
+
215
+ const commandInput: ConverseStreamRequest = {
216
+ messages: convertMessages(context, model, cacheRetention),
217
+ system: buildSystemPrompt(context.systemPrompt, model, cacheRetention),
218
+ inferenceConfig: {
219
+ maxTokens: options.maxTokens,
220
+ temperature: options.temperature,
221
+ topP: options.topP,
222
+ },
223
+ toolConfig,
224
+ additionalModelRequestFields,
225
+ };
226
+ options?.onPayload?.(commandInput);
227
+
228
+ const host = `bedrock-runtime.${region}.amazonaws.com`;
229
+ const url = `https://${host}/model/${encodeURIComponent(model.id)}/converse-stream`;
230
+ const urlPath = `/model/${encodeURIComponent(model.id)}/converse-stream`;
231
+ rawRequestDump = {
232
+ provider: model.provider,
233
+ api: output.api,
234
+ model: model.id,
235
+ method: "POST",
236
+ url,
237
+ body: commandInput,
238
+ };
239
+
240
+ const bodyText = JSON.stringify(commandInput);
241
+ const body = new TextEncoder().encode(bodyText);
242
+ const baseHeaders: Record<string, string> = {
243
+ "content-type": "application/json",
244
+ accept: "application/vnd.amazon.eventstream",
245
+ };
246
+
247
+ const bearerToken = resolveBearerToken(options);
248
+ let requestHeaders: Record<string, string>;
249
+ if (bearerToken) {
250
+ requestHeaders = { ...baseHeaders, Authorization: `Bearer ${bearerToken}` };
251
+ } else {
252
+ let credentials: { accessKeyId: string; secretAccessKey: string; sessionToken?: string };
253
+ if ($flag("AWS_BEDROCK_SKIP_AUTH")) {
254
+ credentials = { accessKeyId: "dummy-access-key", secretAccessKey: "dummy-secret-key" };
255
+ } else {
256
+ credentials = await resolveAwsCredentials({
257
+ profile: options.profile,
258
+ region,
259
+ signal: options.signal,
260
+ });
261
+ }
262
+ const signed = await signRequest({
263
+ method: "POST",
264
+ host,
265
+ path: urlPath,
266
+ body,
267
+ region,
268
+ service: "bedrock",
269
+ credentials,
270
+ headers: baseHeaders,
271
+ });
272
+ requestHeaders = { ...baseHeaders, ...signed };
273
+ }
274
+
275
+ const response = await fetchWithRetry(url, {
276
+ method: "POST",
277
+ headers: requestHeaders,
278
+ body,
279
+ signal: options.signal,
280
+ fetch: options.fetch,
281
+ });
282
+
283
+ if (!response.ok) {
284
+ const errBody = await response.text().catch(() => "");
285
+ throw withHttpStatus(
286
+ new Error(`Bedrock HTTP ${response.status}: ${errBody.slice(0, 1000)}`),
287
+ response.status,
288
+ );
289
+ }
290
+ if (!response.body) throw new Error("Bedrock response has no body");
291
+
292
+ // Track first event for the abort/diagnostic path (currently informational).
293
+ for await (const message of decodeEventStream(response.body)) {
294
+ const messageType = message.headers[":message-type"];
295
+ const eventType = message.headers[":event-type"];
296
+
297
+ if (messageType === "exception") {
298
+ const exceptionType = message.headers[":exception-type"] || "Exception";
299
+ const payload = safeParsePayload(message.payload) as { message?: string } | undefined;
300
+ const errorMessage = payload?.message || new TextDecoder().decode(message.payload);
301
+ const status = exceptionType === "validationException" ? 400 : 0;
302
+ const err = new Error(`${exceptionType}: ${errorMessage}`);
303
+ throw status ? withHttpStatus(err, status) : err;
304
+ }
305
+ if (messageType === "error") {
306
+ const code = message.headers[":error-code"] || "UnknownError";
307
+ const errorMessage = message.headers[":error-message"] || new TextDecoder().decode(message.payload);
308
+ throw new Error(`${code}: ${errorMessage}`);
309
+ }
310
+ if (messageType !== "event") continue;
311
+
312
+ const payload = safeParsePayload(message.payload);
313
+ if (!payload) continue;
314
+
315
+ switch (eventType) {
316
+ case "messageStart": {
317
+ // no-op: first event marker is implicit by stream entry.
318
+ const ev = payload as MessageStartEvent;
319
+ if (ev.role !== "assistant") {
320
+ throw new Error("Unexpected assistant message start but got user message start instead");
321
+ }
322
+ stream.push({ type: "start", partial: output });
323
+ break;
324
+ }
325
+ case "contentBlockStart": {
326
+ if (!firstTokenTime) firstTokenTime = Date.now();
327
+ handleContentBlockStart(payload as ContentBlockStartEvent, blocks, output, stream);
328
+ break;
329
+ }
330
+ case "contentBlockDelta": {
331
+ if (!firstTokenTime) firstTokenTime = Date.now();
332
+ handleContentBlockDelta(payload as ContentBlockDeltaEvent, blocks, output, stream);
333
+ break;
334
+ }
335
+ case "contentBlockStop": {
336
+ handleContentBlockStop(payload as ContentBlockStopEvent, blocks, output, stream);
337
+ break;
338
+ }
339
+ case "messageStop": {
340
+ const ev = payload as MessageStopEvent;
341
+ output.stopReason = mapStopReason(ev.stopReason);
342
+ break;
343
+ }
344
+ case "metadata": {
345
+ handleMetadata(payload as MetadataEvent, model, output);
346
+ break;
347
+ }
348
+ default:
349
+ // Unknown event types (Bedrock may add new ones) — ignore.
350
+ break;
351
+ }
352
+ }
353
+
354
+ if (options.signal?.aborted) throw new Error("Request was aborted");
355
+
356
+ if (output.stopReason === "error" || output.stopReason === "aborted") {
357
+ throw new Error(output.errorMessage ?? "An unknown error occurred");
358
+ }
359
+
360
+ output.duration = Date.now() - startTime;
361
+ if (firstTokenTime) output.ttft = firstTokenTime - startTime;
362
+ stream.push({ type: "done", reason: output.stopReason, message: output });
363
+ stream.end();
364
+ } catch (error) {
365
+ for (const block of output.content) {
366
+ delete (block as Block).index;
367
+ delete (block as Block).partialJson;
368
+ }
369
+ output.stopReason = options.signal?.aborted ? "aborted" : "error";
370
+ output.errorStatus = extractHttpStatusFromError(error);
371
+ const baseMessage = error instanceof Error ? error.message : JSON.stringify(error);
372
+ // Enrich error with thinking block diagnostics for signature-related failures
373
+ let diagnostics = "";
374
+ if (baseMessage.includes("signature") || baseMessage.includes("thinking")) {
375
+ const thinkingBlocks = context.messages
376
+ .filter((m): m is AssistantMessage => m.role === "assistant")
377
+ .flatMap((m, mi) =>
378
+ m.content
379
+ .filter(b => b.type === "thinking")
380
+ .map((b, bi) => ({
381
+ msg: mi,
382
+ block: bi,
383
+ stop: m.stopReason,
384
+ sigLen: b.thinkingSignature?.length ?? -1,
385
+ thinkLen: b.thinking.length,
386
+ })),
387
+ );
388
+ if (thinkingBlocks.length > 0) {
389
+ diagnostics = `\n[thinking-diag] ${JSON.stringify(thinkingBlocks)}`;
390
+ }
391
+ }
392
+ output.errorMessage = await appendRawHttpRequestDumpFor400(baseMessage + diagnostics, error, rawRequestDump);
393
+ output.duration = Date.now() - startTime;
394
+ if (firstTokenTime) output.ttft = firstTokenTime - startTime;
395
+ stream.push({ type: "error", reason: output.stopReason, error: output });
396
+ stream.end();
397
+ }
398
+ })();
399
+
400
+ return stream;
401
+ };
402
+
403
+ function safeParsePayload(payload: Uint8Array): unknown {
404
+ if (payload.length === 0) return {};
405
+ try {
406
+ return JSON.parse(new TextDecoder().decode(payload));
407
+ } catch {
408
+ return undefined;
409
+ }
410
+ }
411
+
412
+ function handleContentBlockStart(
413
+ event: ContentBlockStartEvent,
414
+ blocks: Block[],
415
+ output: AssistantMessage,
416
+ stream: AssistantMessageEventStream,
417
+ ): void {
418
+ const index = event.contentBlockIndex;
419
+ const start = event.start;
420
+
421
+ if (start?.toolUse) {
422
+ const block: Block = {
423
+ type: "toolCall",
424
+ id: normalizeToolCallId(start.toolUse.toolUseId || ""),
425
+ name: start.toolUse.name || "",
426
+ arguments: {},
427
+ partialJson: "",
428
+ index,
429
+ };
430
+ output.content.push(block);
431
+ stream.push({ type: "toolcall_start", contentIndex: blocks.length - 1, partial: output });
432
+ }
433
+ }
434
+
435
+ function handleContentBlockDelta(
436
+ event: ContentBlockDeltaEvent,
437
+ blocks: Block[],
438
+ output: AssistantMessage,
439
+ stream: AssistantMessageEventStream,
440
+ ): void {
441
+ const contentBlockIndex = event.contentBlockIndex;
442
+ const delta = event.delta;
443
+ let index = blocks.findIndex(b => b.index === contentBlockIndex);
444
+ let block = blocks[index];
445
+
446
+ if (delta?.text !== undefined) {
447
+ // If no text block exists yet, create one — `handleContentBlockStart` is not sent for text blocks
448
+ if (!block) {
449
+ const newBlock: Block = { type: "text", text: "", index: contentBlockIndex };
450
+ output.content.push(newBlock);
451
+ index = blocks.length - 1;
452
+ block = blocks[index];
453
+ stream.push({ type: "text_start", contentIndex: index, partial: output });
454
+ }
455
+ if (block.type === "text") {
456
+ block.text += delta.text;
457
+ stream.push({ type: "text_delta", contentIndex: index, delta: delta.text, partial: output });
458
+ }
459
+ } else if (delta?.toolUse && block?.type === "toolCall") {
460
+ block.partialJson = (block.partialJson || "") + (delta.toolUse.input || "");
461
+ const throttled = parseStreamingJsonThrottled(block.partialJson, block.lastParseLen ?? 0);
462
+ if (throttled) {
463
+ block.arguments = throttled.value;
464
+ block.lastParseLen = throttled.parsedLen;
465
+ }
466
+ stream.push({ type: "toolcall_delta", contentIndex: index, delta: delta.toolUse.input || "", partial: output });
467
+ } else if (delta?.reasoningContent) {
468
+ let thinkingBlock = block;
469
+ let thinkingIndex = index;
470
+
471
+ if (!thinkingBlock) {
472
+ const newBlock: Block = { type: "thinking", thinking: "", thinkingSignature: "", index: contentBlockIndex };
473
+ output.content.push(newBlock);
474
+ thinkingIndex = blocks.length - 1;
475
+ thinkingBlock = blocks[thinkingIndex];
476
+ stream.push({ type: "thinking_start", contentIndex: thinkingIndex, partial: output });
477
+ }
478
+
479
+ if (thinkingBlock?.type === "thinking") {
480
+ if (delta.reasoningContent.text) {
481
+ thinkingBlock.thinking += delta.reasoningContent.text;
482
+ stream.push({
483
+ type: "thinking_delta",
484
+ contentIndex: thinkingIndex,
485
+ delta: delta.reasoningContent.text,
486
+ partial: output,
487
+ });
488
+ }
489
+ if (delta.reasoningContent.signature) {
490
+ thinkingBlock.thinkingSignature =
491
+ (thinkingBlock.thinkingSignature || "") + delta.reasoningContent.signature;
492
+ }
493
+ }
494
+ }
495
+ }
496
+
497
+ function handleMetadata(event: MetadataEvent, model: Model<"bedrock-converse-stream">, output: AssistantMessage): void {
498
+ if (event.usage) {
499
+ output.usage.input = event.usage.inputTokens || 0;
500
+ output.usage.output = event.usage.outputTokens || 0;
501
+ output.usage.cacheRead = event.usage.cacheReadInputTokens || 0;
502
+ output.usage.cacheWrite = event.usage.cacheWriteInputTokens || 0;
503
+ output.usage.totalTokens = event.usage.totalTokens || output.usage.input + output.usage.output;
504
+ calculateCost(model, output.usage);
505
+ }
506
+ }
507
+
508
+ function handleContentBlockStop(
509
+ event: ContentBlockStopEvent,
510
+ blocks: Block[],
511
+ output: AssistantMessage,
512
+ stream: AssistantMessageEventStream,
513
+ ): void {
514
+ const index = blocks.findIndex(b => b.index === event.contentBlockIndex);
515
+ const block = blocks[index];
516
+ if (!block) return;
517
+ delete (block as Block).index;
518
+
519
+ switch (block.type) {
520
+ case "text":
521
+ stream.push({ type: "text_end", contentIndex: index, content: block.text, partial: output });
522
+ break;
523
+ case "thinking":
524
+ stream.push({ type: "thinking_end", contentIndex: index, content: block.thinking, partial: output });
525
+ break;
526
+ case "toolCall":
527
+ block.arguments = parseStreamingJson(block.partialJson);
528
+ delete (block as Block).partialJson;
529
+ delete (block as Block).lastParseLen;
530
+ stream.push({ type: "toolcall_end", contentIndex: index, toolCall: block, partial: output });
531
+ break;
532
+ }
533
+ }
534
+
535
+ /**
536
+ * Check if the model supports prompt caching.
537
+ * Supported: Claude 3.5 Haiku, Claude 3.7 Sonnet, Claude 4.x+ models, Haiku 4.5+
538
+ *
539
+ * For base models and system-defined inference profiles the model ID / ARN
540
+ * contains the model name, so we can decide locally.
541
+ *
542
+ * For application inference profiles (whose ARNs don't contain the model name),
543
+ * set AWS_BEDROCK_FORCE_CACHE=1 to enable cache points. Amazon Nova models
544
+ * have automatic caching and don't need explicit cache points.
545
+ */
546
+ function supportsPromptCaching(model: Model<"bedrock-converse-stream">): boolean {
547
+ if (model.cost.cacheRead || model.cost.cacheWrite) return true;
548
+ const id = model.id.toLowerCase();
549
+ // Claude 4.x models (opus-4, sonnet-4, haiku-4)
550
+ if (id.includes("claude") && (id.includes("-4-") || id.includes("-4."))) return true;
551
+ // Claude 3.5 Haiku, Claude 3.7 Sonnet (legacy naming)
552
+ if (id.includes("claude-3-7-sonnet") || id.includes("claude-3-5-haiku")) return true;
553
+ // Claude Haiku 4.5+ (new naming)
554
+ if (id.includes("claude-haiku")) return true;
555
+ // Application inference profiles don't contain the model name in the ARN.
556
+ // Allow users to force cache points via environment variable.
557
+ if (typeof process !== "undefined" && $flag("AWS_BEDROCK_FORCE_CACHE")) return true;
558
+ return false;
559
+ }
560
+
561
+ /**
562
+ * Check if the model supports thinking signatures in reasoningContent.
563
+ * Only Anthropic Claude models support the signature field.
564
+ * Other models (Nova, Titan, Mistral, Llama, etc.) reject it with:
565
+ * "This model doesn't support the reasoningContent.reasoningText.signature field"
566
+ */
567
+ function supportsThinkingSignature(model: Model<"bedrock-converse-stream">): boolean {
568
+ const id = model.id.toLowerCase();
569
+ return id.includes("anthropic.claude") || id.includes("anthropic/claude");
570
+ }
571
+
572
+ function buildSystemPrompt(
573
+ systemPrompt: readonly string[] | undefined,
574
+ model: Model<"bedrock-converse-stream">,
575
+ cacheRetention: CacheRetention,
576
+ ): SystemContent[] | undefined {
577
+ const prompts = systemPrompt?.map(prompt => prompt.toWellFormed()).filter(prompt => prompt.length > 0) ?? [];
578
+ if (prompts.length === 0) return undefined;
579
+
580
+ const blocks: SystemContent[] = prompts.map(prompt => ({ text: prompt }));
581
+
582
+ // Add cache point for supported Claude models
583
+ if (cacheRetention !== "none" && supportsPromptCaching(model)) {
584
+ blocks.push({
585
+ cachePoint: { type: "default", ...(cacheRetention === "long" ? { ttl: "1h" } : {}) },
586
+ });
587
+ }
588
+
589
+ return blocks;
590
+ }
591
+
592
+ function convertMessages(
593
+ context: Context,
594
+ model: Model<"bedrock-converse-stream">,
595
+ cacheRetention: CacheRetention,
596
+ ): WireMessage[] {
597
+ const result: WireMessage[] = [];
598
+ const transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);
599
+
600
+ for (let i = 0; i < transformedMessages.length; i++) {
601
+ const m = transformedMessages[i];
602
+
603
+ switch (m.role) {
604
+ case "developer":
605
+ case "user":
606
+ if (typeof m.content === "string") {
607
+ // Skip empty user messages
608
+ if (!m.content || m.content.trim() === "") continue;
609
+ result.push({ role: "user", content: [{ text: m.content.toWellFormed() }] });
610
+ } else {
611
+ const contentBlocks: UserContent[] = [];
612
+ for (const c of m.content) {
613
+ switch (c.type) {
614
+ case "text": {
615
+ const text = c.text.toWellFormed();
616
+ if (text.trim().length === 0) continue;
617
+ contentBlocks.push({ text });
618
+ break;
619
+ }
620
+ case "image":
621
+ contentBlocks.push({ image: createImageBlock(c.mimeType, c.data) });
622
+ break;
623
+ default:
624
+ throw new Error("Unknown user content type");
625
+ }
626
+ }
627
+ // Skip message if all blocks filtered out
628
+ if (contentBlocks.length === 0) continue;
629
+ result.push({ role: "user", content: contentBlocks });
630
+ }
631
+ break;
632
+ case "assistant": {
633
+ // Skip assistant messages with empty content (e.g., from aborted requests)
634
+ // Bedrock rejects messages with empty content arrays
635
+ if (m.content.length === 0) continue;
636
+ const contentBlocks: AssistantContent[] = [];
637
+ for (const c of m.content) {
638
+ switch (c.type) {
639
+ case "text":
640
+ // Skip empty text blocks
641
+ if (c.text.trim().length === 0) continue;
642
+ contentBlocks.push({ text: c.text.toWellFormed() });
643
+ break;
644
+ case "toolCall":
645
+ contentBlocks.push({
646
+ toolUse: {
647
+ toolUseId: normalizeToolCallId(c.id),
648
+ name: c.name,
649
+ input: c.arguments,
650
+ },
651
+ });
652
+ break;
653
+ case "thinking":
654
+ // Skip empty thinking blocks
655
+ if (c.thinking.trim().length === 0) continue;
656
+ // Thinking blocks require a valid signature when sent as reasoningContent.
657
+ // If the signature is missing (e.g., from an aborted stream), or the model
658
+ // doesn't support signatures, convert to plain text instead.
659
+ if (supportsThinkingSignature(model) && c.thinkingSignature) {
660
+ contentBlocks.push({
661
+ reasoningContent: {
662
+ reasoningText: { text: c.thinking.toWellFormed(), signature: c.thinkingSignature },
663
+ },
664
+ });
665
+ } else if (!supportsThinkingSignature(model)) {
666
+ // Model doesn't support signatures at all — send as unsigned reasoning
667
+ contentBlocks.push({
668
+ reasoningContent: { reasoningText: { text: c.thinking.toWellFormed() } },
669
+ });
670
+ } else {
671
+ // Model requires signature but we don't have one — demote to text
672
+ contentBlocks.push({ text: `[Thinking]: ${c.thinking.toWellFormed()}` });
673
+ }
674
+ break;
675
+ default:
676
+ throw new Error("Unknown assistant content type");
677
+ }
678
+ }
679
+ // Skip if all content blocks were filtered out
680
+ if (contentBlocks.length === 0) continue;
681
+ result.push({ role: "assistant", content: contentBlocks });
682
+ break;
683
+ }
684
+ case "toolResult": {
685
+ // Collect all consecutive toolResult messages into a single user message —
686
+ // Bedrock requires all tool results to be in one message.
687
+ const toolResults: ToolResultBlockWire[] = [];
688
+ toolResults.push({
689
+ toolResult: {
690
+ toolUseId: normalizeToolCallId(m.toolCallId),
691
+ content: m.content.map(c =>
692
+ c.type === "image"
693
+ ? { image: createImageBlock(c.mimeType, c.data) }
694
+ : { text: c.text.toWellFormed() },
695
+ ),
696
+ status: m.isError ? "error" : "success",
697
+ },
698
+ });
699
+
700
+ let j = i + 1;
701
+ while (j < transformedMessages.length && transformedMessages[j].role === "toolResult") {
702
+ const nextMsg = transformedMessages[j] as ToolResultMessage;
703
+ toolResults.push({
704
+ toolResult: {
705
+ toolUseId: normalizeToolCallId(nextMsg.toolCallId),
706
+ content: nextMsg.content.map(c =>
707
+ c.type === "image"
708
+ ? { image: createImageBlock(c.mimeType, c.data) }
709
+ : { text: c.text.toWellFormed() },
710
+ ),
711
+ status: nextMsg.isError ? "error" : "success",
712
+ },
713
+ });
714
+ j++;
715
+ }
716
+ i = j - 1;
717
+
718
+ result.push({ role: "user", content: toolResults });
719
+ break;
720
+ }
721
+ default:
722
+ throw new Error("Unknown message role");
723
+ }
724
+ }
725
+
726
+ // Add cache point to the last user message for supported Claude models
727
+ if (cacheRetention !== "none" && supportsPromptCaching(model) && result.length > 0) {
728
+ const lastMessage = result[result.length - 1];
729
+ if (lastMessage.role === "user" && lastMessage.content) {
730
+ (lastMessage.content as UserContent[]).push({
731
+ cachePoint: { type: "default", ...(cacheRetention === "long" ? { ttl: "1h" } : {}) },
732
+ });
733
+ }
734
+ }
735
+
736
+ return result;
737
+ }
738
+
739
+ function convertToolConfig(
740
+ tools: Tool[] | undefined,
741
+ toolChoice: BedrockOptions["toolChoice"],
742
+ ): WireToolConfig | undefined {
743
+ if (!tools?.length || toolChoice === "none") return undefined;
744
+
745
+ const bedrockTools: WireToolSpec[] = tools.map(tool => ({
746
+ toolSpec: {
747
+ name: tool.name,
748
+ description: tool.description || "",
749
+ inputSchema: { json: toolWireSchema(tool) },
750
+ },
751
+ }));
752
+
753
+ let bedrockToolChoice: WireToolChoice | undefined;
754
+ switch (toolChoice) {
755
+ case "auto":
756
+ bedrockToolChoice = { auto: {} };
757
+ break;
758
+ case "any":
759
+ bedrockToolChoice = { any: {} };
760
+ break;
761
+ default:
762
+ if (toolChoice?.type === "tool") {
763
+ bedrockToolChoice = { tool: { name: toolChoice.name } };
764
+ }
765
+ }
766
+
767
+ return { tools: bedrockTools, toolChoice: bedrockToolChoice };
768
+ }
769
+
770
+ function mapStopReason(reason: string | undefined): StopReason {
771
+ switch (reason) {
772
+ case "end_turn":
773
+ case "stop_sequence":
774
+ return "stop";
775
+ case "max_tokens":
776
+ case "model_context_window_exceeded":
777
+ return "length";
778
+ case "tool_use":
779
+ return "toolUse";
780
+ default:
781
+ return "error";
782
+ }
783
+ }
784
+
785
+ function buildAdditionalModelRequestFields(
786
+ model: Model<"bedrock-converse-stream">,
787
+ options: BedrockOptions,
788
+ ): Record<string, unknown> | undefined {
789
+ const reasoning = options.reasoning;
790
+ if (!reasoning || !model.reasoning) return undefined;
791
+
792
+ const mode = model.thinking?.mode;
793
+ if (mode === "anthropic-adaptive") {
794
+ const effort = mapEffortToAnthropicAdaptiveEffort(model, reasoning);
795
+ // Starting with Claude Opus 4.7, Anthropic switched the adaptive-thinking
796
+ // default to "omitted", which silently suppresses streamed reasoning and
797
+ // can read as a stalled stream during long reasoning runs (issue #1373).
798
+ // Opt back into "summarized" by default on models that accept the field.
799
+ const adaptive: { type: "adaptive"; display?: BedrockThinkingDisplay } = { type: "adaptive" };
800
+ if (supportsAdaptiveThinkingDisplay(model.id)) {
801
+ adaptive.display = options.thinkingDisplay ?? "summarized";
802
+ }
803
+ return {
804
+ thinking: adaptive,
805
+ output_config: { effort },
806
+ };
807
+ }
808
+
809
+ const level = requireSupportedEffort(model, reasoning);
810
+ const defaultBudgets: Record<Effort, number> = {
811
+ minimal: 1024,
812
+ low: 2048,
813
+ medium: 8192,
814
+ high: 16384,
815
+ xhigh: 32768,
816
+ };
817
+ const budget = options.thinkingBudgets?.[level] ?? defaultBudgets[level];
818
+
819
+ const result: Record<string, unknown> = {
820
+ thinking: {
821
+ type: "enabled",
822
+ budget_tokens: budget,
823
+ display: options.thinkingDisplay ?? "summarized",
824
+ },
825
+ };
826
+
827
+ if (options.interleavedThinking) {
828
+ result.anthropic_beta = ["interleaved-thinking-2025-05-14"];
829
+ }
830
+
831
+ return result;
832
+ }
833
+
834
+ /**
835
+ * Adaptive thinking `display` is supported starting with Claude Opus 4.7.
836
+ * Older adaptive-thinking models (Opus 4.6, Sonnet 4.6+) reject the field.
837
+ * Bedrock model ids are prefixed with region/inference-profile slugs (e.g.
838
+ * `eu.anthropic.claude-opus-4-7-...`); the regex matches the `claude-opus-X-Y`
839
+ * fragment regardless of prefix.
840
+ */
841
+ function supportsAdaptiveThinkingDisplay(modelId: string): boolean {
842
+ const match = /claude-opus-(\d+)-(\d+)/.exec(modelId);
843
+ if (!match) return false;
844
+ const major = Number(match[1]);
845
+ const minor = Number(match[2]);
846
+ return major > 4 || (major === 4 && minor >= 7);
847
+ }
848
+
849
+ /**
850
+ * Bedrock's wire format expects the image as `{ source: { bytes: <base64-string> }, format }`.
851
+ * The caller already passes base64-encoded data, so no decode/re-encode round-trip is needed.
852
+ */
853
+ function createImageBlock(mimeType: string, data: string): ImageBlockWire["image"] {
854
+ let format: "jpeg" | "png" | "gif" | "webp";
855
+ switch (mimeType) {
856
+ case "image/jpeg":
857
+ case "image/jpg":
858
+ format = "jpeg";
859
+ break;
860
+ case "image/png":
861
+ format = "png";
862
+ break;
863
+ case "image/gif":
864
+ format = "gif";
865
+ break;
866
+ case "image/webp":
867
+ format = "webp";
868
+ break;
869
+ default:
870
+ throw new Error(`Unknown image type: ${mimeType}`);
871
+ }
872
+ return { source: { bytes: data }, format };
873
+ }