@librechat/agents 3.2.2 → 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 (401) 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 +33 -0
  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 +47 -21
  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 +33 -1
  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 +47 -21
  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/instrumentation.d.ts +1 -0
  230. package/dist/types/langfuse.d.ts +4 -0
  231. package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -1
  232. package/dist/types/llm/anthropic/utils/message_outputs.d.ts +1 -1
  233. package/dist/types/llm/anthropic/utils/output_parsers.d.ts +2 -2
  234. package/dist/types/llm/bedrock/index.d.ts +2 -2
  235. package/dist/types/llm/fake.d.ts +3 -3
  236. package/dist/types/llm/google/index.d.ts +1 -0
  237. package/dist/types/llm/google/types.d.ts +1 -1
  238. package/dist/types/llm/google/utils/common.d.ts +2 -2
  239. package/dist/types/llm/google/utils/tools.d.ts +1 -1
  240. package/dist/types/llm/google/utils/zod_to_genai_parameters.d.ts +1 -1
  241. package/dist/types/llm/openai/index.d.ts +2 -2
  242. package/dist/types/llm/openai/utils/index.d.ts +1 -1
  243. package/dist/types/llm/openrouter/index.d.ts +4 -4
  244. package/dist/types/messages/contextPruning.d.ts +1 -1
  245. package/dist/types/messages/format.d.ts +9 -4
  246. package/dist/types/messages/prune.d.ts +1 -1
  247. package/dist/types/session/JsonlSessionStore.d.ts +1 -1
  248. package/dist/types/session/handlers.d.ts +1 -1
  249. package/dist/types/session/types.d.ts +1 -1
  250. package/dist/types/summarization/node.d.ts +1 -1
  251. package/dist/types/tools/SubagentTool.d.ts +2 -2
  252. package/dist/types/tools/ToolNode.d.ts +9 -2
  253. package/dist/types/tools/cloudflare/CloudflareSandboxExecutionEngine.d.ts +1 -1
  254. package/dist/types/tools/search/types.d.ts +1 -1
  255. package/dist/types/tools/search/utils.d.ts +11 -0
  256. package/dist/types/types/graph.d.ts +12 -4
  257. package/dist/types/types/llm.d.ts +4 -3
  258. package/dist/types/types/messages.d.ts +1 -1
  259. package/dist/types/types/run.d.ts +6 -6
  260. package/dist/types/types/stream.d.ts +2 -2
  261. package/dist/types/types/tools.d.ts +5 -1
  262. package/dist/types/utils/handlers.d.ts +2 -2
  263. package/dist/types/utils/run.d.ts +1 -1
  264. package/package.json +6 -3
  265. package/src/__tests__/stream.eagerEventExecution.test.ts +543 -6
  266. package/src/agents/AgentContext.ts +2 -2
  267. package/src/agents/__tests__/AgentContext.test.ts +3 -3
  268. package/src/agents/__tests__/promptCacheLiveHelpers.ts +1 -1
  269. package/src/events.ts +1 -1
  270. package/src/graphs/Graph.ts +329 -72
  271. package/src/graphs/MultiAgentGraph.ts +1 -1
  272. package/src/graphs/__tests__/Graph.reasoning.test.ts +919 -6
  273. package/src/graphs/__tests__/MultiAgentGraph.test.ts +1 -1
  274. package/src/graphs/__tests__/composition.smoke.test.ts +1 -1
  275. package/src/hooks/__tests__/HookRegistry.test.ts +1 -1
  276. package/src/hooks/__tests__/compactHooks.test.ts +8 -8
  277. package/src/hooks/__tests__/createWorkspacePolicyHook.test.ts +34 -22
  278. package/src/hooks/__tests__/executeHooks.test.ts +3 -3
  279. package/src/hooks/__tests__/integration.test.ts +3 -3
  280. package/src/hooks/__tests__/toolHooks.test.ts +10 -10
  281. package/src/hooks/createWorkspacePolicyHook.ts +17 -14
  282. package/src/hooks/executeHooks.ts +1 -1
  283. package/src/hooks/types.ts +5 -0
  284. package/src/instrumentation.ts +49 -8
  285. package/src/langfuse.ts +35 -1
  286. package/src/langfuseToolOutputTracing.ts +2 -2
  287. package/src/llm/anthropic/index.ts +1 -1
  288. package/src/llm/anthropic/utils/message_inputs.ts +1 -1
  289. package/src/llm/anthropic/utils/message_outputs.ts +3 -5
  290. package/src/llm/anthropic/utils/output_parsers.ts +5 -5
  291. package/src/llm/bedrock/index.ts +4 -4
  292. package/src/llm/bedrock/toolCache.test.ts +48 -9
  293. package/src/llm/bedrock/toolCache.ts +11 -6
  294. package/src/llm/custom-chat-models.smoke.test.ts +114 -0
  295. package/src/llm/fake.ts +30 -25
  296. package/src/llm/google/index.ts +43 -1
  297. package/src/llm/google/llm.spec.ts +173 -1
  298. package/src/llm/google/types.ts +1 -1
  299. package/src/llm/google/utils/common.ts +154 -45
  300. package/src/llm/google/utils/tools.ts +8 -8
  301. package/src/llm/google/utils/zod_to_genai_parameters.ts +4 -4
  302. package/src/llm/invoke.test.ts +3 -3
  303. package/src/llm/invoke.ts +170 -10
  304. package/src/llm/openai/index.ts +4 -4
  305. package/src/llm/openai/utils/index.ts +14 -14
  306. package/src/llm/openrouter/index.ts +4 -4
  307. package/src/llm/openrouter/reasoning.test.ts +2 -2
  308. package/src/llm/vertexai/fixThoughtSignatures.test.ts +1 -1
  309. package/src/llm/vertexai/index.ts +1 -1
  310. package/src/messages/cache.test.ts +22 -0
  311. package/src/messages/cache.ts +25 -12
  312. package/src/messages/content.ts +1 -1
  313. package/src/messages/contextPruning.ts +1 -1
  314. package/src/messages/format.ts +227 -43
  315. package/src/messages/formatAgentMessages.skills.test.ts +105 -26
  316. package/src/messages/formatAgentMessages.test.ts +841 -10
  317. package/src/messages/labelContentByAgent.test.ts +2 -2
  318. package/src/messages/prune.ts +1 -1
  319. package/src/messages/reducer.ts +1 -1
  320. package/src/messages/tools.ts +1 -1
  321. package/src/openai/__tests__/openai.test.ts +2 -2
  322. package/src/openai/index.ts +1 -1
  323. package/src/responses/__tests__/responses.test.ts +2 -2
  324. package/src/responses/index.ts +1 -1
  325. package/src/run.ts +82 -47
  326. package/src/session/AgentSession.ts +6 -6
  327. package/src/session/JsonlSessionStore.ts +3 -3
  328. package/src/session/__tests__/JsonlSessionStore.test.ts +5 -5
  329. package/src/session/__tests__/handlers.test.ts +2 -2
  330. package/src/session/handlers.ts +5 -5
  331. package/src/session/types.ts +1 -1
  332. package/src/specs/agent-handoffs.test.ts +1 -1
  333. package/src/specs/deterministic-trace-id.test.ts +50 -0
  334. package/src/specs/langfuse-callbacks.test.ts +2 -2
  335. package/src/specs/langfuse-metadata.test.ts +39 -0
  336. package/src/specs/langfuse-tool-output-tracing.test.ts +1 -1
  337. package/src/specs/multi-agent-summarization.test.ts +4 -4
  338. package/src/specs/subagent.test.ts +3 -3
  339. package/src/specs/summarization-unit.test.ts +1 -1
  340. package/src/specs/thinking-handoff.test.ts +1 -1
  341. package/src/splitStream.test.ts +48 -0
  342. package/src/stream.test.ts +53 -3
  343. package/src/stream.ts +450 -39
  344. package/src/summarization/__tests__/aggregator.test.ts +2 -2
  345. package/src/summarization/__tests__/node.test.ts +2 -2
  346. package/src/summarization/node.ts +1 -1
  347. package/src/tools/BashProgrammaticToolCalling.ts +5 -5
  348. package/src/tools/Calculator.ts +1 -1
  349. package/src/tools/CodeExecutor.ts +2 -4
  350. package/src/tools/SubagentTool.ts +2 -2
  351. package/src/tools/ToolNode.ts +37 -16
  352. package/src/tools/ToolSearch.ts +1 -1
  353. package/src/tools/__tests__/CloudflareSandboxExecution.test.ts +4 -4
  354. package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +12 -12
  355. package/src/tools/__tests__/LocalExecutionTools.test.ts +125 -93
  356. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +29 -5
  357. package/src/tools/__tests__/ReadFile.test.ts +1 -1
  358. package/src/tools/__tests__/SkillTool.test.ts +4 -4
  359. package/src/tools/__tests__/SubagentExecutor.test.ts +17 -13
  360. package/src/tools/__tests__/SubagentTool.test.ts +2 -2
  361. package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +1 -1
  362. package/src/tools/__tests__/ToolNode.outputReferences.test.ts +2 -5
  363. package/src/tools/__tests__/ToolNode.session.test.ts +1 -1
  364. package/src/tools/__tests__/ToolSearch.test.ts +1 -1
  365. package/src/tools/__tests__/annotateMessagesForLLM.test.ts +1 -1
  366. package/src/tools/__tests__/directToolHITLResumeScope.test.ts +35 -32
  367. package/src/tools/__tests__/directToolHooks.test.ts +41 -0
  368. package/src/tools/__tests__/handlers.test.ts +2 -2
  369. package/src/tools/__tests__/hitl.test.ts +11 -11
  370. package/src/tools/__tests__/localToolNames.test.ts +8 -6
  371. package/src/tools/__tests__/skillCatalog.test.ts +1 -1
  372. package/src/tools/__tests__/subagentHooks.test.ts +20 -10
  373. package/src/tools/__tests__/workspaceSeam.test.ts +20 -7
  374. package/src/tools/cloudflare/CloudflareSandboxExecutionEngine.ts +9 -6
  375. package/src/tools/cloudflare/CloudflareSandboxTools.ts +19 -19
  376. package/src/tools/handlers.ts +5 -5
  377. package/src/tools/local/CompileCheckTool.ts +4 -7
  378. package/src/tools/local/FileCheckpointer.ts +6 -5
  379. package/src/tools/local/LocalCodingTools.ts +100 -45
  380. package/src/tools/local/LocalExecutionEngine.ts +5 -5
  381. package/src/tools/local/LocalExecutionTools.ts +9 -9
  382. package/src/tools/local/LocalProgrammaticToolCalling.ts +5 -4
  383. package/src/tools/local/attachments.ts +0 -6
  384. package/src/tools/local/resolveLocalExecutionTools.ts +15 -15
  385. package/src/tools/search/firecrawl.ts +1 -1
  386. package/src/tools/search/jina-reranker.test.ts +148 -37
  387. package/src/tools/search/rerankers.ts +14 -4
  388. package/src/tools/search/tavily-search.ts +2 -2
  389. package/src/tools/search/types.ts +1 -1
  390. package/src/tools/search/utils.ts +99 -9
  391. package/src/tools/subagent/SubagentExecutor.ts +12 -6
  392. package/src/types/graph.ts +20 -12
  393. package/src/types/llm.ts +7 -6
  394. package/src/types/messages.ts +1 -1
  395. package/src/types/run.ts +7 -7
  396. package/src/types/stream.ts +2 -2
  397. package/src/types/tools.ts +5 -1
  398. package/src/utils/handlers.ts +2 -2
  399. package/src/utils/llmConfig.ts +1 -1
  400. package/src/utils/logging.ts +20 -10
  401. package/src/utils/run.ts +2 -2
@@ -1,11 +1,11 @@
1
1
  import type { StructuredToolInterface } from '@langchain/core/tools';
2
2
  import type { RunnableToolLike } from '@langchain/core/runnables';
3
3
  import type { ToolCall } from '@langchain/core/messages/tool';
4
- import type { HookRegistry } from '@/hooks';
5
4
  import type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';
6
5
  import type { MessageContentComplex, ToolErrorData } from './stream';
7
6
  import type { HumanInTheLoopConfig } from './hitl';
8
7
  import type { LangfuseConfig } from './graph';
8
+ import type { HookRegistry } from '@/hooks';
9
9
  /** Replacement type for `import type { ToolCall } from '@langchain/core/messages/tool'` in order to have stringified args typed */
10
10
  export type CustomToolCall = {
11
11
  name: string;
@@ -83,6 +83,9 @@ export type ToolNodeOptions = {
83
83
  toolDefinitions?: Map<string, LCTool>;
84
84
  /** Agent ID for event-driven mode (used to identify which agent's context to use) */
85
85
  agentId?: string;
86
+ /** ID of the agent that owns this tool node, surfaced to hooks as `executingAgentId`
87
+ * so a batch can be attributed to a specific agent even where `agentId` is undefined. */
88
+ executingAgentId?: string;
86
89
  /** Tool names that must be executed directly (via runTool) even in event-driven mode (e.g., graph-managed handoff tools) */
87
90
  directToolNames?: Set<string>;
88
91
  /** Opt-in eager execution for event-driven tool calls. */
@@ -892,6 +895,7 @@ export type ProgrammaticHookContext = {
892
895
  runId: string;
893
896
  threadId?: string;
894
897
  agentId?: string;
898
+ executingAgentId?: string;
895
899
  };
896
900
  /** Search mode: code_interpreter uses external sandbox, local uses safe substring matching */
897
901
  export type ToolSearchMode = 'code_interpreter' | 'local';
@@ -14,9 +14,9 @@
14
14
  * });
15
15
  * ```
16
16
  */
17
- import { GraphEvents } from '@/common';
18
- import { createContentAggregator } from '@/stream';
19
17
  import type * as t from '@/types';
18
+ import { createContentAggregator } from '@/stream';
19
+ import { GraphEvents } from '@/common';
20
20
  interface HandlerCallbacks {
21
21
  onRunStep?: (event: GraphEvents.ON_RUN_STEP, data: t.StreamEventData) => void;
22
22
  onRunStepCompleted?: (event: GraphEvents.ON_RUN_STEP_COMPLETED, data: t.StreamEventData) => void;
@@ -1,5 +1,5 @@
1
1
  import { CallbackManagerForChainRun } from '@langchain/core/callbacks/manager';
2
- import { Runnable, RunnableConfig } from '@langchain/core/runnables';
2
+ import { RunnableConfig, Runnable } from '@langchain/core/runnables';
3
3
  /**
4
4
  * Delays the execution for a specified number of milliseconds.
5
5
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "3.2.02",
3
+ "version": "3.2.31",
4
4
  "main": "./dist/cjs/main.cjs",
5
5
  "module": "./dist/esm/main.mjs",
6
6
  "types": "./dist/types/index.d.ts",
@@ -103,7 +103,7 @@
103
103
  ],
104
104
  "packageManager": "npm@10.5.2",
105
105
  "engines": {
106
- "node": ">=20.0.0"
106
+ "node": ">=24.0.0"
107
107
  },
108
108
  "files": [
109
109
  "dist",
@@ -116,6 +116,8 @@
116
116
  "prepublishOnly": "npm run build",
117
117
  "build": "NODE_ENV=production rollup -c && tsc -p tsconfig.build.json",
118
118
  "build:dev": "rollup -c",
119
+ "sort-imports": "node scripts/sort-imports.ts",
120
+ "sort-imports:check": "node scripts/sort-imports.ts --check",
119
121
  "start": "node dist/esm/main.js",
120
122
  "clean": "node ./config/clean.js",
121
123
  "script": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/cli.ts",
@@ -269,7 +271,7 @@
269
271
  "@rollup/plugin-typescript": "^12.1.2",
270
272
  "@swc/core": "^1.6.13",
271
273
  "@types/jest": "^30.0.0",
272
- "@types/node": "^20.14.11",
274
+ "@types/node": "^24.13.1",
273
275
  "@types/node-fetch": "^2.6.13",
274
276
  "@types/yargs-parser": "^21.0.3",
275
277
  "@typescript-eslint/eslint-plugin": "^8.24.0",
@@ -294,6 +296,7 @@
294
296
  },
295
297
  "lint-staged": {
296
298
  "*.{js,ts}": [
299
+ "node scripts/sort-imports.ts",
297
300
  "prettier --write",
298
301
  "eslint --fix"
299
302
  ],
@@ -2,21 +2,22 @@ import { describe, it, expect, jest, afterEach } from '@jest/globals';
2
2
  import type { AgentContext } from '@/agents/AgentContext';
3
3
  import type { StandardGraph } from '@/graphs';
4
4
  import type * as t from '@/types';
5
+ import {
6
+ STREAMED_TOOL_CALL_SEAL_METADATA_KEY,
7
+ STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY,
8
+ OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER,
9
+ } from '@/tools/streamedToolCallSeals';
5
10
  import {
6
11
  Constants,
7
12
  ContentTypes,
8
13
  GraphEvents,
9
14
  Providers,
10
15
  StepTypes,
16
+ ToolCallTypes,
11
17
  } from '@/common';
18
+ import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
12
19
  import { HandlerRegistry } from '@/events';
13
20
  import * as events from '@/utils/events';
14
- import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
15
- import {
16
- STREAMED_TOOL_CALL_SEAL_METADATA_KEY,
17
- STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY,
18
- OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER,
19
- } from '@/tools/streamedToolCallSeals';
20
21
 
21
22
  function createGraph(overrides: Partial<StandardGraph> = {}): StandardGraph {
22
23
  const runSteps = new Map<string, t.RunStep>();
@@ -97,6 +98,42 @@ function chunkStateKey(stepKey: string, chunkKey: string | number): string {
97
98
  return `${stepKey}\u0000${String(chunkKey)}`;
98
99
  }
99
100
 
101
+ function attachContentAggregator(graph: StandardGraph): {
102
+ contentParts: Array<t.MessageContentComplex | undefined>;
103
+ } {
104
+ const { contentParts, aggregateContent } = createContentAggregator();
105
+ const dispatchRunStep = graph.dispatchRunStep.bind(graph);
106
+ const stepIndices = new Map<string, number>();
107
+ graph.dispatchRunStep = jest.fn<StandardGraph['dispatchRunStep']>(
108
+ async (stepKey, stepDetails, metadata) => {
109
+ const stepId = await dispatchRunStep(stepKey, stepDetails, metadata);
110
+ const runStep = graph.getRunStep(stepId);
111
+ if (runStep != null) {
112
+ if (!stepIndices.has(stepId)) {
113
+ stepIndices.set(stepId, stepIndices.size);
114
+ }
115
+ aggregateContent({
116
+ event: GraphEvents.ON_RUN_STEP,
117
+ data: {
118
+ ...runStep,
119
+ index: stepIndices.get(stepId) ?? 0,
120
+ },
121
+ });
122
+ }
123
+ return stepId;
124
+ }
125
+ );
126
+ graph.dispatchMessageDelta = jest.fn<StandardGraph['dispatchMessageDelta']>(
127
+ async (id, delta) => {
128
+ aggregateContent({
129
+ event: GraphEvents.ON_MESSAGE_DELTA,
130
+ data: { id, delta } as t.MessageDeltaEvent,
131
+ });
132
+ }
133
+ );
134
+ return { contentParts };
135
+ }
136
+
100
137
  const finalToolCallResponseMetadata = { finish_reason: 'tool_calls' };
101
138
  const openAIResponsesToolCallMetadata = {
102
139
  [STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY]:
@@ -1611,6 +1648,506 @@ describe('ChatModelStreamHandler eager event tool execution', () => {
1611
1648
  );
1612
1649
  });
1613
1650
 
1651
+ it('defers disableStreaming mixed hidden reasoning and visible text chunks', async () => {
1652
+ const dispatchMessageDelta = jest.fn<StandardGraph['dispatchMessageDelta']>(
1653
+ async () => undefined
1654
+ );
1655
+ const dispatchReasoningDelta = jest.fn<
1656
+ StandardGraph['dispatchReasoningDelta']
1657
+ >(async () => undefined);
1658
+ const agentContext: Partial<AgentContext> = {
1659
+ provider: Providers.OPENAI,
1660
+ reasoningKey: 'reasoning_content',
1661
+ clientOptions: { disableStreaming: true } as t.OpenAIClientOptions,
1662
+ currentTokenType: ContentTypes.TEXT,
1663
+ toolDefinitions: [],
1664
+ graphTools: [],
1665
+ agentId: 'agent_1',
1666
+ };
1667
+ const graph = createGraph({
1668
+ dispatchMessageDelta,
1669
+ dispatchReasoningDelta,
1670
+ getAgentContext: jest.fn(
1671
+ () => agentContext
1672
+ ) as unknown as StandardGraph['getAgentContext'],
1673
+ });
1674
+ const handler = new ChatModelStreamHandler();
1675
+ const metadata = { langgraph_node: 'agent' };
1676
+
1677
+ const mixedChunk = {
1678
+ content: 'final answer',
1679
+ additional_kwargs: {
1680
+ reasoning_content: 'hidden reasoning',
1681
+ },
1682
+ } as unknown as t.StreamChunk;
1683
+
1684
+ await handler.handle(
1685
+ GraphEvents.CHAT_MODEL_STREAM,
1686
+ { chunk: mixedChunk },
1687
+ metadata,
1688
+ graph
1689
+ );
1690
+ await handler.handle(
1691
+ GraphEvents.CHAT_MODEL_STREAM,
1692
+ { chunk: mixedChunk },
1693
+ metadata,
1694
+ graph
1695
+ );
1696
+
1697
+ expect(dispatchReasoningDelta).not.toHaveBeenCalled();
1698
+ expect(dispatchMessageDelta).not.toHaveBeenCalled();
1699
+ expect(graph.dispatchRunStep).not.toHaveBeenCalled();
1700
+ });
1701
+
1702
+ it('leaves mixed hidden reasoning and visible text chunks on the streaming path', async () => {
1703
+ const dispatchMessageDelta = jest.fn<StandardGraph['dispatchMessageDelta']>(
1704
+ async () => undefined
1705
+ );
1706
+ const dispatchReasoningDelta = jest.fn<
1707
+ StandardGraph['dispatchReasoningDelta']
1708
+ >(async () => undefined);
1709
+ const graph = createGraph({
1710
+ dispatchMessageDelta,
1711
+ dispatchReasoningDelta,
1712
+ getAgentContext: jest.fn(
1713
+ (): Partial<AgentContext> => ({
1714
+ provider: Providers.OPENAI,
1715
+ reasoningKey: 'reasoning_content',
1716
+ currentTokenType: ContentTypes.TEXT,
1717
+ toolDefinitions: [],
1718
+ graphTools: [],
1719
+ agentId: 'agent_1',
1720
+ })
1721
+ ) as unknown as StandardGraph['getAgentContext'],
1722
+ });
1723
+ const metadata = { langgraph_node: 'agent' };
1724
+
1725
+ await new ChatModelStreamHandler().handle(
1726
+ GraphEvents.CHAT_MODEL_STREAM,
1727
+ {
1728
+ chunk: {
1729
+ content: 'streamed answer',
1730
+ additional_kwargs: {
1731
+ reasoning_content: 'hidden reasoning',
1732
+ },
1733
+ } as unknown as t.StreamChunk,
1734
+ },
1735
+ metadata,
1736
+ graph
1737
+ );
1738
+
1739
+ expect(dispatchReasoningDelta).not.toHaveBeenCalled();
1740
+ expect(dispatchMessageDelta).toHaveBeenCalledTimes(1);
1741
+ expect(dispatchMessageDelta).toHaveBeenCalledWith(
1742
+ expect.stringMatching(/^step_/),
1743
+ {
1744
+ content: [{ type: ContentTypes.TEXT, text: 'streamed answer' }],
1745
+ },
1746
+ metadata
1747
+ );
1748
+ });
1749
+
1750
+ it('dispatches Gemini server-side tool context blocks from mixed stream content', async () => {
1751
+ const dispatchMessageDelta = jest.fn<StandardGraph['dispatchMessageDelta']>(
1752
+ async () => undefined
1753
+ );
1754
+ const graph = createGraph({
1755
+ dispatchMessageDelta,
1756
+ getAgentContext: jest.fn(
1757
+ (): Partial<AgentContext> => ({
1758
+ provider: Providers.GOOGLE,
1759
+ reasoningKey: 'reasoning',
1760
+ currentTokenType: ContentTypes.TEXT,
1761
+ toolDefinitions: [],
1762
+ graphTools: [],
1763
+ agentId: 'agent_1',
1764
+ })
1765
+ ) as unknown as StandardGraph['getAgentContext'],
1766
+ });
1767
+ const handler = new ChatModelStreamHandler();
1768
+ const metadata = { langgraph_node: 'agent' };
1769
+ const toolCallPart: t.MessageContentComplex = {
1770
+ type: 'toolCall',
1771
+ toolCall: {
1772
+ id: 'server-search-1',
1773
+ name: 'google_search',
1774
+ args: {},
1775
+ },
1776
+ };
1777
+ const textPart: t.MessageContentComplex = {
1778
+ type: ContentTypes.TEXT,
1779
+ text: 'Search complete.',
1780
+ };
1781
+ const toolResponsePart: t.MessageContentComplex = {
1782
+ type: 'toolResponse',
1783
+ toolResponse: {
1784
+ id: 'server-search-1',
1785
+ name: 'google_search',
1786
+ response: { results: [] },
1787
+ },
1788
+ };
1789
+
1790
+ await handler.handle(
1791
+ GraphEvents.CHAT_MODEL_STREAM,
1792
+ {
1793
+ chunk: {
1794
+ content: [toolCallPart, textPart, toolResponsePart],
1795
+ } as unknown as t.StreamChunk,
1796
+ },
1797
+ metadata,
1798
+ graph
1799
+ );
1800
+
1801
+ expect(dispatchMessageDelta).toHaveBeenCalledTimes(3);
1802
+ expect(dispatchMessageDelta).toHaveBeenNthCalledWith(
1803
+ 1,
1804
+ expect.stringMatching(/^step_/),
1805
+ { content: [toolCallPart] },
1806
+ metadata
1807
+ );
1808
+ expect(dispatchMessageDelta).toHaveBeenNthCalledWith(
1809
+ 2,
1810
+ expect.stringMatching(/^step_/),
1811
+ { content: [textPart] },
1812
+ metadata
1813
+ );
1814
+ expect(dispatchMessageDelta).toHaveBeenNthCalledWith(
1815
+ 3,
1816
+ expect.stringMatching(/^step_/),
1817
+ { content: [toolResponsePart] },
1818
+ metadata
1819
+ );
1820
+ });
1821
+
1822
+ it('dispatches Gemini server-side tool context onto message steps after tool chunks', async () => {
1823
+ const messageDeltas: Array<{
1824
+ id: string;
1825
+ delta: t.MessageDelta;
1826
+ }> = [];
1827
+ const dispatchMessageDelta = jest.fn<StandardGraph['dispatchMessageDelta']>(
1828
+ async (id, delta) => {
1829
+ messageDeltas.push({
1830
+ id,
1831
+ delta,
1832
+ });
1833
+ }
1834
+ );
1835
+ const graph = createGraph({
1836
+ dispatchMessageDelta,
1837
+ getAgentContext: jest.fn(
1838
+ (): Partial<AgentContext> => ({
1839
+ provider: Providers.GOOGLE,
1840
+ reasoningKey: 'reasoning',
1841
+ currentTokenType: ContentTypes.TEXT,
1842
+ toolDefinitions: [{ name: 'weather' }],
1843
+ graphTools: [],
1844
+ agentId: 'agent_1',
1845
+ })
1846
+ ) as unknown as StandardGraph['getAgentContext'],
1847
+ });
1848
+ const handler = new ChatModelStreamHandler();
1849
+ const metadata = { langgraph_node: 'agent' };
1850
+ const toolCallPart: t.MessageContentComplex = {
1851
+ type: 'toolCall',
1852
+ toolCall: {
1853
+ id: 'server-search-1',
1854
+ name: 'google_search',
1855
+ args: {},
1856
+ },
1857
+ };
1858
+
1859
+ await handler.handle(
1860
+ GraphEvents.CHAT_MODEL_STREAM,
1861
+ {
1862
+ chunk: {
1863
+ content: [toolCallPart],
1864
+ tool_call_chunks: [
1865
+ {
1866
+ id: 'call_weather',
1867
+ name: 'weather',
1868
+ args: '{"city":"NYC"}',
1869
+ index: 0,
1870
+ },
1871
+ ],
1872
+ } as unknown as t.StreamChunk,
1873
+ },
1874
+ metadata,
1875
+ graph
1876
+ );
1877
+
1878
+ expect(dispatchMessageDelta).toHaveBeenCalledTimes(1);
1879
+ expect(graph.getRunStep(messageDeltas[0]?.id ?? '')?.type).toBe(
1880
+ StepTypes.MESSAGE_CREATION
1881
+ );
1882
+ expect(messageDeltas[0]?.delta).toEqual({ content: [toolCallPart] });
1883
+ });
1884
+
1885
+ it('dispatches Gemini server-side tool context when reasoning is present', async () => {
1886
+ const dispatchMessageDelta = jest.fn<StandardGraph['dispatchMessageDelta']>(
1887
+ async () => undefined
1888
+ );
1889
+ const dispatchReasoningDelta = jest.fn<
1890
+ StandardGraph['dispatchReasoningDelta']
1891
+ >(async () => undefined);
1892
+ const graph = createGraph({
1893
+ dispatchMessageDelta,
1894
+ dispatchReasoningDelta,
1895
+ getAgentContext: jest.fn(
1896
+ (): Partial<AgentContext> => ({
1897
+ provider: Providers.GOOGLE,
1898
+ reasoningKey: 'reasoning',
1899
+ currentTokenType: ContentTypes.TEXT,
1900
+ toolDefinitions: [],
1901
+ graphTools: [],
1902
+ agentId: 'agent_1',
1903
+ })
1904
+ ) as unknown as StandardGraph['getAgentContext'],
1905
+ });
1906
+ const handler = new ChatModelStreamHandler();
1907
+ const metadata = { langgraph_node: 'agent' };
1908
+ const toolCallPart: t.MessageContentComplex = {
1909
+ type: 'toolCall',
1910
+ toolCall: {
1911
+ id: 'server-search-1',
1912
+ name: 'google_search',
1913
+ args: {},
1914
+ },
1915
+ };
1916
+
1917
+ await handler.handle(
1918
+ GraphEvents.CHAT_MODEL_STREAM,
1919
+ {
1920
+ chunk: {
1921
+ content: [toolCallPart],
1922
+ additional_kwargs: {
1923
+ reasoning: 'Need to search before answering.',
1924
+ },
1925
+ } as unknown as t.StreamChunk,
1926
+ },
1927
+ metadata,
1928
+ graph
1929
+ );
1930
+
1931
+ expect(dispatchMessageDelta).toHaveBeenCalledTimes(1);
1932
+ expect(dispatchMessageDelta).toHaveBeenCalledWith(
1933
+ expect.stringMatching(/^step_/),
1934
+ { content: [toolCallPart] },
1935
+ metadata
1936
+ );
1937
+ expect(dispatchReasoningDelta).toHaveBeenCalledWith(
1938
+ expect.stringMatching(/^step_/),
1939
+ {
1940
+ content: [
1941
+ {
1942
+ type: ContentTypes.THINK,
1943
+ think: 'Need to search before answering.',
1944
+ },
1945
+ ],
1946
+ },
1947
+ metadata
1948
+ );
1949
+ });
1950
+
1951
+ it('dispatches Gemini server-side tool context before client tool calls in mixed chunks', async () => {
1952
+ const dispatchOrder: string[] = [];
1953
+ const dispatchMessageDelta = jest.fn<StandardGraph['dispatchMessageDelta']>(
1954
+ async (_id, delta) => {
1955
+ if (
1956
+ delta.content?.some(
1957
+ (contentPart) => contentPart.type === 'toolCall'
1958
+ ) === true
1959
+ ) {
1960
+ dispatchOrder.push('server-context');
1961
+ }
1962
+ }
1963
+ );
1964
+ const graph = createGraph({
1965
+ dispatchMessageDelta,
1966
+ getAgentContext: jest.fn(
1967
+ (): Partial<AgentContext> => ({
1968
+ provider: Providers.GOOGLE,
1969
+ reasoningKey: 'reasoning',
1970
+ currentTokenType: ContentTypes.TEXT,
1971
+ toolDefinitions: [{ name: 'weather' }],
1972
+ graphTools: [],
1973
+ agentId: 'agent_1',
1974
+ })
1975
+ ) as unknown as StandardGraph['getAgentContext'],
1976
+ });
1977
+ const dispatchRunStep = graph.dispatchRunStep.bind(graph);
1978
+ graph.dispatchRunStep = jest.fn<StandardGraph['dispatchRunStep']>(
1979
+ async (stepKey, stepDetails, metadata) => {
1980
+ const stepId = await dispatchRunStep(stepKey, stepDetails, metadata);
1981
+ if ((stepDetails as t.StepDetails).type === StepTypes.TOOL_CALLS) {
1982
+ dispatchOrder.push('client-tool-calls');
1983
+ }
1984
+ return stepId;
1985
+ }
1986
+ );
1987
+ const handler = new ChatModelStreamHandler();
1988
+ const metadata = { langgraph_node: 'agent' };
1989
+ const toolCallPart: t.MessageContentComplex = {
1990
+ type: 'toolCall',
1991
+ toolCall: {
1992
+ id: 'server-search-1',
1993
+ name: 'google_search',
1994
+ args: {},
1995
+ },
1996
+ };
1997
+
1998
+ await handler.handle(
1999
+ GraphEvents.CHAT_MODEL_STREAM,
2000
+ {
2001
+ chunk: {
2002
+ content: [toolCallPart],
2003
+ tool_calls: [
2004
+ {
2005
+ type: ToolCallTypes.TOOL_CALL,
2006
+ id: 'call_weather',
2007
+ name: 'weather',
2008
+ args: { city: 'NYC' },
2009
+ },
2010
+ ],
2011
+ } as unknown as t.StreamChunk,
2012
+ },
2013
+ metadata,
2014
+ graph
2015
+ );
2016
+
2017
+ expect(dispatchOrder).toEqual(['server-context', 'client-tool-calls']);
2018
+ });
2019
+
2020
+ it('does not dispatch Gemini server-side tool context blocks for non-Google providers', async () => {
2021
+ const dispatchMessageDelta = jest.fn<StandardGraph['dispatchMessageDelta']>(
2022
+ async () => undefined
2023
+ );
2024
+ const graph = createGraph({
2025
+ dispatchMessageDelta,
2026
+ getAgentContext: jest.fn(
2027
+ (): Partial<AgentContext> => ({
2028
+ provider: Providers.OPENAI,
2029
+ reasoningKey: 'reasoning_content',
2030
+ currentTokenType: ContentTypes.TEXT,
2031
+ toolDefinitions: [],
2032
+ graphTools: [],
2033
+ agentId: 'agent_1',
2034
+ })
2035
+ ) as unknown as StandardGraph['getAgentContext'],
2036
+ });
2037
+ const handler = new ChatModelStreamHandler();
2038
+ const metadata = { langgraph_node: 'agent' };
2039
+ const toolCallPart: t.MessageContentComplex = {
2040
+ type: 'toolCall',
2041
+ toolCall: {
2042
+ id: 'server-search-1',
2043
+ name: 'google_search',
2044
+ args: {},
2045
+ },
2046
+ };
2047
+
2048
+ await handler.handle(
2049
+ GraphEvents.CHAT_MODEL_STREAM,
2050
+ {
2051
+ chunk: {
2052
+ content: [toolCallPart],
2053
+ } as unknown as t.StreamChunk,
2054
+ },
2055
+ metadata,
2056
+ graph
2057
+ );
2058
+
2059
+ expect(dispatchMessageDelta).not.toHaveBeenCalled();
2060
+ });
2061
+
2062
+ it('keeps separately streamed Gemini server-side tool context blocks on separate message steps', async () => {
2063
+ const graph = createGraph({
2064
+ getAgentContext: jest.fn(
2065
+ (): Partial<AgentContext> => ({
2066
+ provider: Providers.GOOGLE,
2067
+ reasoningKey: 'reasoning',
2068
+ currentTokenType: ContentTypes.TEXT,
2069
+ toolDefinitions: [],
2070
+ graphTools: [],
2071
+ agentId: 'agent_1',
2072
+ })
2073
+ ) as unknown as StandardGraph['getAgentContext'],
2074
+ });
2075
+ const { contentParts } = attachContentAggregator(graph);
2076
+ const handler = new ChatModelStreamHandler();
2077
+ const metadata = { langgraph_node: 'agent' };
2078
+ const toolCallPart: t.MessageContentComplex = {
2079
+ type: 'toolCall',
2080
+ toolCall: {
2081
+ id: 'server-search-1',
2082
+ name: 'google_search',
2083
+ args: {},
2084
+ },
2085
+ };
2086
+ const toolResponsePart: t.MessageContentComplex = {
2087
+ type: 'toolResponse',
2088
+ toolResponse: {
2089
+ id: 'server-search-1',
2090
+ name: 'google_search',
2091
+ response: { results: [] },
2092
+ },
2093
+ };
2094
+
2095
+ await handler.handle(
2096
+ GraphEvents.CHAT_MODEL_STREAM,
2097
+ {
2098
+ chunk: {
2099
+ content: [toolCallPart],
2100
+ } as unknown as t.StreamChunk,
2101
+ },
2102
+ metadata,
2103
+ graph
2104
+ );
2105
+ await handler.handle(
2106
+ GraphEvents.CHAT_MODEL_STREAM,
2107
+ {
2108
+ chunk: {
2109
+ content: [toolResponsePart],
2110
+ } as unknown as t.StreamChunk,
2111
+ },
2112
+ metadata,
2113
+ graph
2114
+ );
2115
+ await handler.handle(
2116
+ GraphEvents.CHAT_MODEL_STREAM,
2117
+ {
2118
+ chunk: {
2119
+ content: [
2120
+ {
2121
+ type: ContentTypes.TEXT,
2122
+ text: 'Done',
2123
+ },
2124
+ ],
2125
+ } as unknown as t.StreamChunk,
2126
+ },
2127
+ metadata,
2128
+ graph
2129
+ );
2130
+ await handler.handle(
2131
+ GraphEvents.CHAT_MODEL_STREAM,
2132
+ {
2133
+ chunk: {
2134
+ content: ' again.',
2135
+ } as unknown as t.StreamChunk,
2136
+ },
2137
+ metadata,
2138
+ graph
2139
+ );
2140
+
2141
+ expect(contentParts).toEqual([
2142
+ toolCallPart,
2143
+ toolResponsePart,
2144
+ {
2145
+ type: ContentTypes.TEXT,
2146
+ text: 'Done again.',
2147
+ },
2148
+ ]);
2149
+ });
2150
+
1614
2151
  it('processes a reused chunk object when its streamed payload changes', async () => {
1615
2152
  const graph = createGraph();
1616
2153
  const handler = new ChatModelStreamHandler();
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable no-console */
2
- import { HumanMessage, SystemMessage } from '@langchain/core/messages';
3
2
  import { RunnableLambda } from '@langchain/core/runnables';
3
+ import { HumanMessage, SystemMessage } from '@langchain/core/messages';
4
4
  import type {
5
5
  UsageMetadata,
6
6
  BaseMessage,
@@ -16,11 +16,11 @@ import {
16
16
  Constants,
17
17
  Providers,
18
18
  } from '@/common';
19
- import { createSchemaOnlyTools } from '@/tools/schema';
20
19
  import {
21
20
  addCacheControl,
22
21
  addCacheControlToStablePrefixMessages,
23
22
  } from '@/messages/cache';
23
+ import { createSchemaOnlyTools } from '@/tools/schema';
24
24
  import { DEFAULT_RESERVE_RATIO } from '@/messages';
25
25
  import { toJsonSchema } from '@/utils/schema';
26
26
 
@@ -1,9 +1,9 @@
1
1
  // src/agents/__tests__/AgentContext.test.ts
2
2
  import { AIMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';
3
- import { AgentContext } from '../AgentContext';
4
- import { Constants, Providers } from '@/common';
5
- import { addBedrockCacheControl } from '@/messages/cache';
6
3
  import type * as t from '@/types';
4
+ import { addBedrockCacheControl } from '@/messages/cache';
5
+ import { Constants, Providers } from '@/common';
6
+ import { AgentContext } from '../AgentContext';
7
7
 
8
8
  describe('AgentContext', () => {
9
9
  type TestSystemContentBlock =