jeopi-ai 16.2.13

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 (598) hide show
  1. package/CHANGELOG.md +4347 -0
  2. package/README.md +1193 -0
  3. package/dist/types/api-registry.d.ts +30 -0
  4. package/dist/types/auth-broker/client.d.ts +73 -0
  5. package/dist/types/auth-broker/discover.d.ts +35 -0
  6. package/dist/types/auth-broker/index.d.ts +7 -0
  7. package/dist/types/auth-broker/refresher.d.ts +25 -0
  8. package/dist/types/auth-broker/remote-store.d.ts +102 -0
  9. package/dist/types/auth-broker/server.d.ts +43 -0
  10. package/dist/types/auth-broker/snapshot-cache.d.ts +17 -0
  11. package/dist/types/auth-broker/types.d.ts +107 -0
  12. package/dist/types/auth-broker/wire-schemas.d.ts +411 -0
  13. package/dist/types/auth-gateway/http.d.ts +39 -0
  14. package/dist/types/auth-gateway/index.d.ts +3 -0
  15. package/dist/types/auth-gateway/server.d.ts +36 -0
  16. package/dist/types/auth-gateway/types.d.ts +123 -0
  17. package/dist/types/auth-retry.d.ts +124 -0
  18. package/dist/types/auth-storage.d.ts +1026 -0
  19. package/dist/types/dialect/anthropic.d.ts +15 -0
  20. package/dist/types/dialect/catalog.d.ts +3 -0
  21. package/dist/types/dialect/coercion.d.ts +23 -0
  22. package/dist/types/dialect/deepseek.d.ts +14 -0
  23. package/dist/types/dialect/demotion.d.ts +23 -0
  24. package/dist/types/dialect/examples.d.ts +2 -0
  25. package/dist/types/dialect/factory.d.ts +3 -0
  26. package/dist/types/dialect/fenced-thinking.d.ts +53 -0
  27. package/dist/types/dialect/gemini.d.ts +17 -0
  28. package/dist/types/dialect/gemma.d.ts +15 -0
  29. package/dist/types/dialect/glm.d.ts +9 -0
  30. package/dist/types/dialect/harmony.d.ts +8 -0
  31. package/dist/types/dialect/hermes.d.ts +9 -0
  32. package/dist/types/dialect/history.d.ts +3 -0
  33. package/dist/types/dialect/index.d.ts +11 -0
  34. package/dist/types/dialect/inventory.d.ts +12 -0
  35. package/dist/types/dialect/kimi.d.ts +14 -0
  36. package/dist/types/dialect/minimax.d.ts +3 -0
  37. package/dist/types/dialect/owned-stream.d.ts +4 -0
  38. package/dist/types/dialect/qwen3.d.ts +9 -0
  39. package/dist/types/dialect/rendering.d.ts +45 -0
  40. package/dist/types/dialect/thinking.d.ts +6 -0
  41. package/dist/types/dialect/types.d.ts +69 -0
  42. package/dist/types/dialect/xml.d.ts +9 -0
  43. package/dist/types/error/abort.d.ts +14 -0
  44. package/dist/types/error/auth-classify.d.ts +16 -0
  45. package/dist/types/error/auth.d.ts +27 -0
  46. package/dist/types/error/aws.d.ts +23 -0
  47. package/dist/types/error/classes.d.ts +102 -0
  48. package/dist/types/error/finalize.d.ts +39 -0
  49. package/dist/types/error/flags.d.ts +79 -0
  50. package/dist/types/error/format.d.ts +20 -0
  51. package/dist/types/error/gateway.d.ts +20 -0
  52. package/dist/types/error/index.d.ts +13 -0
  53. package/dist/types/error/oauth.d.ts +43 -0
  54. package/dist/types/error/provider.d.ts +42 -0
  55. package/dist/types/error/rate-limit.d.ts +59 -0
  56. package/dist/types/error/retryable.d.ts +27 -0
  57. package/dist/types/error/validation.d.ts +32 -0
  58. package/dist/types/index.d.ts +49 -0
  59. package/dist/types/provider-details.d.ts +24 -0
  60. package/dist/types/providers/__tests__/google-auth.test.d.ts +1 -0
  61. package/dist/types/providers/__tests__/kimi-code-thinking.test.d.ts +1 -0
  62. package/dist/types/providers/__tests__/openai-codex-error.test.d.ts +1 -0
  63. package/dist/types/providers/amazon-bedrock.d.ts +39 -0
  64. package/dist/types/providers/anthropic-client.d.ts +94 -0
  65. package/dist/types/providers/anthropic-messages-server-schema.d.ts +497 -0
  66. package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
  67. package/dist/types/providers/anthropic-wire.d.ts +318 -0
  68. package/dist/types/providers/anthropic.d.ts +248 -0
  69. package/dist/types/providers/aws-credentials.d.ts +53 -0
  70. package/dist/types/providers/aws-eventstream.d.ts +39 -0
  71. package/dist/types/providers/aws-sigv4.d.ts +55 -0
  72. package/dist/types/providers/azure-openai-responses.d.ts +16 -0
  73. package/dist/types/providers/cursor.d.ts +91 -0
  74. package/dist/types/providers/devin.d.ts +12 -0
  75. package/dist/types/providers/error-message.d.ts +27 -0
  76. package/dist/types/providers/github-copilot-headers.d.ts +40 -0
  77. package/dist/types/providers/gitlab-duo-workflow.d.ts +254 -0
  78. package/dist/types/providers/gitlab-duo.d.ts +27 -0
  79. package/dist/types/providers/google-auth.d.ts +32 -0
  80. package/dist/types/providers/google-gemini-cli.d.ts +118 -0
  81. package/dist/types/providers/google-interactions.d.ts +65 -0
  82. package/dist/types/providers/google-shared.d.ts +203 -0
  83. package/dist/types/providers/google-types.d.ts +155 -0
  84. package/dist/types/providers/google-vertex.d.ts +7 -0
  85. package/dist/types/providers/google.d.ts +4 -0
  86. package/dist/types/providers/grammar.d.ts +1 -0
  87. package/dist/types/providers/kimi.d.ts +27 -0
  88. package/dist/types/providers/mock.d.ts +178 -0
  89. package/dist/types/providers/ollama.d.ts +7 -0
  90. package/dist/types/providers/openai-anthropic-shim.d.ts +31 -0
  91. package/dist/types/providers/openai-chat-server-schema.d.ts +695 -0
  92. package/dist/types/providers/openai-chat-server.d.ts +16 -0
  93. package/dist/types/providers/openai-chat-wire.d.ts +644 -0
  94. package/dist/types/providers/openai-codex/request-transformer.d.ts +54 -0
  95. package/dist/types/providers/openai-codex/response-handler.d.ts +26 -0
  96. package/dist/types/providers/openai-codex-responses.d.ts +108 -0
  97. package/dist/types/providers/openai-completions.d.ts +45 -0
  98. package/dist/types/providers/openai-reasoning-fallback.d.ts +25 -0
  99. package/dist/types/providers/openai-responses-server-schema.d.ts +349 -0
  100. package/dist/types/providers/openai-responses-server.d.ts +17 -0
  101. package/dist/types/providers/openai-responses-wire.d.ts +6065 -0
  102. package/dist/types/providers/openai-responses.d.ts +126 -0
  103. package/dist/types/providers/openai-shared.d.ts +506 -0
  104. package/dist/types/providers/pi-native-client.d.ts +13 -0
  105. package/dist/types/providers/pi-native-server.d.ts +69 -0
  106. package/dist/types/providers/register-builtins.d.ts +32 -0
  107. package/dist/types/providers/synthetic.d.ts +26 -0
  108. package/dist/types/providers/transform-messages.d.ts +11 -0
  109. package/dist/types/providers/vision-guard.d.ts +20 -0
  110. package/dist/types/registry/aimlapi.d.ts +4 -0
  111. package/dist/types/registry/alibaba-coding-plan.d.ts +8 -0
  112. package/dist/types/registry/amazon-bedrock.d.ts +5 -0
  113. package/dist/types/registry/anthropic.d.ts +10 -0
  114. package/dist/types/registry/api-key-login.d.ts +42 -0
  115. package/dist/types/registry/api-key-validation.d.ts +43 -0
  116. package/dist/types/registry/azure.d.ts +4 -0
  117. package/dist/types/registry/cerebras.d.ts +7 -0
  118. package/dist/types/registry/cloudflare-ai-gateway.d.ts +13 -0
  119. package/dist/types/registry/coreweave.d.ts +7 -0
  120. package/dist/types/registry/cursor.d.ts +7 -0
  121. package/dist/types/registry/deepseek.d.ts +8 -0
  122. package/dist/types/registry/derived.d.ts +5 -0
  123. package/dist/types/registry/devin.d.ts +8 -0
  124. package/dist/types/registry/firepass.d.ts +16 -0
  125. package/dist/types/registry/fireworks.d.ts +7 -0
  126. package/dist/types/registry/github-copilot.d.ts +7 -0
  127. package/dist/types/registry/gitlab-duo-workflow.d.ts +10 -0
  128. package/dist/types/registry/gitlab-duo.d.ts +9 -0
  129. package/dist/types/registry/google-antigravity.d.ts +9 -0
  130. package/dist/types/registry/google-gemini-cli.d.ts +9 -0
  131. package/dist/types/registry/google-vertex.d.ts +5 -0
  132. package/dist/types/registry/google.d.ts +4 -0
  133. package/dist/types/registry/groq.d.ts +4 -0
  134. package/dist/types/registry/huggingface.d.ts +7 -0
  135. package/dist/types/registry/index.d.ts +4 -0
  136. package/dist/types/registry/kagi.d.ts +14 -0
  137. package/dist/types/registry/kilo.d.ts +7 -0
  138. package/dist/types/registry/kimi-code.d.ts +7 -0
  139. package/dist/types/registry/litellm.d.ts +13 -0
  140. package/dist/types/registry/llama-cpp.d.ts +8 -0
  141. package/dist/types/registry/lm-studio.d.ts +8 -0
  142. package/dist/types/registry/minimax-code-cn.d.ts +6 -0
  143. package/dist/types/registry/minimax-code.d.ts +6 -0
  144. package/dist/types/registry/minimax.d.ts +4 -0
  145. package/dist/types/registry/mistral.d.ts +4 -0
  146. package/dist/types/registry/moonshot.d.ts +7 -0
  147. package/dist/types/registry/nanogpt.d.ts +7 -0
  148. package/dist/types/registry/nvidia.d.ts +7 -0
  149. package/dist/types/registry/oauth/__tests__/xai-oauth.test.d.ts +1 -0
  150. package/dist/types/registry/oauth/anthropic.d.ts +23 -0
  151. package/dist/types/registry/oauth/callback-server.d.ts +72 -0
  152. package/dist/types/registry/oauth/cursor.d.ts +15 -0
  153. package/dist/types/registry/oauth/devin.d.ts +5 -0
  154. package/dist/types/registry/oauth/github-copilot.d.ts +30 -0
  155. package/dist/types/registry/oauth/gitlab-duo-workflow.d.ts +6 -0
  156. package/dist/types/registry/oauth/gitlab-duo.d.ts +3 -0
  157. package/dist/types/registry/oauth/google-antigravity.d.ts +11 -0
  158. package/dist/types/registry/oauth/google-gemini-cli.d.ts +10 -0
  159. package/dist/types/registry/oauth/google-oauth-shared.d.ts +22 -0
  160. package/dist/types/registry/oauth/index.d.ts +64 -0
  161. package/dist/types/registry/oauth/kimi.d.ts +21 -0
  162. package/dist/types/registry/oauth/minimax-code.d.ts +27 -0
  163. package/dist/types/registry/oauth/openai-codex.d.ts +33 -0
  164. package/dist/types/registry/oauth/opencode.d.ts +18 -0
  165. package/dist/types/registry/oauth/perplexity.d.ts +9 -0
  166. package/dist/types/registry/oauth/pkce.d.ts +8 -0
  167. package/dist/types/registry/oauth/types.d.ts +56 -0
  168. package/dist/types/registry/oauth/wafer.d.ts +1 -0
  169. package/dist/types/registry/oauth/xai-oauth.d.ts +52 -0
  170. package/dist/types/registry/oauth/xiaomi.d.ts +25 -0
  171. package/dist/types/registry/ollama-cloud.d.ts +7 -0
  172. package/dist/types/registry/ollama.d.ts +12 -0
  173. package/dist/types/registry/openai-codex-device.d.ts +8 -0
  174. package/dist/types/registry/openai-codex.d.ts +9 -0
  175. package/dist/types/registry/openai.d.ts +4 -0
  176. package/dist/types/registry/opencode-go.d.ts +6 -0
  177. package/dist/types/registry/opencode-zen.d.ts +6 -0
  178. package/dist/types/registry/openrouter.d.ts +13 -0
  179. package/dist/types/registry/parallel.d.ts +14 -0
  180. package/dist/types/registry/perplexity.d.ts +7 -0
  181. package/dist/types/registry/qianfan.d.ts +7 -0
  182. package/dist/types/registry/qwen-portal.d.ts +7 -0
  183. package/dist/types/registry/registry.d.ts +303 -0
  184. package/dist/types/registry/sakana.d.ts +7 -0
  185. package/dist/types/registry/synthetic.d.ts +6 -0
  186. package/dist/types/registry/tavily.d.ts +14 -0
  187. package/dist/types/registry/together.d.ts +6 -0
  188. package/dist/types/registry/types.d.ts +51 -0
  189. package/dist/types/registry/umans.d.ts +7 -0
  190. package/dist/types/registry/venice.d.ts +13 -0
  191. package/dist/types/registry/vercel-ai-gateway.d.ts +7 -0
  192. package/dist/types/registry/vllm.d.ts +7 -0
  193. package/dist/types/registry/wafer-serverless.d.ts +6 -0
  194. package/dist/types/registry/xai-oauth.d.ts +7 -0
  195. package/dist/types/registry/xai.d.ts +4 -0
  196. package/dist/types/registry/xiaomi-token-plan-ams.d.ts +6 -0
  197. package/dist/types/registry/xiaomi-token-plan-cn.d.ts +6 -0
  198. package/dist/types/registry/xiaomi-token-plan-sgp.d.ts +6 -0
  199. package/dist/types/registry/xiaomi.d.ts +6 -0
  200. package/dist/types/registry/zai.d.ts +7 -0
  201. package/dist/types/registry/zenmux.d.ts +7 -0
  202. package/dist/types/registry/zhipu-coding-plan.d.ts +7 -0
  203. package/dist/types/stream.d.ts +44 -0
  204. package/dist/types/types.d.ts +715 -0
  205. package/dist/types/usage/claude.d.ts +4 -0
  206. package/dist/types/usage/gemini.d.ts +2 -0
  207. package/dist/types/usage/github-copilot.d.ts +7 -0
  208. package/dist/types/usage/google-antigravity.d.ts +15 -0
  209. package/dist/types/usage/kimi.d.ts +2 -0
  210. package/dist/types/usage/minimax-code.d.ts +2 -0
  211. package/dist/types/usage/ollama.d.ts +5 -0
  212. package/dist/types/usage/openai-codex-base-url.d.ts +18 -0
  213. package/dist/types/usage/openai-codex-reset.d.ts +79 -0
  214. package/dist/types/usage/openai-codex.d.ts +3 -0
  215. package/dist/types/usage/opencode-go.d.ts +2 -0
  216. package/dist/types/usage/shared.d.ts +1 -0
  217. package/dist/types/usage/zai.d.ts +2 -0
  218. package/dist/types/usage.d.ts +346 -0
  219. package/dist/types/utils/abort.d.ts +25 -0
  220. package/dist/types/utils/anthropic-auth.d.ts +35 -0
  221. package/dist/types/utils/block-symbols.d.ts +20 -0
  222. package/dist/types/utils/deterministic-id.d.ts +16 -0
  223. package/dist/types/utils/empty-completion-retry.d.ts +21 -0
  224. package/dist/types/utils/event-stream.d.ts +30 -0
  225. package/dist/types/utils/foundry.d.ts +1 -0
  226. package/dist/types/utils/google-validation.d.ts +2 -0
  227. package/dist/types/utils/harmony-leak.d.ts +118 -0
  228. package/dist/types/utils/http-inspector.d.ts +30 -0
  229. package/dist/types/utils/idle-iterator.d.ts +137 -0
  230. package/dist/types/utils/leaked-thinking-stream.d.ts +29 -0
  231. package/dist/types/utils/openai-http.d.ts +54 -0
  232. package/dist/types/utils/openrouter-headers.d.ts +1 -0
  233. package/dist/types/utils/parse-bind.d.ts +23 -0
  234. package/dist/types/utils/provider-response.d.ts +3 -0
  235. package/dist/types/utils/proxy.d.ts +29 -0
  236. package/dist/types/utils/request-debug.d.ts +29 -0
  237. package/dist/types/utils/retry-after.d.ts +4 -0
  238. package/dist/types/utils/retry.d.ts +14 -0
  239. package/dist/types/utils/schema/adapt.d.ts +24 -0
  240. package/dist/types/utils/schema/compatibility.d.ts +30 -0
  241. package/dist/types/utils/schema/dereference.d.ts +11 -0
  242. package/dist/types/utils/schema/draft.d.ts +10 -0
  243. package/dist/types/utils/schema/equality.d.ts +4 -0
  244. package/dist/types/utils/schema/fields.d.ts +54 -0
  245. package/dist/types/utils/schema/index.d.ts +15 -0
  246. package/dist/types/utils/schema/json-schema-validator.d.ts +20 -0
  247. package/dist/types/utils/schema/meta-validator.d.ts +2 -0
  248. package/dist/types/utils/schema/normalize.d.ts +124 -0
  249. package/dist/types/utils/schema/spill.d.ts +8 -0
  250. package/dist/types/utils/schema/stamps.d.ts +17 -0
  251. package/dist/types/utils/schema/strict-tool-validation.d.ts +16 -0
  252. package/dist/types/utils/schema/types.d.ts +4 -0
  253. package/dist/types/utils/schema/typescript.d.ts +18 -0
  254. package/dist/types/utils/schema/wire.d.ts +92 -0
  255. package/dist/types/utils/schema/zod-decontaminate.d.ts +31 -0
  256. package/dist/types/utils/sdk-stream-timeout.d.ts +33 -0
  257. package/dist/types/utils/sse-debug.d.ts +5 -0
  258. package/dist/types/utils/stream-markup-healing.d.ts +87 -0
  259. package/dist/types/utils/thinking-loop.d.ts +102 -0
  260. package/dist/types/utils/tool-call-loop-guard.d.ts +26 -0
  261. package/dist/types/utils/tool-choice.d.ts +50 -0
  262. package/dist/types/utils/validation.d.ts +42 -0
  263. package/dist/types/utils.d.ts +24 -0
  264. package/package.json +139 -0
  265. package/src/api-registry.ts +109 -0
  266. package/src/auth-broker/client.ts +359 -0
  267. package/src/auth-broker/discover.ts +222 -0
  268. package/src/auth-broker/index.ts +7 -0
  269. package/src/auth-broker/refresher.ts +117 -0
  270. package/src/auth-broker/remote-store.ts +657 -0
  271. package/src/auth-broker/server.ts +646 -0
  272. package/src/auth-broker/snapshot-cache.ts +191 -0
  273. package/src/auth-broker/types.ts +130 -0
  274. package/src/auth-broker/wire-schemas.ts +249 -0
  275. package/src/auth-gateway/http.ts +194 -0
  276. package/src/auth-gateway/index.ts +3 -0
  277. package/src/auth-gateway/server.ts +802 -0
  278. package/src/auth-gateway/types.ts +151 -0
  279. package/src/auth-retry.ts +250 -0
  280. package/src/auth-storage.ts +5576 -0
  281. package/src/dialect/anthropic.md +31 -0
  282. package/src/dialect/anthropic.ts +608 -0
  283. package/src/dialect/catalog.ts +29 -0
  284. package/src/dialect/coercion.ts +136 -0
  285. package/src/dialect/deepseek.md +24 -0
  286. package/src/dialect/deepseek.ts +609 -0
  287. package/src/dialect/demotion.ts +36 -0
  288. package/src/dialect/examples.ts +33 -0
  289. package/src/dialect/factory.ts +34 -0
  290. package/src/dialect/fenced-thinking.ts +184 -0
  291. package/src/dialect/gemini.md +44 -0
  292. package/src/dialect/gemini.ts +597 -0
  293. package/src/dialect/gemma.md +33 -0
  294. package/src/dialect/gemma.ts +387 -0
  295. package/src/dialect/glm.md +32 -0
  296. package/src/dialect/glm.ts +456 -0
  297. package/src/dialect/harmony.md +31 -0
  298. package/src/dialect/harmony.ts +346 -0
  299. package/src/dialect/hermes.md +25 -0
  300. package/src/dialect/hermes.ts +206 -0
  301. package/src/dialect/history.ts +81 -0
  302. package/src/dialect/index.ts +15 -0
  303. package/src/dialect/inventory.ts +73 -0
  304. package/src/dialect/kimi.md +24 -0
  305. package/src/dialect/kimi.ts +340 -0
  306. package/src/dialect/minimax.md +31 -0
  307. package/src/dialect/minimax.ts +95 -0
  308. package/src/dialect/owned-stream.ts +470 -0
  309. package/src/dialect/prompt-template.md +12 -0
  310. package/src/dialect/qwen3.md +28 -0
  311. package/src/dialect/qwen3.ts +240 -0
  312. package/src/dialect/rendering.ts +249 -0
  313. package/src/dialect/thinking.ts +122 -0
  314. package/src/dialect/types.ts +57 -0
  315. package/src/dialect/xml.md +22 -0
  316. package/src/dialect/xml.ts +90 -0
  317. package/src/error/abort.ts +18 -0
  318. package/src/error/auth-classify.ts +30 -0
  319. package/src/error/auth.ts +48 -0
  320. package/src/error/aws.ts +31 -0
  321. package/src/error/classes.ts +186 -0
  322. package/src/error/finalize.ts +69 -0
  323. package/src/error/flags.ts +506 -0
  324. package/src/error/format.ts +45 -0
  325. package/src/error/gateway.ts +96 -0
  326. package/src/error/index.ts +13 -0
  327. package/src/error/oauth.ts +58 -0
  328. package/src/error/provider.ts +62 -0
  329. package/src/error/rate-limit.ts +161 -0
  330. package/src/error/retryable.ts +70 -0
  331. package/src/error/validation.ts +44 -0
  332. package/src/index.ts +49 -0
  333. package/src/provider-details.ts +90 -0
  334. package/src/providers/__tests__/google-auth.test.ts +144 -0
  335. package/src/providers/__tests__/kimi-code-thinking.test.ts +112 -0
  336. package/src/providers/__tests__/openai-codex-error.test.ts +84 -0
  337. package/src/providers/amazon-bedrock.ts +1042 -0
  338. package/src/providers/anthropic-client.ts +295 -0
  339. package/src/providers/anthropic-messages-server-schema.ts +252 -0
  340. package/src/providers/anthropic-messages-server.ts +756 -0
  341. package/src/providers/anthropic-wire.ts +318 -0
  342. package/src/providers/anthropic.ts +4078 -0
  343. package/src/providers/aws-credentials.ts +586 -0
  344. package/src/providers/aws-eventstream.ts +181 -0
  345. package/src/providers/aws-sigv4.ts +218 -0
  346. package/src/providers/azure-openai-responses.ts +382 -0
  347. package/src/providers/cursor/proto/agent.proto +3526 -0
  348. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  349. package/src/providers/cursor/proto/buf.yaml +17 -0
  350. package/src/providers/cursor.ts +2695 -0
  351. package/src/providers/devin/proto/buf/validate/validate.proto +468 -0
  352. package/src/providers/devin/proto/buf.gen.yaml +33 -0
  353. package/src/providers/devin/proto/buf.yaml +17 -0
  354. package/src/providers/devin/proto/cel/expr/checked.proto +103 -0
  355. package/src/providers/devin/proto/cel/expr/eval.proto +38 -0
  356. package/src/providers/devin/proto/cel/expr/explain.proto +15 -0
  357. package/src/providers/devin/proto/cel/expr/syntax.proto +113 -0
  358. package/src/providers/devin/proto/cel/expr/value.proto +41 -0
  359. package/src/providers/devin/proto/connectext/grpc/status/v1/status.proto +11 -0
  360. package/src/providers/devin/proto/errorspb/errors.proto +56 -0
  361. package/src/providers/devin/proto/errorspb/hintdetail.proto +7 -0
  362. package/src/providers/devin/proto/errorspb/markers.proto +10 -0
  363. package/src/providers/devin/proto/errorspb/tags.proto +12 -0
  364. package/src/providers/devin/proto/errorspb/testing.proto +6 -0
  365. package/src/providers/devin/proto/exa/analytics_pb/analytics.proto +188 -0
  366. package/src/providers/devin/proto/exa/api_server_pb/api_server.proto +2461 -0
  367. package/src/providers/devin/proto/exa/auth_pb/auth.proto +19 -0
  368. package/src/providers/devin/proto/exa/auto_cascade_common_pb/auto_cascade_common.proto +79 -0
  369. package/src/providers/devin/proto/exa/browser_preview_pb/browser_preview.proto +32 -0
  370. package/src/providers/devin/proto/exa/bug_checker_pb/bug_checker.proto +22 -0
  371. package/src/providers/devin/proto/exa/cascade_plugins_pb/cascade_plugins.proto +262 -0
  372. package/src/providers/devin/proto/exa/chat_client_server_pb/chat_client_server.proto +57 -0
  373. package/src/providers/devin/proto/exa/chat_pb/chat.proto +449 -0
  374. package/src/providers/devin/proto/exa/code_edit/code_edit_pb/code_edit.proto +186 -0
  375. package/src/providers/devin/proto/exa/codeium_common_pb/codeium_common.proto +4157 -0
  376. package/src/providers/devin/proto/exa/context_module_pb/context_module.proto +175 -0
  377. package/src/providers/devin/proto/exa/cortex_pb/cortex.proto +3268 -0
  378. package/src/providers/devin/proto/exa/dev_pb/dev.proto +26 -0
  379. package/src/providers/devin/proto/exa/diff_action_pb/diff_action.proto +75 -0
  380. package/src/providers/devin/proto/exa/eval/pr_eval/datasets_pb/datasets.proto +103 -0
  381. package/src/providers/devin/proto/exa/eval_pb/eval.proto +1315 -0
  382. package/src/providers/devin/proto/exa/extension_server_pb/extension_server.proto +556 -0
  383. package/src/providers/devin/proto/exa/file_system_provider_pb/file_system_provider.proto +75 -0
  384. package/src/providers/devin/proto/exa/index_pb/index.proto +461 -0
  385. package/src/providers/devin/proto/exa/knowledge_base_pb/knowledge_base.proto +144 -0
  386. package/src/providers/devin/proto/exa/language_server_pb/language_server.proto +2385 -0
  387. package/src/providers/devin/proto/exa/model_management_pb/model_management.proto +186 -0
  388. package/src/providers/devin/proto/exa/opensearch_clients_pb/opensearch_clients.proto +503 -0
  389. package/src/providers/devin/proto/exa/product_analytics_pb/product_analytics.proto +37 -0
  390. package/src/providers/devin/proto/exa/prompt_pb/prompt.proto +92 -0
  391. package/src/providers/devin/proto/exa/reactive_component_pb/reactive_component.proto +96 -0
  392. package/src/providers/devin/proto/exa/seat_management_pb/seat_management.proto +2680 -0
  393. package/src/providers/devin/proto/exa/tokenizer_pb/tokenizer.proto +37 -0
  394. package/src/providers/devin/proto/exa/trainer_pb/config.proto +647 -0
  395. package/src/providers/devin/proto/exa/tree_sitter/language_data_pb/language_data.proto +14 -0
  396. package/src/providers/devin/proto/exa/trust_pb/trust.proto +157 -0
  397. package/src/providers/devin/proto/exa/user_analytics_pb/user_analytics.proto +519 -0
  398. package/src/providers/devin/proto/google.golang.org/appengine/internal/base/api_base.proto +28 -0
  399. package/src/providers/devin/proto/google.golang.org/appengine/internal/datastore/datastore_v3.proto +484 -0
  400. package/src/providers/devin/proto/google.golang.org/appengine/internal/log/log_service.proto +136 -0
  401. package/src/providers/devin/proto/google.golang.org/appengine/internal/remote_api/remote_api.proto +42 -0
  402. package/src/providers/devin/proto/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto +61 -0
  403. package/src/providers/devin/proto/grpc/binlog/v1/binarylog.proto +84 -0
  404. package/src/providers/devin/proto/io/prometheus/client/metrics.proto +98 -0
  405. package/src/providers/devin.ts +577 -0
  406. package/src/providers/error-message.ts +21 -0
  407. package/src/providers/github-copilot-headers.ts +141 -0
  408. package/src/providers/gitlab-duo-workflow-chatml-note.md +1 -0
  409. package/src/providers/gitlab-duo-workflow.ts +3058 -0
  410. package/src/providers/gitlab-duo.ts +395 -0
  411. package/src/providers/google-auth.ts +350 -0
  412. package/src/providers/google-gemini-cli.ts +1362 -0
  413. package/src/providers/google-interactions.ts +753 -0
  414. package/src/providers/google-shared.ts +1103 -0
  415. package/src/providers/google-types.ts +180 -0
  416. package/src/providers/google-vertex.ts +183 -0
  417. package/src/providers/google.ts +87 -0
  418. package/src/providers/grammar.ts +70 -0
  419. package/src/providers/kimi.ts +52 -0
  420. package/src/providers/mock.ts +507 -0
  421. package/src/providers/ollama.ts +773 -0
  422. package/src/providers/openai-anthropic-shim.ts +152 -0
  423. package/src/providers/openai-chat-server-schema.ts +242 -0
  424. package/src/providers/openai-chat-server.ts +715 -0
  425. package/src/providers/openai-chat-wire.ts +847 -0
  426. package/src/providers/openai-codex/request-transformer.ts +295 -0
  427. package/src/providers/openai-codex/response-handler.ts +102 -0
  428. package/src/providers/openai-codex-responses.ts +3468 -0
  429. package/src/providers/openai-completions.ts +2173 -0
  430. package/src/providers/openai-reasoning-fallback.ts +269 -0
  431. package/src/providers/openai-responses-reasoning-suppression.md +1 -0
  432. package/src/providers/openai-responses-server-schema.ts +282 -0
  433. package/src/providers/openai-responses-server.ts +1280 -0
  434. package/src/providers/openai-responses-wire.ts +6391 -0
  435. package/src/providers/openai-responses.ts +1022 -0
  436. package/src/providers/openai-shared.ts +2648 -0
  437. package/src/providers/pi-native-client.ts +266 -0
  438. package/src/providers/pi-native-server.ts +242 -0
  439. package/src/providers/register-builtins.ts +475 -0
  440. package/src/providers/synthetic.ts +50 -0
  441. package/src/providers/transform-messages.ts +787 -0
  442. package/src/providers/vision-guard.ts +54 -0
  443. package/src/registry/aimlapi.ts +6 -0
  444. package/src/registry/alibaba-coding-plan.ts +95 -0
  445. package/src/registry/amazon-bedrock.ts +22 -0
  446. package/src/registry/anthropic.ts +26 -0
  447. package/src/registry/api-key-login.ts +112 -0
  448. package/src/registry/api-key-validation.ts +161 -0
  449. package/src/registry/azure.ts +6 -0
  450. package/src/registry/cerebras.ts +23 -0
  451. package/src/registry/cloudflare-ai-gateway.ts +45 -0
  452. package/src/registry/coreweave.ts +40 -0
  453. package/src/registry/cursor.ts +20 -0
  454. package/src/registry/deepseek.ts +46 -0
  455. package/src/registry/derived.ts +9 -0
  456. package/src/registry/devin.ts +15 -0
  457. package/src/registry/firepass.ts +32 -0
  458. package/src/registry/fireworks.ts +28 -0
  459. package/src/registry/github-copilot.ts +22 -0
  460. package/src/registry/gitlab-duo-workflow.ts +20 -0
  461. package/src/registry/gitlab-duo.ts +19 -0
  462. package/src/registry/google-antigravity.ts +22 -0
  463. package/src/registry/google-gemini-cli.ts +22 -0
  464. package/src/registry/google-vertex.ts +38 -0
  465. package/src/registry/google.ts +6 -0
  466. package/src/registry/groq.ts +6 -0
  467. package/src/registry/huggingface.ts +29 -0
  468. package/src/registry/index.ts +4 -0
  469. package/src/registry/kagi.ts +46 -0
  470. package/src/registry/kilo.ts +114 -0
  471. package/src/registry/kimi-code.ts +17 -0
  472. package/src/registry/litellm.ts +45 -0
  473. package/src/registry/llama-cpp.ts +35 -0
  474. package/src/registry/lm-studio.ts +31 -0
  475. package/src/registry/minimax-code-cn.ts +12 -0
  476. package/src/registry/minimax-code.ts +12 -0
  477. package/src/registry/minimax.ts +6 -0
  478. package/src/registry/mistral.ts +6 -0
  479. package/src/registry/moonshot.ts +22 -0
  480. package/src/registry/nanogpt.ts +22 -0
  481. package/src/registry/nvidia.ts +61 -0
  482. package/src/registry/oauth/__tests__/xai-oauth.test.ts +104 -0
  483. package/src/registry/oauth/anthropic.ts +311 -0
  484. package/src/registry/oauth/callback-server.ts +315 -0
  485. package/src/registry/oauth/cursor.ts +171 -0
  486. package/src/registry/oauth/devin.ts +124 -0
  487. package/src/registry/oauth/github-copilot.ts +369 -0
  488. package/src/registry/oauth/gitlab-duo-workflow.ts +146 -0
  489. package/src/registry/oauth/gitlab-duo.ts +222 -0
  490. package/src/registry/oauth/google-antigravity.ts +209 -0
  491. package/src/registry/oauth/google-gemini-cli.ts +273 -0
  492. package/src/registry/oauth/google-oauth-shared.ts +125 -0
  493. package/src/registry/oauth/index.ts +269 -0
  494. package/src/registry/oauth/kimi.ts +289 -0
  495. package/src/registry/oauth/minimax-code.ts +53 -0
  496. package/src/registry/oauth/oauth.html +311 -0
  497. package/src/registry/oauth/openai-codex.ts +364 -0
  498. package/src/registry/oauth/opencode.ts +50 -0
  499. package/src/registry/oauth/perplexity.ts +228 -0
  500. package/src/registry/oauth/pkce.ts +18 -0
  501. package/src/registry/oauth/types.ts +65 -0
  502. package/src/registry/oauth/wafer.ts +24 -0
  503. package/src/registry/oauth/xai-oauth.ts +394 -0
  504. package/src/registry/oauth/xiaomi.ts +211 -0
  505. package/src/registry/ollama-cloud.ts +36 -0
  506. package/src/registry/ollama.ts +43 -0
  507. package/src/registry/openai-codex-device.ts +18 -0
  508. package/src/registry/openai-codex.ts +19 -0
  509. package/src/registry/openai.ts +6 -0
  510. package/src/registry/opencode-go.ts +12 -0
  511. package/src/registry/opencode-zen.ts +12 -0
  512. package/src/registry/openrouter.ts +28 -0
  513. package/src/registry/parallel.ts +45 -0
  514. package/src/registry/perplexity.ts +13 -0
  515. package/src/registry/qianfan.ts +27 -0
  516. package/src/registry/qwen-portal.ts +50 -0
  517. package/src/registry/registry.ts +161 -0
  518. package/src/registry/sakana.ts +22 -0
  519. package/src/registry/synthetic.ts +21 -0
  520. package/src/registry/tavily.ts +45 -0
  521. package/src/registry/together.ts +22 -0
  522. package/src/registry/types.ts +56 -0
  523. package/src/registry/umans.ts +23 -0
  524. package/src/registry/venice.ts +33 -0
  525. package/src/registry/vercel-ai-gateway.ts +38 -0
  526. package/src/registry/vllm.ts +34 -0
  527. package/src/registry/wafer-serverless.ts +12 -0
  528. package/src/registry/xai-oauth.ts +17 -0
  529. package/src/registry/xai.ts +6 -0
  530. package/src/registry/xiaomi-token-plan-ams.ts +12 -0
  531. package/src/registry/xiaomi-token-plan-cn.ts +12 -0
  532. package/src/registry/xiaomi-token-plan-sgp.ts +12 -0
  533. package/src/registry/xiaomi.ts +12 -0
  534. package/src/registry/zai.ts +27 -0
  535. package/src/registry/zenmux.ts +22 -0
  536. package/src/registry/zhipu-coding-plan.ts +27 -0
  537. package/src/stream.ts +1778 -0
  538. package/src/types.ts +856 -0
  539. package/src/usage/claude.ts +485 -0
  540. package/src/usage/gemini.ts +258 -0
  541. package/src/usage/github-copilot.ts +424 -0
  542. package/src/usage/google-antigravity.ts +497 -0
  543. package/src/usage/kimi.ts +271 -0
  544. package/src/usage/minimax-code.ts +30 -0
  545. package/src/usage/ollama.ts +41 -0
  546. package/src/usage/openai-codex-base-url.ts +35 -0
  547. package/src/usage/openai-codex-reset.ts +174 -0
  548. package/src/usage/openai-codex.ts +535 -0
  549. package/src/usage/opencode-go.ts +89 -0
  550. package/src/usage/shared.ts +10 -0
  551. package/src/usage/zai.ts +321 -0
  552. package/src/usage.ts +333 -0
  553. package/src/utils/abort.ts +67 -0
  554. package/src/utils/anthropic-auth.ts +93 -0
  555. package/src/utils/block-symbols.ts +32 -0
  556. package/src/utils/deterministic-id.ts +20 -0
  557. package/src/utils/empty-completion-retry.ts +159 -0
  558. package/src/utils/event-stream.ts +171 -0
  559. package/src/utils/foundry.ts +8 -0
  560. package/src/utils/google-validation.ts +25 -0
  561. package/src/utils/harmony-leak.ts +456 -0
  562. package/src/utils/http-inspector.ts +168 -0
  563. package/src/utils/idle-iterator.ts +473 -0
  564. package/src/utils/leaked-thinking-stream.ts +294 -0
  565. package/src/utils/openai-http.ts +122 -0
  566. package/src/utils/openrouter-headers.ts +12 -0
  567. package/src/utils/parse-bind.ts +56 -0
  568. package/src/utils/provider-response.ts +30 -0
  569. package/src/utils/proxy.ts +240 -0
  570. package/src/utils/request-debug.ts +351 -0
  571. package/src/utils/retry-after.ts +110 -0
  572. package/src/utils/retry.ts +59 -0
  573. package/src/utils/schema/CONSTRAINTS.md +166 -0
  574. package/src/utils/schema/adapt.ts +36 -0
  575. package/src/utils/schema/compatibility.ts +435 -0
  576. package/src/utils/schema/dereference.ts +98 -0
  577. package/src/utils/schema/draft.ts +341 -0
  578. package/src/utils/schema/equality.ts +97 -0
  579. package/src/utils/schema/fields.ts +207 -0
  580. package/src/utils/schema/index.ts +15 -0
  581. package/src/utils/schema/json-schema-validator.ts +595 -0
  582. package/src/utils/schema/meta-validator.ts +167 -0
  583. package/src/utils/schema/normalize.ts +1901 -0
  584. package/src/utils/schema/spill.ts +43 -0
  585. package/src/utils/schema/stamps.ts +109 -0
  586. package/src/utils/schema/strict-tool-validation.ts +117 -0
  587. package/src/utils/schema/types.ts +10 -0
  588. package/src/utils/schema/typescript.ts +198 -0
  589. package/src/utils/schema/wire.ts +789 -0
  590. package/src/utils/schema/zod-decontaminate.ts +331 -0
  591. package/src/utils/sdk-stream-timeout.ts +43 -0
  592. package/src/utils/sse-debug.ts +18 -0
  593. package/src/utils/stream-markup-healing.ts +247 -0
  594. package/src/utils/thinking-loop.ts +552 -0
  595. package/src/utils/tool-call-loop-guard.ts +107 -0
  596. package/src/utils/tool-choice.ts +99 -0
  597. package/src/utils/validation.ts +1507 -0
  598. package/src/utils.ts +171 -0
@@ -0,0 +1,787 @@
1
+ import { renderDemotedThinking } from "../dialect/demotion";
2
+ import type { Api, AssistantMessage, Message, Model, ToolCall, ToolResultMessage, UserMessage } from "../types";
3
+
4
+ const enum ToolCallStatus {
5
+ /** A tool result has already been emitted for this tool call; later duplicates must be skipped. */
6
+ Resolved = 1,
7
+ /** A synthetic aborted result was emitted; later real results must be skipped. */
8
+ Aborted = 2,
9
+ }
10
+
11
+ /**
12
+ * Maximum tool-call id length the strictest replay provider accepts.
13
+ *
14
+ * Anthropic requires `^[a-zA-Z0-9_-]+$` with a 64-char cap; Google and Codex
15
+ * `normalizeToolCallId` implementations cap individual id segments to the same
16
+ * 64-char ceiling. Replacement ids minted here flow back through
17
+ * `convertAnthropicMessages` (and friends) unchanged, so the `_dupN` suffix
18
+ * MUST not push a normalized id past this bound.
19
+ */
20
+ const MAX_TOOL_CALL_ID_LENGTH = 64;
21
+
22
+ function appendDuplicateSuffix(originalId: string, suffix: string, maxLength: number): string {
23
+ // Responses-family ids are composites (`callId|itemId`): the wire call_id is
24
+ // the FIRST segment (normalizeResponsesToolCallId splits on `|`), so the
25
+ // suffix must land on every segment or the duplicate collapses back onto the
26
+ // original call_id at encode time. The length budget applies per segment,
27
+ // matching the per-segment caps of the provider normalizers.
28
+ if (originalId.includes("|")) {
29
+ return originalId
30
+ .split("|")
31
+ .map(segment => appendSegmentDuplicateSuffix(segment, suffix, maxLength))
32
+ .join("|");
33
+ }
34
+ return appendSegmentDuplicateSuffix(originalId, suffix, maxLength);
35
+ }
36
+
37
+ function appendSegmentDuplicateSuffix(segment: string, suffix: string, maxLength: number): string {
38
+ if (segment.length + suffix.length <= maxLength) return `${segment}${suffix}`;
39
+ const prefixBudget = Math.max(0, maxLength - suffix.length);
40
+ return `${segment.slice(0, prefixBudget)}${suffix}`;
41
+ }
42
+
43
+ type PendingToolResultRewrite = { replacementId: string } | undefined;
44
+
45
+ function deduplicateToolCallIds(
46
+ messages: Message[],
47
+ maxToolCallIdLength = MAX_TOOL_CALL_ID_LENGTH,
48
+ duplicateSuffixPrefix = "_dup",
49
+ ): Message[] {
50
+ const seenToolCallIds = new Map<string, number>();
51
+ const pendingToolResultRewrites = new Map<string, PendingToolResultRewrite[]>();
52
+
53
+ return messages.map(msg => {
54
+ if (msg.role === "toolResult") {
55
+ const rewrites = pendingToolResultRewrites.get(msg.toolCallId);
56
+ if (!rewrites || rewrites.length === 0) return msg;
57
+
58
+ const rewrite = rewrites.shift();
59
+ if (rewrites.length === 0) pendingToolResultRewrites.delete(msg.toolCallId);
60
+ if (rewrite) return { ...msg, toolCallId: rewrite.replacementId };
61
+ return msg;
62
+ }
63
+
64
+ if (msg.role !== "assistant") return msg;
65
+
66
+ const enqueueToolResultRewrite = (id: string, rewrite: PendingToolResultRewrite): void => {
67
+ const rewrites = pendingToolResultRewrites.get(id);
68
+ if (rewrites) {
69
+ rewrites.push(rewrite);
70
+ return;
71
+ }
72
+ pendingToolResultRewrites.set(id, [rewrite]);
73
+ };
74
+
75
+ // Ids this turn has already touched; used to scope the "drop carried-over
76
+ // pending rewrites" semantics to the FIRST occurrence per turn so multiple
77
+ // blocks of the same id within one turn still accumulate as duplicates.
78
+ const idsTouchedInTurn = new Set<string>();
79
+ let contentChanged = false;
80
+ const content = msg.content.map(block => {
81
+ if (block.type !== "toolCall") return block;
82
+
83
+ // Drop any pending rewrites carried over from a prior assistant turn
84
+ // for this id on its first appearance this turn. When a later turn
85
+ // re-emits the same id, the older duplicate call's expected result
86
+ // never landed in time — the second pass synthesizes
87
+ // "No result provided" for it, and the upcoming real result(id) must
88
+ // route to one of THIS turn's calls. Without this guard the older
89
+ // `_dup` id would steal the next result.
90
+ if (!idsTouchedInTurn.has(block.id)) {
91
+ pendingToolResultRewrites.delete(block.id);
92
+ idsTouchedInTurn.add(block.id);
93
+ }
94
+
95
+ const previousCount = seenToolCallIds.get(block.id) ?? 0;
96
+ if (previousCount === 0) {
97
+ seenToolCallIds.set(block.id, 1);
98
+ enqueueToolResultRewrite(block.id, undefined);
99
+ return block;
100
+ }
101
+
102
+ let duplicateIndex = previousCount;
103
+ let replacementId = appendDuplicateSuffix(
104
+ block.id,
105
+ `${duplicateSuffixPrefix}${duplicateIndex}`,
106
+ maxToolCallIdLength,
107
+ );
108
+ while (seenToolCallIds.has(replacementId)) {
109
+ duplicateIndex += 1;
110
+ replacementId = appendDuplicateSuffix(
111
+ block.id,
112
+ `${duplicateSuffixPrefix}${duplicateIndex}`,
113
+ maxToolCallIdLength,
114
+ );
115
+ }
116
+ seenToolCallIds.set(block.id, duplicateIndex + 1);
117
+ seenToolCallIds.set(replacementId, 1);
118
+ enqueueToolResultRewrite(block.id, { replacementId });
119
+ contentChanged = true;
120
+ return { ...block, id: replacementId };
121
+ });
122
+
123
+ if (!contentChanged) return msg;
124
+ return { ...msg, content };
125
+ });
126
+ }
127
+
128
+ /**
129
+ * Drop assistant `toolCall` blocks whose `id` or `name` is empty / whitespace-only,
130
+ * the `toolResult` messages they point at, and any assistant turn that has no
131
+ * replayable content left.
132
+ *
133
+ * Models occasionally emit malformed calls such as `{ "name": "", "arguments": "{}" }`
134
+ * (observed: GLM-5.2 + thinking on long turns, #3458) or a structurally valid
135
+ * `toolCall` whose provider/native passthrough id never materialized (`id: ""`).
136
+ * The agent loop rejects or skips these at execution time, but the malformed block
137
+ * and its error tool-result can stay in `currentContext.messages`, so every
138
+ * subsequent request replays them. Every provider validates the call shape —
139
+ * Anthropic 400s on `tool_use.name` / `tool_use.id` (alongside an orphan
140
+ * `tool_result`), OpenAI Chat Completions 400s on malformed
141
+ * `tool_calls[i].function.*` — wedging the session in a 400 loop until manual
142
+ * `/clear`.
143
+ *
144
+ * Run before any other transform so the rest of the pipeline never sees a
145
+ * malformed call. Idempotent: a re-run on an already-sanitized list returns
146
+ * the input untouched. Provider-agnostic — any wire model could surface this.
147
+ */
148
+ function isMalformedToolCallName(name: string | undefined): boolean {
149
+ return !name || name.trim().length === 0;
150
+ }
151
+
152
+ function isMalformedToolCallId(id: string | undefined): boolean {
153
+ return !id || id.trim().length === 0;
154
+ }
155
+
156
+ function isMalformedToolCall(block: { id: string; name: string }): boolean {
157
+ return isMalformedToolCallId(block.id) || isMalformedToolCallName(block.name);
158
+ }
159
+
160
+ function sanitizeMalformedToolCalls(messages: Message[]): Message[] {
161
+ // Fast path: skip the rewrite entirely when nothing is malformed.
162
+ let hasMalformed = false;
163
+ outer: for (const msg of messages) {
164
+ if (msg.role !== "assistant") continue;
165
+ for (const block of msg.content) {
166
+ if (block.type === "toolCall" && isMalformedToolCall(block)) {
167
+ hasMalformed = true;
168
+ break outer;
169
+ }
170
+ }
171
+ }
172
+ if (!hasMalformed) return messages;
173
+
174
+ // Positional FIFO pairing within one assistant→tool-result window: a tool-call
175
+ // id can repeat across history when an OpenAI-Responses composite id
176
+ // (`callId|itemId`) collapses on the wire to the same `callId` (see
177
+ // `deduplicateToolCallIds` + `transform-messages-dedup`). A set-based "drop
178
+ // every result for this id" loses the real output for the surviving valid
179
+ // occurrence whenever one duplicate is malformed. Track each `toolCall`
180
+ // occurrence's malformed-ness on a per-id queue and pop on matching
181
+ // `toolResult`, but clear the queues at every non-result boundary so a
182
+ // malformed call whose rejection result never arrived cannot consume a later
183
+ // valid call's real result when the id is reused.
184
+ const dropQueues = new Map<string, boolean[]>();
185
+ const result: Message[] = [];
186
+ for (const msg of messages) {
187
+ if (msg.role === "assistant") {
188
+ dropQueues.clear();
189
+ const filtered: AssistantMessage["content"] = [];
190
+ for (const block of msg.content) {
191
+ if (block.type === "toolCall") {
192
+ const malformed = isMalformedToolCall(block);
193
+ const queue = dropQueues.get(block.id);
194
+ if (queue) queue.push(malformed);
195
+ else dropQueues.set(block.id, [malformed]);
196
+ if (malformed) continue;
197
+ }
198
+ filtered.push(block);
199
+ }
200
+ if (filtered.length === 0) continue;
201
+ result.push(filtered.length === msg.content.length ? msg : { ...msg, content: filtered });
202
+ continue;
203
+ }
204
+ if (msg.role === "toolResult") {
205
+ const queue = dropQueues.get(msg.toolCallId);
206
+ if (queue && queue.length > 0) {
207
+ const drop = queue.shift() === true;
208
+ if (queue.length === 0) dropQueues.delete(msg.toolCallId);
209
+ if (drop) continue;
210
+ }
211
+ result.push(msg);
212
+ continue;
213
+ }
214
+ dropQueues.clear();
215
+ result.push(msg);
216
+ }
217
+ return result;
218
+ }
219
+
220
+ function shouldDropTruncatedThinkingOnlyAssistant(msg: AssistantMessage): boolean {
221
+ const isTruncatedStop = msg.stopReason === "length" || msg.stopReason === "error" || msg.stopReason === "aborted";
222
+ return isTruncatedStop && !msg.content.some(block => block.type === "toolCall" || block.type === "text");
223
+ }
224
+
225
+ function getLatestSurvivingAssistantIndex(messages: readonly Message[]): number {
226
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
227
+ const msg = messages[index]!;
228
+ if (msg.role === "assistant" && !shouldDropTruncatedThinkingOnlyAssistant(msg)) {
229
+ return index;
230
+ }
231
+ }
232
+ return -1;
233
+ }
234
+
235
+ function isAnthropicMessagesModel(model: Model): model is Model<"anthropic-messages"> {
236
+ return model.api === "anthropic-messages";
237
+ }
238
+
239
+ /**
240
+ * Targets that have proven they read unsigned foreign thinking when replayed
241
+ * natively. This is a semantic-carry allowlist only: OpenAI-compatible
242
+ * `reasoning_content` schema requirements and llama.cpp cache-prefix replay are
243
+ * handled by their encoders and MUST NOT make foreign thinking look meaningful.
244
+ */
245
+ function targetReadsForeignThinking(model: Model, compat: Model["compat"]): boolean {
246
+ if (compat === undefined) return false;
247
+ if (model.api === "anthropic-messages") {
248
+ return "replayUnsignedThinking" in compat && compat.replayUnsignedThinking === true;
249
+ }
250
+ if (model.api !== "openai-completions") return false;
251
+ if (!("thinkingFormat" in compat)) return false;
252
+ if (compat.requiresThinkingAsText) return false;
253
+ return model.reasoning && compat.thinkingFormat === "zai";
254
+ }
255
+
256
+ const ANTHROPIC_TOOL_CALL_ID_PATTERN = /^[a-zA-Z0-9_-]{1,64}$/;
257
+
258
+ function isValidAnthropicToolCallId(id: string): boolean {
259
+ return ANTHROPIC_TOOL_CALL_ID_PATTERN.test(id);
260
+ }
261
+
262
+ function fallbackAnthropicToolCallId(originalId: string): string {
263
+ return `toolu_${Bun.hash(originalId).toString(36)}`;
264
+ }
265
+
266
+ function normalizeAnthropicTargetToolCallId<TApi extends Api>(
267
+ id: string,
268
+ model: Model<TApi>,
269
+ source: AssistantMessage,
270
+ normalizeToolCallId?: (id: string, model: Model<TApi>, source: AssistantMessage) => string,
271
+ ): string {
272
+ if (isValidAnthropicToolCallId(id)) return id;
273
+ const normalized =
274
+ normalizeToolCallId?.(id, model, source) ?? id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, MAX_TOOL_CALL_ID_LENGTH);
275
+ if (isValidAnthropicToolCallId(normalized)) return normalized;
276
+ return fallbackAnthropicToolCallId(id);
277
+ }
278
+
279
+ /**
280
+ * Normalize tool call ID for cross-provider compatibility.
281
+ * OpenAI Responses API generates IDs that are 450+ chars with special characters like `|`.
282
+ * Anthropic APIs require IDs matching ^[a-zA-Z0-9_-]+$ (max 64 chars).
283
+ *
284
+ * For aborted/errored turns, this function:
285
+ * - Preserves tool call structure (unlike converting to text summaries)
286
+ * - Injects synthetic "aborted" tool results
287
+ */
288
+ export function transformMessages<TApi extends Api>(
289
+ messages: Message[],
290
+ model: Model<TApi>,
291
+ normalizeToolCallId?: (id: string, model: Model<TApi>, source: AssistantMessage) => string,
292
+ maxNormalizedToolCallIdLength = MAX_TOOL_CALL_ID_LENGTH,
293
+ duplicateToolCallIdSuffixPrefix = "_dup",
294
+ targetCompat: Model<TApi>["compat"] = model.compat,
295
+ ): Message[] {
296
+ // Drop assistant `toolCall` blocks with empty/whitespace `id` or `name`
297
+ // (and their matched `toolResult` messages) before anything else looks at
298
+ // the history. Replays of these would 400 every provider — see
299
+ // `sanitizeMalformedToolCalls`.
300
+ messages = sanitizeMalformedToolCalls(messages);
301
+
302
+ // Build a map of original tool call IDs to normalized IDs
303
+ const toolCallIdMap = new Map<string, string>();
304
+
305
+ const latestSurvivingAssistantIndex = getLatestSurvivingAssistantIndex(messages);
306
+ // First pass: transform messages (thinking blocks, tool call ID normalization)
307
+ const normalizedMessages = messages.map((msg, index) => {
308
+ // User and developer messages pass through unchanged
309
+ if (msg.role === "user" || msg.role === "developer") {
310
+ return msg;
311
+ }
312
+
313
+ // Handle toolResult messages - normalize toolCallId if we have a mapping
314
+ if (msg.role === "toolResult") {
315
+ const normalizedId = toolCallIdMap.get(msg.toolCallId);
316
+ if (normalizedId && normalizedId !== msg.toolCallId) {
317
+ return { ...msg, toolCallId: normalizedId };
318
+ }
319
+ return msg;
320
+ }
321
+
322
+ // Assistant messages need transformation check
323
+ if (msg.role === "assistant") {
324
+ const assistantMsg = msg as AssistantMessage;
325
+ const isSameModel =
326
+ assistantMsg.provider === model.provider &&
327
+ assistantMsg.api === model.api &&
328
+ assistantMsg.model === model.id;
329
+
330
+ const isAnthropicTarget = isAnthropicMessagesModel(model);
331
+ // Anthropic's all-or-none contract on prior-turn thinking blocks
332
+ // applies to every `anthropic-messages → anthropic-messages` replay,
333
+ // not just the latest assistant turn. The legacy
334
+ // `mustPreserveLatestAnthropicThinking` flag only honored it for the
335
+ // latest turn; every prior turn fell through to the cross-API
336
+ // text-demotion path whenever the conversation crossed a model id,
337
+ // silently dropping the reasoning chain on continuation for custom
338
+ // anthropic-messages providers configured via `models.yaml` and
339
+ // session-level model swaps (#2257).
340
+ const isAnthropicReplay = isAnthropicTarget && assistantMsg.api === "anthropic-messages";
341
+ const isLatestSurvivingAssistant = index === latestSurvivingAssistantIndex;
342
+ // Signature policy is a second axis. Anthropic cryptographically
343
+ // binds reasoning signatures to its key+session+model, so cross-model
344
+ // signatures must be stripped whenever official Anthropic is on
345
+ // either end of the replay:
346
+ // * official → 3p: the 3p target can't reverify the signature;
347
+ // keeping it leaks private continuation metadata for no benefit.
348
+ // * 3p → official: official rejects a foreign signature outright.
349
+ // * official → official cross-model: the new model rejects the
350
+ // previous model's signature.
351
+ // 3p ↔ 3p replays preserve signatures because compatible providers
352
+ // (Z.AI, DeepSeek, custom `models.yaml` providers) treat them as
353
+ // opaque continuation hints rather than verified material; stripping
354
+ // degrades the reasoning chain into unsigned/text on the next turn
355
+ // (#2265). Source-side official detection uses the canonical catalog
356
+ // provider id `"anthropic"` because assistant messages carry no
357
+ // `baseUrl` — a user who manually points `provider: "anthropic"` at
358
+ // a custom proxy via `models.yaml` will see signatures stripped, the
359
+ // conservative direction (degraded reasoning, not broken requests).
360
+ const isOfficialAnthropicSource = isAnthropicReplay && assistantMsg.provider === "anthropic";
361
+ const isOfficialAnthropicTarget = isAnthropicTarget && model.compat.officialEndpoint;
362
+ const officialAnthropicInvolved = isOfficialAnthropicSource || isOfficialAnthropicTarget;
363
+ // Compatible Anthropic-messages reasoning targets that accept
364
+ // unsigned thinking natively (Z.AI, DeepSeek, the generic
365
+ // `reasoning && !official` case in the compat builder). Used to keep
366
+ // `redacted_thinking` siblings beside unsigned visible thinking on
367
+ // targets that won't text-demote it.
368
+ const replaysUnsignedAnthropicThinking = isAnthropicTarget && model.compat.replayUnsignedThinking;
369
+ // Thinking signatures can be untrustworthy for two distinct reasons with very
370
+ // different blast radii:
371
+ //
372
+ // 1. Aborted/errored turns: the stream stopped mid-block, so only the block
373
+ // that was streaming at the abort point — always the FINAL content block —
374
+ // can carry a partially-streamed (invalid) signature. Every earlier block
375
+ // completed: Anthropic delivers a block's signature at its
376
+ // `content_block_stop`, which necessarily fired before the next block began,
377
+ // so those signatures are whole and valid. Stripping them would needlessly
378
+ // discard a replayable thinking chain — e.g. interrupting during the visible
379
+ // text output after thinking already finished leaves a fully-signed thinking
380
+ // block that must be kept, or Anthropic rejects the replay with HTTP 400
381
+ // "Invalid `signature` in `thinking` block".
382
+ //
383
+ // 2. Abandoned tool-use turns: a turn that carries toolCall blocks but did NOT
384
+ // request tool execution (stopReason !== "toolUse" — e.g. adaptive-thinking
385
+ // Opus emitting tool calls and then ending on `end_turn`/`stop`). The agent
386
+ // loop pairs those calls with placeholder tool_results to keep the
387
+ // tool_use/tool_result contract valid. The turn completed cleanly, but its
388
+ // signatures are end_turn-bound and cannot be replayed in that synthesized
389
+ // continuation, so EVERY thinking signature is stripped.
390
+ //
391
+ // Latest abandoned turns are exempt because Anthropic requires thinking blocks
392
+ // from its most recent response to remain byte-for-byte unmodified.
393
+ const invalidStopReason = assistantMsg.stopReason === "aborted" || assistantMsg.stopReason === "error";
394
+ const abandonedToolUse =
395
+ !invalidStopReason &&
396
+ assistantMsg.stopReason !== "toolUse" &&
397
+ assistantMsg.content.some(b => b.type === "toolCall");
398
+ const lastBlockIndex = assistantMsg.content.length - 1;
399
+
400
+ const transformedContent = assistantMsg.content.flatMap((block, blockIndex) => {
401
+ if (block.type === "thinking") {
402
+ // Only an aborted/errored turn's final (mid-stream) block can hold a
403
+ // partial signature; abandoned tool-use turns strip all. Drop the
404
+ // untrustworthy signature so the encoder can downgrade the block to text.
405
+ const signatureUntrustworthy = abandonedToolUse || (invalidStopReason && blockIndex === lastBlockIndex);
406
+ let sanitized: typeof block =
407
+ signatureUntrustworthy && block.thinkingSignature
408
+ ? { ...block, thinkingSignature: undefined }
409
+ : block;
410
+ if (isAnthropicReplay) {
411
+ // Latest abandoned turn: Anthropic's byte-for-byte rule forbids
412
+ // even stripping a signature on the latest message.
413
+ if (isLatestSurvivingAssistant && abandonedToolUse) return block;
414
+ // Cross-model prior turns crossing an official Anthropic endpoint
415
+ // must strip the source signature so the downstream encoder
416
+ // applies its `replayUnsignedThinking` policy (unsigned thinking
417
+ // is emitted natively on Anthropic-compatible reasoning endpoints
418
+ // and demoted to text on official Anthropic). 3p ↔ 3p replays
419
+ // keep the signature so the reasoning chain stays signed on
420
+ // continuation (#2265).
421
+ if (
422
+ !isLatestSurvivingAssistant &&
423
+ !isSameModel &&
424
+ officialAnthropicInvolved &&
425
+ sanitized.thinkingSignature
426
+ ) {
427
+ sanitized = { ...sanitized, thinkingSignature: undefined };
428
+ }
429
+ // Drop blocks with neither a signature anchor nor any text —
430
+ // nothing for the next turn to replay.
431
+ if (!sanitized.thinkingSignature && (!sanitized.thinking || sanitized.thinking.trim() === "")) {
432
+ return [];
433
+ }
434
+ return sanitized;
435
+ }
436
+ // Cross-API target: same-model replay keeps signatures untouched
437
+ // (the encoder needs them for native replay; an OpenAI encrypted
438
+ // reasoning blob has empty text but a load-bearing signature).
439
+ if (isSameModel && sanitized.thinkingSignature) return sanitized;
440
+ // Nothing left for the next turn to replay: drop empty/no-anchor
441
+ // thinking blocks before the cross-model paths.
442
+ if (!sanitized.thinking || sanitized.thinking.trim() === "") return [];
443
+ if (isSameModel) return sanitized;
444
+ // Cross-model + cross-API: preserve native thinking only for
445
+ // targets proven to read unsigned foreign reasoning (Z.AI-format
446
+ // OpenAI-compatible targets, plus Anthropic-compatible
447
+ // `replayUnsignedThinking`). Tool-call schema requirements and
448
+ // llama.cpp cache-prefix replay are orthogonal encoder concerns;
449
+ // keeping inert foreign CoT native for those flags loses the
450
+ // canonical visible-text fallback without adding model context.
451
+ if (targetReadsForeignThinking(model, targetCompat)) {
452
+ return sanitized.thinkingSignature ? { ...sanitized, thinkingSignature: undefined } : sanitized;
453
+ }
454
+ // Other cross-API targets (openai-responses encrypted blobs, google
455
+ // thought parts, anthropic-target from a non-Anthropic source, or any
456
+ // reasoning-disabled target) can't replay an unsigned thinking block:
457
+ // the native reasoning slot either rejects a foreign signature or — as
458
+ // verified end-to-end against Gemini 3 — silently discards unsigned
459
+ // thought content (it is neither recalled nor influences generation).
460
+ // Demote to text so the reasoning survives as context, wrapped in the
461
+ // TARGET model's own canonical thinking-block dialect (e.g. a ```thinking
462
+ // fence for Gemini) so it reads as reasoning rather than bare prose the
463
+ // model might mimic.
464
+ return {
465
+ type: "text" as const,
466
+ text: renderDemotedThinking(model.id, sanitized.thinking),
467
+ };
468
+ }
469
+
470
+ if (block.type === "redactedThinking") {
471
+ // Redacted thinking is native-only. Keep it for same-model
472
+ // signed replay, the latest byte-for-byte Anthropic turn, or
473
+ // compatible targets that will also emit sibling unsigned
474
+ // thinking natively. Drop it when the visible thinking was
475
+ // cross-model stripped and will be demoted to text.
476
+ if (isAnthropicReplay) {
477
+ if (isSameModel || isLatestSurvivingAssistant || replaysUnsignedAnthropicThinking) return block;
478
+ return [];
479
+ }
480
+ if (isSameModel) return block;
481
+ return [];
482
+ }
483
+
484
+ if (block.type === "fallback") {
485
+ // Server-side-fallback boundary marker (Anthropic beta
486
+ // `server-side-fallback-2026-06-01`). Only the official
487
+ // Anthropic endpoint accepts this block on replay: every
488
+ // other target either rejects unknown content blocks with a
489
+ // 400 (anthropic-compatible endpoints like Umans/Z.AI/MiniMax,
490
+ // and older omp gateways whose schema pre-dates this feature)
491
+ // or throws in its converter (Bedrock). Even the official
492
+ // replay path only accepts the block when the current request
493
+ // itself opts into the beta — but we don't know that here, so
494
+ // keep it and let `convertAnthropicMessages` re-check the
495
+ // per-request opt-in before serializing.
496
+ if (isAnthropicTarget && model.compat.officialEndpoint) return block;
497
+ return [];
498
+ }
499
+
500
+ if (block.type === "text") {
501
+ if (isSameModel) return block;
502
+ return {
503
+ type: "text" as const,
504
+ text: block.text,
505
+ };
506
+ }
507
+
508
+ if (block.type === "toolCall") {
509
+ const toolCall = block as ToolCall;
510
+ let normalizedToolCall: ToolCall = toolCall;
511
+
512
+ if (!isSameModel && toolCall.thoughtSignature) {
513
+ normalizedToolCall = { ...toolCall, thoughtSignature: undefined };
514
+ }
515
+
516
+ if (isAnthropicTarget) {
517
+ const normalizedId = normalizeAnthropicTargetToolCallId(
518
+ toolCall.id,
519
+ model,
520
+ assistantMsg,
521
+ normalizeToolCallId,
522
+ );
523
+ if (normalizedId !== toolCall.id) {
524
+ toolCallIdMap.set(toolCall.id, normalizedId);
525
+ normalizedToolCall = { ...normalizedToolCall, id: normalizedId };
526
+ }
527
+ } else if (!isSameModel && normalizeToolCallId) {
528
+ const normalizedId = normalizeToolCallId(toolCall.id, model, assistantMsg);
529
+ if (normalizedId !== toolCall.id) {
530
+ toolCallIdMap.set(toolCall.id, normalizedId);
531
+ normalizedToolCall = { ...normalizedToolCall, id: normalizedId };
532
+ }
533
+ }
534
+
535
+ return normalizedToolCall;
536
+ }
537
+
538
+ return block;
539
+ });
540
+
541
+ return {
542
+ ...assistantMsg,
543
+ content: transformedContent,
544
+ };
545
+ }
546
+ return msg;
547
+ });
548
+ const transformed = deduplicateToolCallIds(
549
+ normalizedMessages,
550
+ maxNormalizedToolCallIdLength,
551
+ duplicateToolCallIdSuffixPrefix,
552
+ );
553
+ // All real tool results, keyed by id, in document order. One id can map to
554
+ // more than one result: compaction can fold an assistant `tool_use` into a
555
+ // summary string while its `tool_result` survives, and a later turn may reuse
556
+ // the id. `takeRealToolResult` pulls the earliest unconsumed result positioned
557
+ // AFTER the call's assistant turn, so an orphaned earlier result is never
558
+ // pulled forward onto a later call (which would surface a prior turn's output).
559
+ type IndexedToolResult = { index: number; msg: ToolResultMessage; consumed: boolean };
560
+ const realToolResultsById = new Map<string, IndexedToolResult[]>();
561
+ for (let index = 0; index < transformed.length; index++) {
562
+ const msg = transformed[index];
563
+ if (msg.role === "toolResult") {
564
+ const entry: IndexedToolResult = { index, msg, consumed: false };
565
+ const entries = realToolResultsById.get(msg.toolCallId);
566
+ if (entries) entries.push(entry);
567
+ else realToolResultsById.set(msg.toolCallId, [entry]);
568
+ }
569
+ }
570
+ const takeRealToolResult = (id: string, afterIndex: number): ToolResultMessage | undefined => {
571
+ const entries = realToolResultsById.get(id);
572
+ if (!entries) return undefined;
573
+ for (const entry of entries) {
574
+ if (entry.consumed || entry.index <= afterIndex) continue;
575
+ entry.consumed = true;
576
+ return entry.msg;
577
+ }
578
+ return undefined;
579
+ };
580
+
581
+ // Anthropic rejects `tool_result` blocks whose `tool_use_id` does not appear in a prior
582
+ // `tool_use` block. After handoff/compaction folds an assistant turn into a summary
583
+ // string, the user-side `toolResult` for that turn can survive while the originating
584
+ // `tool_use` disappears — leaving an orphan that triggers HTTP 400. Track the set of
585
+ // `tool_use` ids that survive transformation so the second pass can drop orphans cleanly.
586
+ const validToolUseIds = new Set<string>();
587
+ for (const msg of transformed) {
588
+ if (msg.role !== "assistant") continue;
589
+ for (const block of msg.content) {
590
+ if (block.type === "toolCall") validToolUseIds.add(block.id);
591
+ }
592
+ }
593
+
594
+ // Second pass: ensure each surviving assistant tool call is immediately
595
+ // followed by exactly one corresponding tool result.
596
+ const result: Message[] = [];
597
+ let pendingToolCalls: ToolCall[] = [];
598
+ // Index of the assistant turn that declared `pendingToolCalls`; a pulled
599
+ // result must be positioned after it (see `takeRealToolResult`).
600
+ let pendingToolCallsStartIndex = -1;
601
+ let pendingAbortedToolCalls = new Map<string, ToolCall>();
602
+ let pendingAbortedTimestamp: number | undefined;
603
+ let pendingAbortedStartIndex = -1;
604
+ // Track which tool calls already have an emitted result so delayed/duplicate
605
+ // toolResult messages cannot create a second provider-visible result.
606
+ const toolCallStatus = new Map<string, ToolCallStatus>();
607
+
608
+ const flushPendingToolCalls = (timestamp: number): void => {
609
+ if (pendingToolCalls.length === 0) return;
610
+ for (const tc of pendingToolCalls) {
611
+ if (toolCallStatus.has(tc.id)) continue;
612
+ const realToolResult = takeRealToolResult(tc.id, pendingToolCallsStartIndex);
613
+ if (realToolResult) {
614
+ result.push(realToolResult);
615
+ toolCallStatus.set(tc.id, ToolCallStatus.Resolved);
616
+ continue;
617
+ }
618
+ result.push({
619
+ role: "toolResult",
620
+ toolCallId: tc.id,
621
+ toolName: tc.name,
622
+ content: [{ type: "text", text: "No result provided" }],
623
+ isError: true,
624
+ timestamp,
625
+ } as ToolResultMessage);
626
+ toolCallStatus.set(tc.id, ToolCallStatus.Resolved);
627
+ }
628
+ pendingToolCalls = [];
629
+ };
630
+
631
+ const flushPendingAbortedToolCalls = (): void => {
632
+ if (pendingAbortedTimestamp === undefined) return;
633
+ for (const tc of pendingAbortedToolCalls.values()) {
634
+ if (toolCallStatus.has(tc.id)) continue;
635
+ const realToolResult = takeRealToolResult(tc.id, pendingAbortedStartIndex);
636
+ if (realToolResult) {
637
+ result.push(realToolResult);
638
+ toolCallStatus.set(tc.id, ToolCallStatus.Resolved);
639
+ continue;
640
+ }
641
+ result.push({
642
+ role: "toolResult",
643
+ toolCallId: tc.id,
644
+ toolName: tc.name,
645
+ content: [{ type: "text", text: "aborted" }],
646
+ isError: true,
647
+ timestamp: pendingAbortedTimestamp,
648
+ } as ToolResultMessage);
649
+ toolCallStatus.set(tc.id, ToolCallStatus.Aborted);
650
+ }
651
+ pendingAbortedToolCalls = new Map();
652
+ pendingAbortedTimestamp = undefined;
653
+ };
654
+
655
+ for (let i = 0; i < transformed.length; i++) {
656
+ const msg = transformed[i];
657
+ const messageTimestamp = "timestamp" in msg && typeof msg.timestamp === "number" ? msg.timestamp : Date.now();
658
+
659
+ if (msg.role === "assistant") {
660
+ flushPendingToolCalls(messageTimestamp);
661
+ flushPendingAbortedToolCalls();
662
+
663
+ const assistantMsg = msg as AssistantMessage;
664
+
665
+ // Drop assistant turns that carry no actionable content (no `text`, no `toolCall`)
666
+ // AND were terminated by a truncating stop reason (`length` / `error` / `aborted`).
667
+ // These are produced when the provider returns `stop_reason: "max_tokens"` (or a
668
+ // stream error) mid-thinking, leaving a `[thinking]`-only message with a valid
669
+ // signature but nothing for the next turn to anchor on. Keeping it creates
670
+ // back-to-back assistant turns once the next response lands, which Anthropic
671
+ // rejects with "messages.X.content.Y: `thinking` blocks in the latest assistant
672
+ // message cannot be modified".
673
+ //
674
+ // `stopReason: "stop"` thinking-only messages are intentionally preserved: they
675
+ // represent reasoning-only assistant turns used for replay round-trips
676
+ // (OpenAI completions `reasoning_text`, Google signed thought parts).
677
+ const originalMsg = messages[i]!;
678
+ if (originalMsg.role === "assistant" && shouldDropTruncatedThinkingOnlyAssistant(originalMsg)) {
679
+ continue;
680
+ }
681
+
682
+ const toolCalls = assistantMsg.content.filter(b => b.type === "toolCall") as ToolCall[];
683
+
684
+ if (assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") {
685
+ // Keep the assistant message with tool calls intact. Real tool results are
686
+ // emitted immediately if available; otherwise synthesize aborted results
687
+ // before the next turn boundary.
688
+ result.push(msg);
689
+ pendingAbortedToolCalls = new Map(toolCalls.map(toolCall => [toolCall.id, toolCall] as const));
690
+ pendingAbortedTimestamp = assistantMsg.timestamp;
691
+ pendingAbortedStartIndex = i;
692
+ continue;
693
+ }
694
+
695
+ if (toolCalls.length > 0) {
696
+ pendingToolCalls = toolCalls;
697
+ pendingToolCallsStartIndex = i;
698
+ }
699
+
700
+ result.push(msg);
701
+ } else if (msg.role === "toolResult") {
702
+ if (toolCallStatus.has(msg.toolCallId)) continue;
703
+
704
+ if (pendingAbortedToolCalls.has(msg.toolCallId)) {
705
+ pendingAbortedToolCalls.delete(msg.toolCallId);
706
+ toolCallStatus.set(msg.toolCallId, ToolCallStatus.Resolved);
707
+ result.push(msg);
708
+ continue;
709
+ }
710
+
711
+ if (pendingToolCalls.some(tc => tc.id === msg.toolCallId)) {
712
+ toolCallStatus.set(msg.toolCallId, ToolCallStatus.Resolved);
713
+ result.push(msg);
714
+ continue;
715
+ }
716
+
717
+ if (!validToolUseIds.has(msg.toolCallId)) {
718
+ // Orphan `tool_result`: the originating `tool_use` is not present in the
719
+ // transformed history (typically because handoff/compaction folded the
720
+ // assistant message into a summary string while the user-side result
721
+ // survived). Sending the block as-is would 400 the request, so it must
722
+ // be dropped.
723
+ //
724
+ // If a pending tool-call window is still open (either normal or
725
+ // aborted), the orphan cannot be replaced with a developer note here:
726
+ //
727
+ // * Anthropic requires the next message after an assistant `tool_use`
728
+ // to be the matching `tool_result`. Inserting a developer message
729
+ // would break that contiguity.
730
+ // * Flushing pending aborted calls here would wedge synthetic results
731
+ // between the assistant turn and a real result that may still arrive
732
+ // inside the current contiguous result window.
733
+ //
734
+ // Drop the orphan silently in that case; the pending calls will be
735
+ // resolved in their own contiguous result window or at the next boundary.
736
+ if (pendingToolCalls.some(tc => !toolCallStatus.has(tc.id)) || pendingAbortedToolCalls.size > 0) {
737
+ continue;
738
+ }
739
+ // No pending tool-call window: safe to preserve the text payload so the
740
+ // model still sees what the tool returned.
741
+ //
742
+ // The note is emitted with `role: "user"` rather than `role: "developer"`
743
+ // because the developer role is elevated by some providers:
744
+ //
745
+ // * Ollama maps `developer` -> `system` (highest instruction priority).
746
+ // * OpenAI chat-completions reasoning models forward `developer` as
747
+ // `developer` (above-user instruction priority).
748
+ //
749
+ // Stale, model-untrusted tool output must not gain instruction priority
750
+ // above user/developer messages it lived alongside before compaction.
751
+ // `user` role is mapped to plain user content by every provider, so the
752
+ // content survives without ever being treated as an instruction the
753
+ // model should obey.
754
+ const textParts: string[] = [];
755
+ for (const part of msg.content) {
756
+ if (part.type === "text" && part.text.trim() !== "") textParts.push(part.text);
757
+ }
758
+ if (textParts.length > 0) {
759
+ const errorAttr = msg.isError ? ' is-error="true"' : "";
760
+ result.push({
761
+ role: "user",
762
+ content: `<stale-tool-result tool="${msg.toolName}" id="${msg.toolCallId}"${errorAttr}>\n${textParts.join("\n")}\n</stale-tool-result>`,
763
+ timestamp: messageTimestamp,
764
+ } as UserMessage);
765
+ }
766
+ }
767
+
768
+ // The matching tool_use exists elsewhere, but this result is not in
769
+ // the currently open result window. Emitting it here would break the
770
+ // provider invariant; the first real result is pulled into the correct
771
+ // slot by the pending-call flush instead.
772
+ } else if (msg.role === "user" || msg.role === "developer") {
773
+ flushPendingToolCalls(messageTimestamp);
774
+ flushPendingAbortedToolCalls();
775
+ result.push(msg);
776
+ } else {
777
+ flushPendingToolCalls(messageTimestamp);
778
+ flushPendingAbortedToolCalls();
779
+ result.push(msg);
780
+ }
781
+ }
782
+
783
+ flushPendingToolCalls(Date.now());
784
+ flushPendingAbortedToolCalls();
785
+
786
+ return result;
787
+ }