@librechat/agents 3.2.21 → 3.2.31

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 (398) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +3 -2
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/events.cjs.map +1 -1
  4. package/dist/cjs/graphs/Graph.cjs +200 -54
  5. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  6. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  7. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +13 -7
  8. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs.map +1 -1
  9. package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
  10. package/dist/cjs/hooks/types.cjs.map +1 -1
  11. package/dist/cjs/instrumentation.cjs +2 -2
  12. package/dist/cjs/instrumentation.cjs.map +1 -1
  13. package/dist/cjs/langfuse.cjs +17 -1
  14. package/dist/cjs/langfuse.cjs.map +1 -1
  15. package/dist/cjs/langfuseToolOutputTracing.cjs +2 -2
  16. package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
  17. package/dist/cjs/llm/anthropic/index.cjs +1 -1
  18. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  19. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  20. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +1 -1
  21. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
  22. package/dist/cjs/llm/bedrock/index.cjs +2 -2
  23. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  24. package/dist/cjs/llm/bedrock/toolCache.cjs +8 -5
  25. package/dist/cjs/llm/bedrock/toolCache.cjs.map +1 -1
  26. package/dist/cjs/llm/fake.cjs +16 -14
  27. package/dist/cjs/llm/fake.cjs.map +1 -1
  28. package/dist/cjs/llm/google/index.cjs +22 -0
  29. package/dist/cjs/llm/google/index.cjs.map +1 -1
  30. package/dist/cjs/llm/google/utils/common.cjs +88 -27
  31. package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
  32. package/dist/cjs/llm/init.cjs +2 -2
  33. package/dist/cjs/llm/invoke.cjs +108 -11
  34. package/dist/cjs/llm/invoke.cjs.map +1 -1
  35. package/dist/cjs/llm/openai/index.cjs +1 -1
  36. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  37. package/dist/cjs/llm/openai/utils/index.cjs +1 -1
  38. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  39. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  40. package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
  41. package/dist/cjs/main.cjs +1 -0
  42. package/dist/cjs/main.cjs.map +1 -1
  43. package/dist/cjs/messages/cache.cjs +8 -7
  44. package/dist/cjs/messages/cache.cjs.map +1 -1
  45. package/dist/cjs/messages/content.cjs.map +1 -1
  46. package/dist/cjs/messages/contextPruning.cjs.map +1 -1
  47. package/dist/cjs/messages/format.cjs +124 -17
  48. package/dist/cjs/messages/format.cjs.map +1 -1
  49. package/dist/cjs/messages/prune.cjs.map +1 -1
  50. package/dist/cjs/messages/reducer.cjs +1 -1
  51. package/dist/cjs/messages/reducer.cjs.map +1 -1
  52. package/dist/cjs/messages/tools.cjs +1 -1
  53. package/dist/cjs/messages/tools.cjs.map +1 -1
  54. package/dist/cjs/openai/index.cjs.map +1 -1
  55. package/dist/cjs/responses/index.cjs.map +1 -1
  56. package/dist/cjs/run.cjs +41 -20
  57. package/dist/cjs/run.cjs.map +1 -1
  58. package/dist/cjs/session/AgentSession.cjs +4 -4
  59. package/dist/cjs/session/AgentSession.cjs.map +1 -1
  60. package/dist/cjs/session/JsonlSessionStore.cjs +2 -2
  61. package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -1
  62. package/dist/cjs/session/handlers.cjs +2 -2
  63. package/dist/cjs/session/handlers.cjs.map +1 -1
  64. package/dist/cjs/stream.cjs +248 -25
  65. package/dist/cjs/stream.cjs.map +1 -1
  66. package/dist/cjs/summarization/node.cjs.map +1 -1
  67. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +1 -1
  68. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  69. package/dist/cjs/tools/Calculator.cjs +1 -1
  70. package/dist/cjs/tools/Calculator.cjs.map +1 -1
  71. package/dist/cjs/tools/CodeExecutor.cjs +1 -1
  72. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  73. package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
  74. package/dist/cjs/tools/ToolNode.cjs +37 -18
  75. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  76. package/dist/cjs/tools/ToolSearch.cjs +1 -1
  77. package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
  78. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs +7 -4
  79. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs.map +1 -1
  80. package/dist/cjs/tools/cloudflare/CloudflareSandboxTools.cjs +4 -4
  81. package/dist/cjs/tools/cloudflare/CloudflareSandboxTools.cjs.map +1 -1
  82. package/dist/cjs/tools/handlers.cjs +2 -1
  83. package/dist/cjs/tools/handlers.cjs.map +1 -1
  84. package/dist/cjs/tools/local/CompileCheckTool.cjs.map +1 -1
  85. package/dist/cjs/tools/local/FileCheckpointer.cjs +2 -1
  86. package/dist/cjs/tools/local/FileCheckpointer.cjs.map +1 -1
  87. package/dist/cjs/tools/local/LocalCodingTools.cjs +45 -19
  88. package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
  89. package/dist/cjs/tools/local/LocalExecutionEngine.cjs +3 -3
  90. package/dist/cjs/tools/local/LocalExecutionEngine.cjs.map +1 -1
  91. package/dist/cjs/tools/local/LocalExecutionTools.cjs +2 -2
  92. package/dist/cjs/tools/local/LocalExecutionTools.cjs.map +1 -1
  93. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +4 -3
  94. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
  95. package/dist/cjs/tools/local/attachments.cjs +0 -5
  96. package/dist/cjs/tools/local/attachments.cjs.map +1 -1
  97. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +4 -4
  98. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -1
  99. package/dist/cjs/tools/search/firecrawl.cjs +1 -1
  100. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
  101. package/dist/cjs/tools/search/rerankers.cjs +7 -3
  102. package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
  103. package/dist/cjs/tools/search/tavily-search.cjs +1 -1
  104. package/dist/cjs/tools/search/tavily-search.cjs.map +1 -1
  105. package/dist/cjs/tools/search/utils.cjs +76 -8
  106. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  107. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -1
  108. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  109. package/dist/cjs/utils/handlers.cjs +1 -1
  110. package/dist/cjs/utils/handlers.cjs.map +1 -1
  111. package/dist/cjs/utils/run.cjs +1 -1
  112. package/dist/cjs/utils/run.cjs.map +1 -1
  113. package/dist/esm/agents/AgentContext.mjs +3 -2
  114. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  115. package/dist/esm/events.mjs.map +1 -1
  116. package/dist/esm/graphs/Graph.mjs +200 -54
  117. package/dist/esm/graphs/Graph.mjs.map +1 -1
  118. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  119. package/dist/esm/hooks/createWorkspacePolicyHook.mjs +13 -7
  120. package/dist/esm/hooks/createWorkspacePolicyHook.mjs.map +1 -1
  121. package/dist/esm/hooks/executeHooks.mjs.map +1 -1
  122. package/dist/esm/hooks/types.mjs.map +1 -1
  123. package/dist/esm/instrumentation.mjs +2 -2
  124. package/dist/esm/instrumentation.mjs.map +1 -1
  125. package/dist/esm/langfuse.mjs +17 -2
  126. package/dist/esm/langfuse.mjs.map +1 -1
  127. package/dist/esm/langfuseToolOutputTracing.mjs +2 -2
  128. package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
  129. package/dist/esm/llm/anthropic/index.mjs +1 -1
  130. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  131. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  132. package/dist/esm/llm/anthropic/utils/message_outputs.mjs +1 -1
  133. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  134. package/dist/esm/llm/bedrock/index.mjs +2 -2
  135. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  136. package/dist/esm/llm/bedrock/toolCache.mjs +8 -5
  137. package/dist/esm/llm/bedrock/toolCache.mjs.map +1 -1
  138. package/dist/esm/llm/fake.mjs +16 -14
  139. package/dist/esm/llm/fake.mjs.map +1 -1
  140. package/dist/esm/llm/google/index.mjs +23 -1
  141. package/dist/esm/llm/google/index.mjs.map +1 -1
  142. package/dist/esm/llm/google/utils/common.mjs +88 -27
  143. package/dist/esm/llm/google/utils/common.mjs.map +1 -1
  144. package/dist/esm/llm/init.mjs +2 -2
  145. package/dist/esm/llm/invoke.mjs +104 -7
  146. package/dist/esm/llm/invoke.mjs.map +1 -1
  147. package/dist/esm/llm/openai/index.mjs +1 -1
  148. package/dist/esm/llm/openai/index.mjs.map +1 -1
  149. package/dist/esm/llm/openai/utils/index.mjs +1 -1
  150. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  151. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  152. package/dist/esm/llm/vertexai/index.mjs.map +1 -1
  153. package/dist/esm/main.mjs +1 -1
  154. package/dist/esm/messages/cache.mjs +8 -7
  155. package/dist/esm/messages/cache.mjs.map +1 -1
  156. package/dist/esm/messages/content.mjs.map +1 -1
  157. package/dist/esm/messages/contextPruning.mjs.map +1 -1
  158. package/dist/esm/messages/format.mjs +124 -18
  159. package/dist/esm/messages/format.mjs.map +1 -1
  160. package/dist/esm/messages/prune.mjs.map +1 -1
  161. package/dist/esm/messages/reducer.mjs +1 -1
  162. package/dist/esm/messages/reducer.mjs.map +1 -1
  163. package/dist/esm/messages/tools.mjs +1 -1
  164. package/dist/esm/messages/tools.mjs.map +1 -1
  165. package/dist/esm/openai/index.mjs.map +1 -1
  166. package/dist/esm/responses/index.mjs.map +1 -1
  167. package/dist/esm/run.mjs +41 -20
  168. package/dist/esm/run.mjs.map +1 -1
  169. package/dist/esm/session/AgentSession.mjs +4 -4
  170. package/dist/esm/session/AgentSession.mjs.map +1 -1
  171. package/dist/esm/session/JsonlSessionStore.mjs +2 -2
  172. package/dist/esm/session/JsonlSessionStore.mjs.map +1 -1
  173. package/dist/esm/session/handlers.mjs +2 -2
  174. package/dist/esm/session/handlers.mjs.map +1 -1
  175. package/dist/esm/stream.mjs +248 -25
  176. package/dist/esm/stream.mjs.map +1 -1
  177. package/dist/esm/summarization/node.mjs.map +1 -1
  178. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +1 -1
  179. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  180. package/dist/esm/tools/Calculator.mjs +1 -1
  181. package/dist/esm/tools/Calculator.mjs.map +1 -1
  182. package/dist/esm/tools/CodeExecutor.mjs +1 -1
  183. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  184. package/dist/esm/tools/SubagentTool.mjs.map +1 -1
  185. package/dist/esm/tools/ToolNode.mjs +37 -18
  186. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  187. package/dist/esm/tools/ToolSearch.mjs +1 -1
  188. package/dist/esm/tools/ToolSearch.mjs.map +1 -1
  189. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs +7 -4
  190. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs.map +1 -1
  191. package/dist/esm/tools/cloudflare/CloudflareSandboxTools.mjs +4 -4
  192. package/dist/esm/tools/cloudflare/CloudflareSandboxTools.mjs.map +1 -1
  193. package/dist/esm/tools/handlers.mjs +2 -1
  194. package/dist/esm/tools/handlers.mjs.map +1 -1
  195. package/dist/esm/tools/local/CompileCheckTool.mjs.map +1 -1
  196. package/dist/esm/tools/local/FileCheckpointer.mjs +2 -1
  197. package/dist/esm/tools/local/FileCheckpointer.mjs.map +1 -1
  198. package/dist/esm/tools/local/LocalCodingTools.mjs +45 -19
  199. package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
  200. package/dist/esm/tools/local/LocalExecutionEngine.mjs +3 -3
  201. package/dist/esm/tools/local/LocalExecutionEngine.mjs.map +1 -1
  202. package/dist/esm/tools/local/LocalExecutionTools.mjs +2 -2
  203. package/dist/esm/tools/local/LocalExecutionTools.mjs.map +1 -1
  204. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +4 -3
  205. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
  206. package/dist/esm/tools/local/attachments.mjs +0 -5
  207. package/dist/esm/tools/local/attachments.mjs.map +1 -1
  208. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +4 -4
  209. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -1
  210. package/dist/esm/tools/search/firecrawl.mjs +1 -1
  211. package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
  212. package/dist/esm/tools/search/rerankers.mjs +8 -4
  213. package/dist/esm/tools/search/rerankers.mjs.map +1 -1
  214. package/dist/esm/tools/search/tavily-search.mjs +1 -1
  215. package/dist/esm/tools/search/tavily-search.mjs.map +1 -1
  216. package/dist/esm/tools/search/utils.mjs +76 -9
  217. package/dist/esm/tools/search/utils.mjs.map +1 -1
  218. package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -1
  219. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  220. package/dist/esm/utils/handlers.mjs +1 -1
  221. package/dist/esm/utils/handlers.mjs.map +1 -1
  222. package/dist/esm/utils/run.mjs +1 -1
  223. package/dist/esm/utils/run.mjs.map +1 -1
  224. package/dist/types/agents/__tests__/promptCacheLiveHelpers.d.ts +1 -1
  225. package/dist/types/events.d.ts +1 -1
  226. package/dist/types/graphs/Graph.d.ts +7 -1
  227. package/dist/types/hooks/executeHooks.d.ts +1 -1
  228. package/dist/types/hooks/types.d.ts +5 -0
  229. package/dist/types/langfuse.d.ts +4 -0
  230. package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -1
  231. package/dist/types/llm/anthropic/utils/message_outputs.d.ts +1 -1
  232. package/dist/types/llm/anthropic/utils/output_parsers.d.ts +2 -2
  233. package/dist/types/llm/bedrock/index.d.ts +2 -2
  234. package/dist/types/llm/fake.d.ts +3 -3
  235. package/dist/types/llm/google/index.d.ts +1 -0
  236. package/dist/types/llm/google/types.d.ts +1 -1
  237. package/dist/types/llm/google/utils/common.d.ts +2 -2
  238. package/dist/types/llm/google/utils/tools.d.ts +1 -1
  239. package/dist/types/llm/google/utils/zod_to_genai_parameters.d.ts +1 -1
  240. package/dist/types/llm/openai/index.d.ts +2 -2
  241. package/dist/types/llm/openai/utils/index.d.ts +1 -1
  242. package/dist/types/llm/openrouter/index.d.ts +4 -4
  243. package/dist/types/messages/contextPruning.d.ts +1 -1
  244. package/dist/types/messages/format.d.ts +9 -4
  245. package/dist/types/messages/prune.d.ts +1 -1
  246. package/dist/types/session/JsonlSessionStore.d.ts +1 -1
  247. package/dist/types/session/handlers.d.ts +1 -1
  248. package/dist/types/session/types.d.ts +1 -1
  249. package/dist/types/summarization/node.d.ts +1 -1
  250. package/dist/types/tools/SubagentTool.d.ts +2 -2
  251. package/dist/types/tools/ToolNode.d.ts +9 -2
  252. package/dist/types/tools/cloudflare/CloudflareSandboxExecutionEngine.d.ts +1 -1
  253. package/dist/types/tools/search/types.d.ts +1 -1
  254. package/dist/types/tools/search/utils.d.ts +11 -0
  255. package/dist/types/types/graph.d.ts +4 -4
  256. package/dist/types/types/llm.d.ts +4 -3
  257. package/dist/types/types/messages.d.ts +1 -1
  258. package/dist/types/types/run.d.ts +6 -6
  259. package/dist/types/types/stream.d.ts +2 -2
  260. package/dist/types/types/tools.d.ts +5 -1
  261. package/dist/types/utils/handlers.d.ts +2 -2
  262. package/dist/types/utils/run.d.ts +1 -1
  263. package/package.json +6 -3
  264. package/src/__tests__/stream.eagerEventExecution.test.ts +543 -6
  265. package/src/agents/AgentContext.ts +2 -2
  266. package/src/agents/__tests__/AgentContext.test.ts +3 -3
  267. package/src/agents/__tests__/promptCacheLiveHelpers.ts +1 -1
  268. package/src/events.ts +1 -1
  269. package/src/graphs/Graph.ts +329 -72
  270. package/src/graphs/MultiAgentGraph.ts +1 -1
  271. package/src/graphs/__tests__/Graph.reasoning.test.ts +919 -6
  272. package/src/graphs/__tests__/MultiAgentGraph.test.ts +1 -1
  273. package/src/graphs/__tests__/composition.smoke.test.ts +1 -1
  274. package/src/hooks/__tests__/HookRegistry.test.ts +1 -1
  275. package/src/hooks/__tests__/compactHooks.test.ts +8 -8
  276. package/src/hooks/__tests__/createWorkspacePolicyHook.test.ts +34 -22
  277. package/src/hooks/__tests__/executeHooks.test.ts +3 -3
  278. package/src/hooks/__tests__/integration.test.ts +3 -3
  279. package/src/hooks/__tests__/toolHooks.test.ts +10 -10
  280. package/src/hooks/createWorkspacePolicyHook.ts +17 -14
  281. package/src/hooks/executeHooks.ts +1 -1
  282. package/src/hooks/types.ts +5 -0
  283. package/src/instrumentation.ts +11 -11
  284. package/src/langfuse.ts +35 -1
  285. package/src/langfuseToolOutputTracing.ts +2 -2
  286. package/src/llm/anthropic/index.ts +1 -1
  287. package/src/llm/anthropic/utils/message_inputs.ts +1 -1
  288. package/src/llm/anthropic/utils/message_outputs.ts +3 -5
  289. package/src/llm/anthropic/utils/output_parsers.ts +5 -5
  290. package/src/llm/bedrock/index.ts +4 -4
  291. package/src/llm/bedrock/toolCache.test.ts +48 -9
  292. package/src/llm/bedrock/toolCache.ts +11 -6
  293. package/src/llm/fake.ts +30 -25
  294. package/src/llm/google/index.ts +43 -1
  295. package/src/llm/google/llm.spec.ts +173 -1
  296. package/src/llm/google/types.ts +1 -1
  297. package/src/llm/google/utils/common.ts +154 -45
  298. package/src/llm/google/utils/tools.ts +8 -8
  299. package/src/llm/google/utils/zod_to_genai_parameters.ts +4 -4
  300. package/src/llm/invoke.test.ts +3 -3
  301. package/src/llm/invoke.ts +170 -10
  302. package/src/llm/openai/index.ts +4 -4
  303. package/src/llm/openai/utils/index.ts +14 -14
  304. package/src/llm/openrouter/index.ts +4 -4
  305. package/src/llm/openrouter/reasoning.test.ts +2 -2
  306. package/src/llm/vertexai/fixThoughtSignatures.test.ts +1 -1
  307. package/src/llm/vertexai/index.ts +1 -1
  308. package/src/messages/cache.test.ts +22 -0
  309. package/src/messages/cache.ts +25 -12
  310. package/src/messages/content.ts +1 -1
  311. package/src/messages/contextPruning.ts +1 -1
  312. package/src/messages/format.ts +227 -43
  313. package/src/messages/formatAgentMessages.skills.test.ts +105 -26
  314. package/src/messages/formatAgentMessages.test.ts +841 -10
  315. package/src/messages/labelContentByAgent.test.ts +2 -2
  316. package/src/messages/prune.ts +1 -1
  317. package/src/messages/reducer.ts +1 -1
  318. package/src/messages/tools.ts +1 -1
  319. package/src/openai/__tests__/openai.test.ts +2 -2
  320. package/src/openai/index.ts +1 -1
  321. package/src/responses/__tests__/responses.test.ts +2 -2
  322. package/src/responses/index.ts +1 -1
  323. package/src/run.ts +68 -41
  324. package/src/session/AgentSession.ts +6 -6
  325. package/src/session/JsonlSessionStore.ts +3 -3
  326. package/src/session/__tests__/JsonlSessionStore.test.ts +5 -5
  327. package/src/session/__tests__/handlers.test.ts +2 -2
  328. package/src/session/handlers.ts +5 -5
  329. package/src/session/types.ts +1 -1
  330. package/src/specs/agent-handoffs.test.ts +1 -1
  331. package/src/specs/langfuse-callbacks.test.ts +2 -2
  332. package/src/specs/langfuse-metadata.test.ts +39 -0
  333. package/src/specs/langfuse-tool-output-tracing.test.ts +1 -1
  334. package/src/specs/multi-agent-summarization.test.ts +4 -4
  335. package/src/specs/subagent.test.ts +3 -3
  336. package/src/specs/summarization-unit.test.ts +1 -1
  337. package/src/specs/thinking-handoff.test.ts +1 -1
  338. package/src/splitStream.test.ts +48 -0
  339. package/src/stream.test.ts +53 -3
  340. package/src/stream.ts +450 -39
  341. package/src/summarization/__tests__/aggregator.test.ts +2 -2
  342. package/src/summarization/__tests__/node.test.ts +2 -2
  343. package/src/summarization/node.ts +1 -1
  344. package/src/tools/BashProgrammaticToolCalling.ts +5 -5
  345. package/src/tools/Calculator.ts +1 -1
  346. package/src/tools/CodeExecutor.ts +2 -4
  347. package/src/tools/SubagentTool.ts +2 -2
  348. package/src/tools/ToolNode.ts +37 -16
  349. package/src/tools/ToolSearch.ts +1 -1
  350. package/src/tools/__tests__/CloudflareSandboxExecution.test.ts +4 -4
  351. package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +12 -12
  352. package/src/tools/__tests__/LocalExecutionTools.test.ts +125 -93
  353. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +29 -5
  354. package/src/tools/__tests__/ReadFile.test.ts +1 -1
  355. package/src/tools/__tests__/SkillTool.test.ts +4 -4
  356. package/src/tools/__tests__/SubagentExecutor.test.ts +17 -13
  357. package/src/tools/__tests__/SubagentTool.test.ts +2 -2
  358. package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +1 -1
  359. package/src/tools/__tests__/ToolNode.outputReferences.test.ts +2 -5
  360. package/src/tools/__tests__/ToolNode.session.test.ts +1 -1
  361. package/src/tools/__tests__/ToolSearch.test.ts +1 -1
  362. package/src/tools/__tests__/annotateMessagesForLLM.test.ts +1 -1
  363. package/src/tools/__tests__/directToolHITLResumeScope.test.ts +35 -32
  364. package/src/tools/__tests__/directToolHooks.test.ts +41 -0
  365. package/src/tools/__tests__/handlers.test.ts +2 -2
  366. package/src/tools/__tests__/hitl.test.ts +11 -11
  367. package/src/tools/__tests__/localToolNames.test.ts +8 -6
  368. package/src/tools/__tests__/skillCatalog.test.ts +1 -1
  369. package/src/tools/__tests__/subagentHooks.test.ts +20 -10
  370. package/src/tools/__tests__/workspaceSeam.test.ts +20 -7
  371. package/src/tools/cloudflare/CloudflareSandboxExecutionEngine.ts +9 -6
  372. package/src/tools/cloudflare/CloudflareSandboxTools.ts +19 -19
  373. package/src/tools/handlers.ts +5 -5
  374. package/src/tools/local/CompileCheckTool.ts +4 -7
  375. package/src/tools/local/FileCheckpointer.ts +6 -5
  376. package/src/tools/local/LocalCodingTools.ts +100 -45
  377. package/src/tools/local/LocalExecutionEngine.ts +5 -5
  378. package/src/tools/local/LocalExecutionTools.ts +9 -9
  379. package/src/tools/local/LocalProgrammaticToolCalling.ts +5 -4
  380. package/src/tools/local/attachments.ts +0 -6
  381. package/src/tools/local/resolveLocalExecutionTools.ts +15 -15
  382. package/src/tools/search/firecrawl.ts +1 -1
  383. package/src/tools/search/jina-reranker.test.ts +148 -37
  384. package/src/tools/search/rerankers.ts +14 -4
  385. package/src/tools/search/tavily-search.ts +2 -2
  386. package/src/tools/search/types.ts +1 -1
  387. package/src/tools/search/utils.ts +99 -9
  388. package/src/tools/subagent/SubagentExecutor.ts +12 -6
  389. package/src/types/graph.ts +12 -12
  390. package/src/types/llm.ts +7 -6
  391. package/src/types/messages.ts +1 -1
  392. package/src/types/run.ts +7 -7
  393. package/src/types/stream.ts +2 -2
  394. package/src/types/tools.ts +5 -1
  395. package/src/utils/handlers.ts +2 -2
  396. package/src/utils/llmConfig.ts +1 -1
  397. package/src/utils/logging.ts +20 -10
  398. package/src/utils/run.ts +2 -2
@@ -6,13 +6,30 @@ import {
6
6
  ToolMessage,
7
7
  } from '@langchain/core/messages';
8
8
  import type { MessageContentComplex, TPayload } from '@/types';
9
- import { formatAgentMessages } from './format';
9
+ import { _convertMessagesToAnthropicPayload } from '@/llm/anthropic/utils/message_inputs';
10
10
  import {
11
11
  convertMessagesToContent,
12
12
  formatAnthropicArtifactContent,
13
13
  } from './core';
14
- import { _convertMessagesToAnthropicPayload } from '@/llm/anthropic/utils/message_inputs';
15
14
  import { Constants, ContentTypes, Providers } from '@/common';
15
+ import { formatAgentMessages } from './format';
16
+
17
+ type AnthropicPayloadBlock = {
18
+ content?: unknown;
19
+ id?: string;
20
+ input?: unknown;
21
+ name?: string;
22
+ text?: string;
23
+ tool_use_id?: string;
24
+ type: string;
25
+ };
26
+
27
+ const getAnthropicPayloadBlocks = (
28
+ content: unknown
29
+ ): AnthropicPayloadBlock[] => {
30
+ expect(Array.isArray(content)).toBe(true);
31
+ return content as AnthropicPayloadBlock[];
32
+ };
16
33
 
17
34
  describe('formatAgentMessages', () => {
18
35
  it('should format simple user and AI messages', () => {
@@ -24,6 +41,12 @@ describe('formatAgentMessages', () => {
24
41
  expect(result.messages).toHaveLength(2);
25
42
  expect(result.messages[0]).toBeInstanceOf(HumanMessage);
26
43
  expect(result.messages[1]).toBeInstanceOf(AIMessage);
44
+ expect(result.messages.map((message) => message.role)).toEqual([
45
+ 'user',
46
+ 'assistant',
47
+ ]);
48
+ expect(Object.keys(result.messages[0])).not.toContain('role');
49
+ expect(Object.keys(result.messages[1])).not.toContain('role');
27
50
  });
28
51
 
29
52
  it('preserves source messageId on formatted messages', () => {
@@ -56,6 +79,11 @@ describe('formatAgentMessages', () => {
56
79
  expect(result.messages[0]).toBeInstanceOf(AIMessage);
57
80
  expect(result.messages[1]).toBeInstanceOf(ToolMessage);
58
81
  expect(result.messages[2]).toBeInstanceOf(HumanMessage);
82
+ expect(result.messages.map((message) => message.role)).toEqual([
83
+ 'assistant',
84
+ 'tool',
85
+ 'user',
86
+ ]);
59
87
  expect(result.messages[0].id).toBe('msg_assistant_1');
60
88
  expect(result.messages[1].id).toBe('msg_assistant_1');
61
89
  expect(result.messages[2].id).toBe('msg_user_1');
@@ -68,6 +96,8 @@ describe('formatAgentMessages', () => {
68
96
  const result = formatAgentMessages(payload);
69
97
  expect(result.messages).toHaveLength(1);
70
98
  expect(result.messages[0]).toBeInstanceOf(SystemMessage);
99
+ expect(result.messages[0].role).toBe('system');
100
+ expect(Object.keys(result.messages[0])).not.toContain('role');
71
101
  });
72
102
 
73
103
  it('should prepend the latest summary and trim context before its boundary', () => {
@@ -271,8 +301,9 @@ describe('formatAgentMessages', () => {
271
301
 
272
302
  expect(result.messages).toHaveLength(1);
273
303
  expect(result.messages[0]).toBeInstanceOf(AIMessage);
274
- expect(result.messages.some((message) => message instanceof ToolMessage))
275
- .toBe(false);
304
+ expect(
305
+ result.messages.some((message) => message instanceof ToolMessage)
306
+ ).toBe(false);
276
307
  expect((result.messages[0] as AIMessage).tool_calls).toHaveLength(0);
277
308
  expect(result.messages[0].content).toEqual([
278
309
  {
@@ -389,6 +420,810 @@ describe('formatAgentMessages', () => {
389
420
  }
390
421
  });
391
422
 
423
+ it('preserves Anthropic Vertex web search pair when mixed with regular tool calls', () => {
424
+ const serverToolId = `${Constants.ANTHROPIC_SERVER_TOOL_PREFIX}vrtx_search_1`;
425
+ const calculatorToolId = 'toolu_calc_1';
426
+ const payload: TPayload = [
427
+ {
428
+ role: 'user',
429
+ content: 'Search current Claude Vertex docs and calculate 6 * 7.',
430
+ },
431
+ {
432
+ role: 'assistant',
433
+ content: [
434
+ {
435
+ type: ContentTypes.TOOL_CALL,
436
+ tool_call: {
437
+ id: serverToolId,
438
+ name: 'web_search',
439
+ args: '{"query":"Claude Vertex web search"}',
440
+ },
441
+ },
442
+ {
443
+ type: ContentTypes.TEXT,
444
+ text: 'I will calculate the number too.',
445
+ tool_call_ids: [calculatorToolId],
446
+ },
447
+ {
448
+ type: ContentTypes.TOOL_CALL,
449
+ tool_call: {
450
+ id: calculatorToolId,
451
+ name: 'calculator',
452
+ args: '{"expression":"6*7"}',
453
+ output: '42',
454
+ },
455
+ },
456
+ {
457
+ type: 'web_search_tool_result',
458
+ tool_use_id: serverToolId,
459
+ content: [
460
+ {
461
+ type: 'web_search_result',
462
+ url: 'https://example.com/claude-vertex',
463
+ title: 'Claude on Vertex',
464
+ encrypted_content: 'opaque',
465
+ page_age: '1d',
466
+ },
467
+ ],
468
+ } as MessageContentComplex,
469
+ {
470
+ type: ContentTypes.TEXT,
471
+ text: ' \n\t ',
472
+ },
473
+ {
474
+ type: ContentTypes.TEXT,
475
+ text: 'The calculation result is 42.',
476
+ },
477
+ ],
478
+ },
479
+ {
480
+ role: 'user',
481
+ content: 'Follow up on that.',
482
+ },
483
+ ];
484
+
485
+ const { messages } = formatAgentMessages(
486
+ payload,
487
+ undefined,
488
+ new Set(['web_search', 'calculator']),
489
+ undefined,
490
+ { provider: Providers.ANTHROPIC }
491
+ );
492
+ const anthropicPayload = _convertMessagesToAnthropicPayload(messages);
493
+ const allBlocks = anthropicPayload.messages.flatMap((message) =>
494
+ typeof message.content === 'string'
495
+ ? []
496
+ : getAnthropicPayloadBlocks(message.content)
497
+ );
498
+ const serverToolMessageBlocks = anthropicPayload.messages
499
+ .map((message) =>
500
+ typeof message.content === 'string'
501
+ ? []
502
+ : getAnthropicPayloadBlocks(message.content)
503
+ )
504
+ .find((blocks) => blocks.some((block) => block.id === serverToolId));
505
+ const serverToolUseBlocks = allBlocks.filter(
506
+ (block) => block.type === 'server_tool_use'
507
+ );
508
+ const webSearchResultBlocks = allBlocks.filter(
509
+ (block) => block.type === 'web_search_tool_result'
510
+ );
511
+ const regularToolUseBlocks = allBlocks.filter(
512
+ (block) => block.type === 'tool_use' && block.id === calculatorToolId
513
+ );
514
+ const whitespaceTextBlocks = allBlocks.filter(
515
+ (block) =>
516
+ block.type === ContentTypes.TEXT &&
517
+ typeof block.text === 'string' &&
518
+ block.text.trim().length === 0
519
+ );
520
+
521
+ expect(messages.some((message) => message instanceof ToolMessage)).toBe(
522
+ true
523
+ );
524
+ expect(
525
+ messages.some(
526
+ (message) =>
527
+ message instanceof ToolMessage &&
528
+ message.tool_call_id === serverToolId
529
+ )
530
+ ).toBe(false);
531
+ expect(serverToolUseBlocks).toEqual([
532
+ {
533
+ type: 'server_tool_use',
534
+ id: serverToolId,
535
+ name: 'web_search',
536
+ input: { query: 'Claude Vertex web search' },
537
+ },
538
+ ]);
539
+ expect(webSearchResultBlocks).toHaveLength(1);
540
+ expect(webSearchResultBlocks[0].tool_use_id).toBe(serverToolId);
541
+ expect(serverToolMessageBlocks).toBeDefined();
542
+ expect(
543
+ serverToolMessageBlocks?.findIndex((block) => block.id === serverToolId)
544
+ ).toBeLessThan(
545
+ serverToolMessageBlocks?.findIndex(
546
+ (block) => block.tool_use_id === serverToolId
547
+ ) ?? -1
548
+ );
549
+ expect(regularToolUseBlocks).toHaveLength(1);
550
+ expect(whitespaceTextBlocks).toHaveLength(0);
551
+ });
552
+
553
+ it('preserves multiple Anthropic server tool pairs from one persisted turn', () => {
554
+ const firstSearchId = `${Constants.ANTHROPIC_SERVER_TOOL_PREFIX}search_1`;
555
+ const secondSearchId = `${Constants.ANTHROPIC_SERVER_TOOL_PREFIX}search_2`;
556
+ const calculatorToolId = 'toolu_calc_duplicate';
557
+ const payload: TPayload = [
558
+ {
559
+ role: 'user',
560
+ content:
561
+ 'Use native web search twice for current docs and calculate 19 * 23.',
562
+ },
563
+ {
564
+ role: 'assistant',
565
+ content: [
566
+ {
567
+ type: ContentTypes.TEXT,
568
+ text: 'I will check the docs and calculate.',
569
+ },
570
+ {
571
+ type: ContentTypes.TOOL_CALL,
572
+ tool_call: {
573
+ id: firstSearchId,
574
+ name: 'web_search',
575
+ args: '{"query":"Anthropic web search docs"}',
576
+ },
577
+ },
578
+ {
579
+ type: ContentTypes.TOOL_CALL,
580
+ tool_call: {
581
+ id: calculatorToolId,
582
+ name: 'calculator',
583
+ args: '{"input":"19 * 23"}',
584
+ output: '437',
585
+ },
586
+ },
587
+ {
588
+ type: ContentTypes.TOOL_CALL,
589
+ tool_call: {
590
+ id: calculatorToolId,
591
+ name: 'calculator',
592
+ args: '',
593
+ },
594
+ },
595
+ {
596
+ type: 'web_search_tool_result',
597
+ tool_use_id: firstSearchId,
598
+ content: [
599
+ {
600
+ type: 'web_search_result',
601
+ url: 'https://example.com/anthropic-web-search',
602
+ title: 'Anthropic web search',
603
+ encrypted_content: 'opaque-1',
604
+ page_age: '1d',
605
+ },
606
+ ],
607
+ } as MessageContentComplex,
608
+ {
609
+ type: ContentTypes.TEXT,
610
+ text: 'I found the first result and will check one more.',
611
+ },
612
+ {
613
+ type: ContentTypes.TOOL_CALL,
614
+ tool_call: {
615
+ id: secondSearchId,
616
+ name: 'web_search',
617
+ args: '{"query":"Anthropic web_search_20260209 docs"}',
618
+ },
619
+ },
620
+ {
621
+ type: 'web_search_tool_result',
622
+ tool_use_id: secondSearchId,
623
+ content: [
624
+ {
625
+ type: 'web_search_result',
626
+ url: 'https://example.com/anthropic-web-search-20260209',
627
+ title: 'Anthropic web_search_20260209',
628
+ encrypted_content: 'opaque-2',
629
+ page_age: '1d',
630
+ },
631
+ ],
632
+ } as MessageContentComplex,
633
+ {
634
+ type: ContentTypes.TEXT,
635
+ text: 'The calculation result is 437.',
636
+ },
637
+ ],
638
+ },
639
+ {
640
+ role: 'user',
641
+ content: 'Summarize the prior results.',
642
+ },
643
+ ];
644
+
645
+ const { messages } = formatAgentMessages(
646
+ payload,
647
+ undefined,
648
+ new Set(['web_search', 'calculator']),
649
+ undefined,
650
+ { provider: Providers.ANTHROPIC }
651
+ );
652
+ const anthropicPayload = _convertMessagesToAnthropicPayload(messages);
653
+ const allBlocks = anthropicPayload.messages.flatMap((message) =>
654
+ typeof message.content === 'string'
655
+ ? []
656
+ : getAnthropicPayloadBlocks(message.content)
657
+ );
658
+ const serverToolUseBlocks = allBlocks.filter(
659
+ (block) => block.type === 'server_tool_use'
660
+ );
661
+ const webSearchResultBlocks = allBlocks.filter(
662
+ (block) => block.type === 'web_search_tool_result'
663
+ );
664
+ const calculatorUseBlocks = allBlocks.filter(
665
+ (block) => block.type === 'tool_use' && block.id === calculatorToolId
666
+ );
667
+ const serverBlocksByMessage = anthropicPayload.messages
668
+ .map((message) =>
669
+ typeof message.content === 'string'
670
+ ? []
671
+ : getAnthropicPayloadBlocks(message.content)
672
+ )
673
+ .filter((blocks) =>
674
+ blocks.some(
675
+ (block) =>
676
+ block.id === firstSearchId ||
677
+ block.id === secondSearchId ||
678
+ block.tool_use_id === firstSearchId ||
679
+ block.tool_use_id === secondSearchId
680
+ )
681
+ );
682
+
683
+ expect(serverToolUseBlocks.map((block) => block.id)).toEqual([
684
+ firstSearchId,
685
+ secondSearchId,
686
+ ]);
687
+ expect(webSearchResultBlocks.map((block) => block.tool_use_id)).toEqual([
688
+ firstSearchId,
689
+ secondSearchId,
690
+ ]);
691
+ expect(calculatorUseBlocks).toHaveLength(1);
692
+ for (const blocks of serverBlocksByMessage) {
693
+ const serverUseIndexes = new Map<string, number>();
694
+ blocks.forEach((block, index) => {
695
+ if (block.type === 'server_tool_use' && typeof block.id === 'string') {
696
+ serverUseIndexes.set(block.id, index);
697
+ }
698
+ });
699
+ for (const block of blocks) {
700
+ if (
701
+ block.type !== 'web_search_tool_result' ||
702
+ typeof block.tool_use_id !== 'string'
703
+ ) {
704
+ continue;
705
+ }
706
+ expect(serverUseIndexes.get(block.tool_use_id)).toBeLessThan(
707
+ blocks.indexOf(block)
708
+ );
709
+ }
710
+ }
711
+ });
712
+
713
+ it('preserves Anthropic server tool pairs before regular tool boundaries', () => {
714
+ const serverToolId = `${Constants.ANTHROPIC_SERVER_TOOL_PREFIX}search_before_calc`;
715
+ const calculatorToolId = 'toolu_calc_after_search';
716
+ const payload: TPayload = [
717
+ {
718
+ role: 'user',
719
+ content: 'Search first, then calculate 19 * 23.',
720
+ },
721
+ {
722
+ role: 'assistant',
723
+ content: [
724
+ {
725
+ type: ContentTypes.TEXT,
726
+ text: 'I will search first.',
727
+ },
728
+ {
729
+ type: ContentTypes.TOOL_CALL,
730
+ tool_call: {
731
+ id: serverToolId,
732
+ name: 'web_search',
733
+ args: '{"query":"Anthropic web search docs"}',
734
+ },
735
+ },
736
+ {
737
+ type: 'web_search_tool_result',
738
+ tool_use_id: serverToolId,
739
+ content: [
740
+ {
741
+ type: 'web_search_result',
742
+ url: 'https://example.com/anthropic-web-search',
743
+ title: 'Anthropic web search',
744
+ encrypted_content: 'opaque',
745
+ page_age: '1d',
746
+ },
747
+ ],
748
+ } as MessageContentComplex,
749
+ {
750
+ type: ContentTypes.TEXT,
751
+ text: 'Now I will calculate.',
752
+ tool_call_ids: [calculatorToolId],
753
+ },
754
+ {
755
+ type: ContentTypes.TOOL_CALL,
756
+ tool_call: {
757
+ id: calculatorToolId,
758
+ name: 'calculator',
759
+ args: '{"input":"19 * 23"}',
760
+ output: '437',
761
+ },
762
+ },
763
+ {
764
+ type: ContentTypes.TEXT,
765
+ text: 'The calculation result is 437.',
766
+ },
767
+ ],
768
+ },
769
+ {
770
+ role: 'user',
771
+ content: 'Summarize the prior results.',
772
+ },
773
+ ];
774
+
775
+ const { messages } = formatAgentMessages(
776
+ payload,
777
+ undefined,
778
+ new Set(['web_search', 'calculator']),
779
+ undefined,
780
+ { provider: Providers.ANTHROPIC }
781
+ );
782
+ const anthropicPayload = _convertMessagesToAnthropicPayload(messages);
783
+ const assistantBlocks = anthropicPayload.messages
784
+ .filter((message) => message.role === 'assistant')
785
+ .flatMap((message) =>
786
+ typeof message.content === 'string'
787
+ ? []
788
+ : getAnthropicPayloadBlocks(message.content)
789
+ );
790
+ const serverToolUseIndex = assistantBlocks.findIndex(
791
+ (block) => block.type === 'server_tool_use' && block.id === serverToolId
792
+ );
793
+ const serverResultIndex = assistantBlocks.findIndex(
794
+ (block) =>
795
+ block.type === 'web_search_tool_result' &&
796
+ block.tool_use_id === serverToolId
797
+ );
798
+ const calculatorToolUseIndex = assistantBlocks.findIndex(
799
+ (block) => block.type === 'tool_use' && block.id === calculatorToolId
800
+ );
801
+
802
+ expect(serverToolUseIndex).toBeGreaterThanOrEqual(0);
803
+ expect(serverResultIndex).toBeGreaterThan(serverToolUseIndex);
804
+ expect(calculatorToolUseIndex).toBeGreaterThan(serverResultIndex);
805
+ expect(
806
+ messages.some(
807
+ (message) =>
808
+ message instanceof ToolMessage &&
809
+ message.tool_call_id === calculatorToolId
810
+ )
811
+ ).toBe(true);
812
+ });
813
+
814
+ it('keeps non-Anthropic array-content tool calls on AIMessage.tool_calls', () => {
815
+ const toolId = 'toolu_openai_after_image';
816
+ const payload: TPayload = [
817
+ {
818
+ role: 'assistant',
819
+ content: [
820
+ {
821
+ type: ContentTypes.IMAGE_URL,
822
+ image_url: { url: 'https://example.com/chart.png' },
823
+ },
824
+ {
825
+ type: ContentTypes.TEXT,
826
+ text: 'I will inspect the image.',
827
+ tool_call_ids: [toolId],
828
+ },
829
+ {
830
+ type: ContentTypes.TOOL_CALL,
831
+ tool_call: {
832
+ id: toolId,
833
+ name: 'describe_image',
834
+ args: '{"focus":"colors"}',
835
+ output: 'Blue and gray.',
836
+ },
837
+ },
838
+ ],
839
+ },
840
+ ];
841
+
842
+ const { messages } = formatAgentMessages(
843
+ payload,
844
+ undefined,
845
+ new Set(['describe_image']),
846
+ undefined,
847
+ { provider: Providers.OPENAI }
848
+ );
849
+ const aiMessage = messages.find(
850
+ (message) => message instanceof AIMessage
851
+ ) as AIMessage;
852
+
853
+ expect(Array.isArray(aiMessage.content)).toBe(true);
854
+ expect(aiMessage.tool_calls).toEqual([
855
+ {
856
+ id: toolId,
857
+ name: 'describe_image',
858
+ args: { focus: 'colors' },
859
+ },
860
+ ]);
861
+ expect(
862
+ (aiMessage.content as MessageContentComplex[]).some(
863
+ (block) => block.type === 'tool_use'
864
+ )
865
+ ).toBe(false);
866
+ expect(
867
+ messages.some(
868
+ (message) =>
869
+ message instanceof ToolMessage && message.tool_call_id === toolId
870
+ )
871
+ ).toBe(true);
872
+ });
873
+
874
+ it('normalizes Anthropic inlined tool use ids before tool results', () => {
875
+ const serverToolId = `${Constants.ANTHROPIC_SERVER_TOOL_PREFIX}search_before_invalid_calc`;
876
+ const rawCalculatorToolId = 'toolu|responses|calculator|invalid';
877
+ const payload: TPayload = [
878
+ {
879
+ role: 'user',
880
+ content: 'Search first, then calculate 21 * 2.',
881
+ },
882
+ {
883
+ role: 'assistant',
884
+ content: [
885
+ {
886
+ type: ContentTypes.TOOL_CALL,
887
+ tool_call: {
888
+ id: serverToolId,
889
+ name: 'web_search',
890
+ args: '{"query":"Anthropic web search docs"}',
891
+ },
892
+ },
893
+ {
894
+ type: 'web_search_tool_result',
895
+ tool_use_id: serverToolId,
896
+ content: [
897
+ {
898
+ type: 'web_search_result',
899
+ url: 'https://example.com/anthropic-web-search',
900
+ title: 'Anthropic web search',
901
+ encrypted_content: 'opaque',
902
+ page_age: '1d',
903
+ },
904
+ ],
905
+ } as MessageContentComplex,
906
+ {
907
+ type: ContentTypes.TEXT,
908
+ text: 'Now I will calculate.',
909
+ tool_call_ids: [rawCalculatorToolId],
910
+ },
911
+ {
912
+ type: ContentTypes.TOOL_CALL,
913
+ tool_call: {
914
+ id: rawCalculatorToolId,
915
+ name: 'calculator',
916
+ args: '{"input":"21 * 2"}',
917
+ output: '42',
918
+ },
919
+ },
920
+ ],
921
+ },
922
+ {
923
+ role: 'user',
924
+ content: 'Summarize the result.',
925
+ },
926
+ ];
927
+
928
+ const { messages } = formatAgentMessages(
929
+ payload,
930
+ undefined,
931
+ new Set(['web_search', 'calculator']),
932
+ undefined,
933
+ { provider: Providers.ANTHROPIC }
934
+ );
935
+ const anthropicPayload = _convertMessagesToAnthropicPayload(messages);
936
+ const allBlocks = anthropicPayload.messages.flatMap((message) =>
937
+ typeof message.content === 'string'
938
+ ? []
939
+ : getAnthropicPayloadBlocks(message.content)
940
+ );
941
+ const toolUseBlock = allBlocks.find(
942
+ (block) => block.type === 'tool_use' && block.name === 'calculator'
943
+ );
944
+ const toolResultBlock = allBlocks.find(
945
+ (block) => block.type === 'tool_result'
946
+ );
947
+
948
+ expect(toolUseBlock?.id).not.toBe(rawCalculatorToolId);
949
+ expect(toolUseBlock?.id).toMatch(/^[a-zA-Z0-9_-]+$/);
950
+ expect(toolUseBlock?.id).toBe(toolResultBlock?.tool_use_id);
951
+ });
952
+
953
+ it('preserves repairable Anthropic server search results with drifted types', () => {
954
+ const serverToolId = `${Constants.ANTHROPIC_SERVER_TOOL_PREFIX}repairable_result`;
955
+ const payload: TPayload = [
956
+ {
957
+ role: 'user',
958
+ content: 'Search current Anthropic web search docs.',
959
+ },
960
+ {
961
+ role: 'assistant',
962
+ content: [
963
+ {
964
+ type: ContentTypes.TOOL_CALL,
965
+ tool_call: {
966
+ id: serverToolId,
967
+ name: 'web_search',
968
+ args: '{"query":"Anthropic web search docs"}',
969
+ },
970
+ },
971
+ {
972
+ type: ContentTypes.TEXT,
973
+ tool_use_id: serverToolId,
974
+ content: [
975
+ {
976
+ type: 'web_search_result',
977
+ url: 'https://example.com/anthropic-web-search',
978
+ title: 'Anthropic web search',
979
+ encrypted_content: 'opaque',
980
+ page_age: '1d',
981
+ },
982
+ ],
983
+ } as MessageContentComplex,
984
+ {
985
+ type: ContentTypes.TEXT,
986
+ text: 'I found the docs.',
987
+ },
988
+ ],
989
+ },
990
+ {
991
+ role: 'user',
992
+ content: 'Follow up.',
993
+ },
994
+ ];
995
+
996
+ const { messages } = formatAgentMessages(
997
+ payload,
998
+ undefined,
999
+ new Set(['web_search']),
1000
+ undefined,
1001
+ { provider: Providers.ANTHROPIC }
1002
+ );
1003
+ const anthropicPayload = _convertMessagesToAnthropicPayload(messages);
1004
+ const assistantBlocks = anthropicPayload.messages
1005
+ .filter((message) => message.role === 'assistant')
1006
+ .flatMap((message) =>
1007
+ typeof message.content === 'string'
1008
+ ? []
1009
+ : getAnthropicPayloadBlocks(message.content)
1010
+ );
1011
+ const serverToolUseIndex = assistantBlocks.findIndex(
1012
+ (block) => block.type === 'server_tool_use' && block.id === serverToolId
1013
+ );
1014
+ const serverResultIndex = assistantBlocks.findIndex(
1015
+ (block) =>
1016
+ block.type === 'web_search_tool_result' &&
1017
+ block.tool_use_id === serverToolId
1018
+ );
1019
+
1020
+ expect(serverToolUseIndex).toBeGreaterThanOrEqual(0);
1021
+ expect(serverResultIndex).toBeGreaterThan(serverToolUseIndex);
1022
+ expect(
1023
+ messages.some(
1024
+ (message) =>
1025
+ message instanceof ToolMessage &&
1026
+ message.tool_call_id === serverToolId
1027
+ )
1028
+ ).toBe(false);
1029
+ });
1030
+
1031
+ it('does not pair malformed Anthropic server tool result blocks', () => {
1032
+ const serverToolId = `${Constants.ANTHROPIC_SERVER_TOOL_PREFIX}malformed_result`;
1033
+ const payload: TPayload = [
1034
+ {
1035
+ role: 'user',
1036
+ content: 'Search current docs.',
1037
+ },
1038
+ {
1039
+ role: 'assistant',
1040
+ content: [
1041
+ {
1042
+ type: ContentTypes.TOOL_CALL,
1043
+ tool_call: {
1044
+ id: serverToolId,
1045
+ name: 'web_search',
1046
+ args: '{"query":"Anthropic web search docs"}',
1047
+ },
1048
+ },
1049
+ {
1050
+ type: ContentTypes.TEXT,
1051
+ text: 'Malformed result should not pair this server tool.',
1052
+ tool_use_id: serverToolId,
1053
+ } as MessageContentComplex,
1054
+ {
1055
+ type: ContentTypes.TEXT,
1056
+ text: 'Here is the final answer.',
1057
+ },
1058
+ ],
1059
+ },
1060
+ {
1061
+ role: 'user',
1062
+ content: 'Follow up.',
1063
+ },
1064
+ ];
1065
+
1066
+ const { messages } = formatAgentMessages(
1067
+ payload,
1068
+ undefined,
1069
+ new Set(['web_search']),
1070
+ undefined,
1071
+ { provider: Providers.ANTHROPIC }
1072
+ );
1073
+ const anthropicPayload = _convertMessagesToAnthropicPayload(messages);
1074
+ const allBlocks = anthropicPayload.messages.flatMap((message) =>
1075
+ typeof message.content === 'string'
1076
+ ? []
1077
+ : getAnthropicPayloadBlocks(message.content)
1078
+ );
1079
+
1080
+ expect(allBlocks.some((block) => block.id === serverToolId)).toBe(false);
1081
+ expect(allBlocks.some((block) => block.tool_use_id === serverToolId)).toBe(
1082
+ false
1083
+ );
1084
+ });
1085
+
1086
+ it('drops unpaired historical Anthropic Vertex web search calls from mixed turns', () => {
1087
+ const serverToolId = `${Constants.ANTHROPIC_SERVER_TOOL_PREFIX}vrtx_missing_result`;
1088
+ const calculatorToolId = 'toolu_calc_1';
1089
+ const payload: TPayload = [
1090
+ {
1091
+ role: 'user',
1092
+ content: 'Search for current Claude info and calculate 6 * 7.',
1093
+ },
1094
+ {
1095
+ role: 'assistant',
1096
+ content: [
1097
+ {
1098
+ type: ContentTypes.TOOL_CALL,
1099
+ tool_call: {
1100
+ id: serverToolId,
1101
+ name: 'web_search',
1102
+ args: '{"query":"Claude Vertex web search"}',
1103
+ output: 'Search results were formatted for the assistant.',
1104
+ },
1105
+ },
1106
+ {
1107
+ type: ContentTypes.TOOL_CALL,
1108
+ tool_call: {
1109
+ id: calculatorToolId,
1110
+ name: 'calculator',
1111
+ args: '{"expression":"6*7"}',
1112
+ output: '42',
1113
+ },
1114
+ },
1115
+ ],
1116
+ },
1117
+ {
1118
+ role: 'user',
1119
+ content: 'Follow up on that.',
1120
+ },
1121
+ ];
1122
+
1123
+ const { messages } = formatAgentMessages(
1124
+ payload,
1125
+ undefined,
1126
+ new Set(['web_search', 'calculator']),
1127
+ undefined,
1128
+ { provider: Providers.ANTHROPIC }
1129
+ );
1130
+ const anthropicPayload = _convertMessagesToAnthropicPayload(messages);
1131
+ const allBlocks = anthropicPayload.messages.flatMap((message) =>
1132
+ typeof message.content === 'string'
1133
+ ? []
1134
+ : getAnthropicPayloadBlocks(message.content)
1135
+ );
1136
+
1137
+ expect(
1138
+ messages.some(
1139
+ (message) =>
1140
+ message instanceof ToolMessage &&
1141
+ message.tool_call_id === serverToolId
1142
+ )
1143
+ ).toBe(false);
1144
+ expect(allBlocks.some((block) => block.id === serverToolId)).toBe(false);
1145
+ expect(allBlocks.some((block) => block.tool_use_id === serverToolId)).toBe(
1146
+ false
1147
+ );
1148
+ expect(
1149
+ allBlocks.some(
1150
+ (block) => block.type === 'tool_use' && block.id === calculatorToolId
1151
+ )
1152
+ ).toBe(true);
1153
+ });
1154
+
1155
+ it('deduplicates repeated Anthropic Vertex client tool calls in replay payloads', () => {
1156
+ const calculatorToolId = 'toolu_vrtx_calc_1';
1157
+ const payload: TPayload = [
1158
+ {
1159
+ role: 'user',
1160
+ content: 'Calculate 19 * 23.',
1161
+ },
1162
+ {
1163
+ role: 'assistant',
1164
+ content: [
1165
+ {
1166
+ type: ContentTypes.TOOL_CALL,
1167
+ tool_call: {
1168
+ id: calculatorToolId,
1169
+ name: 'calculator',
1170
+ args: '{"input":"19 * 23"}',
1171
+ output: '437',
1172
+ },
1173
+ },
1174
+ {
1175
+ type: ContentTypes.TOOL_CALL,
1176
+ tool_call: {
1177
+ id: calculatorToolId,
1178
+ name: 'calculator',
1179
+ args: '',
1180
+ },
1181
+ },
1182
+ {
1183
+ type: ContentTypes.TEXT,
1184
+ text: '437437',
1185
+ },
1186
+ ],
1187
+ },
1188
+ {
1189
+ role: 'user',
1190
+ content: 'What was the result?',
1191
+ },
1192
+ ];
1193
+
1194
+ const { messages } = formatAgentMessages(
1195
+ payload,
1196
+ undefined,
1197
+ new Set(['calculator']),
1198
+ undefined,
1199
+ { provider: Providers.ANTHROPIC }
1200
+ );
1201
+ const anthropicPayload = _convertMessagesToAnthropicPayload(messages);
1202
+ const allBlocks = anthropicPayload.messages.flatMap((message) =>
1203
+ typeof message.content === 'string'
1204
+ ? []
1205
+ : getAnthropicPayloadBlocks(message.content)
1206
+ );
1207
+ const toolUseBlocks = allBlocks.filter(
1208
+ (block) => block.type === 'tool_use' && block.id === calculatorToolId
1209
+ );
1210
+ const toolResultBlocks = allBlocks.filter(
1211
+ (block) =>
1212
+ block.type === 'tool_result' && block.tool_use_id === calculatorToolId
1213
+ );
1214
+
1215
+ expect(toolUseBlocks).toEqual([
1216
+ {
1217
+ type: 'tool_use',
1218
+ id: calculatorToolId,
1219
+ name: 'calculator',
1220
+ input: { input: '19 * 23' },
1221
+ },
1222
+ ]);
1223
+ expect(toolResultBlocks).toHaveLength(1);
1224
+ expect(toolResultBlocks[0].content).toBe('437');
1225
+ });
1226
+
392
1227
  it('should handle malformed tool call entries with missing tool_call property', () => {
393
1228
  const tools = new Set(['search']);
394
1229
  const payload = [
@@ -695,13 +1530,9 @@ describe('formatAgentMessages', () => {
695
1530
  expect(result.messages[1]).toBeInstanceOf(ToolMessage);
696
1531
  expect(result.messages[2]).toBeInstanceOf(AIMessage);
697
1532
  expect(result.messages[3]).toBeInstanceOf(ToolMessage);
698
- expect((result.messages[0] as AIMessage).tool_calls?.[0].id).toBe(
699
- 'call_1'
700
- );
1533
+ expect((result.messages[0] as AIMessage).tool_calls?.[0].id).toBe('call_1');
701
1534
  expect((result.messages[1] as ToolMessage).tool_call_id).toBe('call_1');
702
- expect((result.messages[2] as AIMessage).tool_calls?.[0].id).toBe(
703
- 'call_2'
704
- );
1535
+ expect((result.messages[2] as AIMessage).tool_calls?.[0].id).toBe('call_2');
705
1536
  expect((result.messages[3] as ToolMessage).tool_call_id).toBe('call_2');
706
1537
  });
707
1538