@librechat/agents 3.2.32 → 3.2.34

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 (597) hide show
  1. package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
  2. package/dist/cjs/agents/AgentContext.cjs +844 -1046
  3. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  4. package/dist/cjs/common/constants.cjs +13 -13
  5. package/dist/cjs/common/constants.cjs.map +1 -1
  6. package/dist/cjs/common/enum.cjs +233 -240
  7. package/dist/cjs/common/enum.cjs.map +1 -1
  8. package/dist/cjs/common/index.cjs +2 -0
  9. package/dist/cjs/events.cjs +121 -169
  10. package/dist/cjs/events.cjs.map +1 -1
  11. package/dist/cjs/graphs/Graph.cjs +1389 -1807
  12. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  13. package/dist/cjs/graphs/MultiAgentGraph.cjs +713 -945
  14. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  15. package/dist/cjs/graphs/index.cjs +2 -0
  16. package/dist/cjs/hitl/askUserQuestion.cjs +60 -62
  17. package/dist/cjs/hitl/askUserQuestion.cjs.map +1 -1
  18. package/dist/cjs/hitl/index.cjs +1 -0
  19. package/dist/cjs/hooks/HookRegistry.cjs +176 -202
  20. package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
  21. package/dist/cjs/hooks/createToolPolicyHook.cjs +71 -101
  22. package/dist/cjs/hooks/createToolPolicyHook.cjs.map +1 -1
  23. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +170 -273
  24. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs.map +1 -1
  25. package/dist/cjs/hooks/executeHooks.cjs +227 -282
  26. package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
  27. package/dist/cjs/hooks/index.cjs +6 -0
  28. package/dist/cjs/hooks/matchers.cjs +196 -230
  29. package/dist/cjs/hooks/matchers.cjs.map +1 -1
  30. package/dist/cjs/hooks/types.cjs +24 -24
  31. package/dist/cjs/hooks/types.cjs.map +1 -1
  32. package/dist/cjs/instrumentation.cjs +110 -137
  33. package/dist/cjs/instrumentation.cjs.map +1 -1
  34. package/dist/cjs/langchain/google-common.cjs +0 -3
  35. package/dist/cjs/langchain/index.cjs +80 -43
  36. package/dist/cjs/langchain/language_models/chat_models.cjs +0 -3
  37. package/dist/cjs/langchain/messages/tool.cjs +0 -3
  38. package/dist/cjs/langchain/messages.cjs +35 -18
  39. package/dist/cjs/langchain/openai.cjs +0 -3
  40. package/dist/cjs/langchain/prompts.cjs +5 -8
  41. package/dist/cjs/langchain/runnables.cjs +11 -10
  42. package/dist/cjs/langchain/tools.cjs +14 -11
  43. package/dist/cjs/langchain/utils/env.cjs +5 -8
  44. package/dist/cjs/langfuse.cjs +60 -79
  45. package/dist/cjs/langfuse.cjs.map +1 -1
  46. package/dist/cjs/langfuseToolOutputTracing.cjs +267 -399
  47. package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
  48. package/dist/cjs/llm/anthropic/index.cjs +432 -562
  49. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  50. package/dist/cjs/llm/anthropic/types.cjs +23 -47
  51. package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
  52. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +441 -731
  53. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  54. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +171 -256
  55. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
  56. package/dist/cjs/llm/anthropic/utils/output_parsers.cjs +2 -0
  57. package/dist/cjs/llm/anthropic/utils/tools.cjs +12 -26
  58. package/dist/cjs/llm/anthropic/utils/tools.cjs.map +1 -1
  59. package/dist/cjs/llm/bedrock/index.cjs +214 -240
  60. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  61. package/dist/cjs/llm/bedrock/toolCache.cjs +84 -106
  62. package/dist/cjs/llm/bedrock/toolCache.cjs.map +1 -1
  63. package/dist/cjs/llm/bedrock/utils/index.cjs +2 -0
  64. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +357 -620
  65. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  66. package/dist/cjs/llm/bedrock/utils/message_outputs.cjs +141 -149
  67. package/dist/cjs/llm/bedrock/utils/message_outputs.cjs.map +1 -1
  68. package/dist/cjs/llm/fake.cjs +86 -96
  69. package/dist/cjs/llm/fake.cjs.map +1 -1
  70. package/dist/cjs/llm/google/index.cjs +183 -237
  71. package/dist/cjs/llm/google/index.cjs.map +1 -1
  72. package/dist/cjs/llm/google/utils/common.cjs +404 -674
  73. package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
  74. package/dist/cjs/llm/google/utils/zod_to_genai_parameters.cjs +2 -0
  75. package/dist/cjs/llm/init.cjs +44 -53
  76. package/dist/cjs/llm/init.cjs.map +1 -1
  77. package/dist/cjs/llm/invoke.cjs +142 -182
  78. package/dist/cjs/llm/invoke.cjs.map +1 -1
  79. package/dist/cjs/llm/openai/index.cjs +1035 -1273
  80. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  81. package/dist/cjs/llm/openai/utils/index.cjs +189 -316
  82. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  83. package/dist/cjs/llm/openrouter/index.cjs +102 -153
  84. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  85. package/dist/cjs/llm/openrouter/toolCache.cjs +35 -44
  86. package/dist/cjs/llm/openrouter/toolCache.cjs.map +1 -1
  87. package/dist/cjs/llm/providers.cjs +29 -37
  88. package/dist/cjs/llm/providers.cjs.map +1 -1
  89. package/dist/cjs/llm/request.cjs +20 -33
  90. package/dist/cjs/llm/request.cjs.map +1 -1
  91. package/dist/cjs/llm/vertexai/index.cjs +446 -453
  92. package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
  93. package/dist/cjs/main.cjs +547 -528
  94. package/dist/cjs/messages/anthropicToolCache.cjs +68 -119
  95. package/dist/cjs/messages/anthropicToolCache.cjs.map +1 -1
  96. package/dist/cjs/messages/cache.cjs +305 -418
  97. package/dist/cjs/messages/cache.cjs.map +1 -1
  98. package/dist/cjs/messages/content.cjs +36 -49
  99. package/dist/cjs/messages/content.cjs.map +1 -1
  100. package/dist/cjs/messages/contextPruning.cjs +112 -145
  101. package/dist/cjs/messages/contextPruning.cjs.map +1 -1
  102. package/dist/cjs/messages/contextPruningSettings.cjs +36 -46
  103. package/dist/cjs/messages/contextPruningSettings.cjs.map +1 -1
  104. package/dist/cjs/messages/core.cjs +256 -397
  105. package/dist/cjs/messages/core.cjs.map +1 -1
  106. package/dist/cjs/messages/format.cjs +904 -1387
  107. package/dist/cjs/messages/format.cjs.map +1 -1
  108. package/dist/cjs/messages/ids.cjs +16 -20
  109. package/dist/cjs/messages/ids.cjs.map +1 -1
  110. package/dist/cjs/messages/index.cjs +12 -0
  111. package/dist/cjs/messages/langchain.cjs +18 -18
  112. package/dist/cjs/messages/langchain.cjs.map +1 -1
  113. package/dist/cjs/messages/prune.cjs +1054 -1517
  114. package/dist/cjs/messages/prune.cjs.map +1 -1
  115. package/dist/cjs/messages/recency.cjs +77 -95
  116. package/dist/cjs/messages/recency.cjs.map +1 -1
  117. package/dist/cjs/messages/reducer.cjs +63 -78
  118. package/dist/cjs/messages/reducer.cjs.map +1 -1
  119. package/dist/cjs/messages/tools.cjs +51 -79
  120. package/dist/cjs/messages/tools.cjs.map +1 -1
  121. package/dist/cjs/openai/index.cjs +171 -217
  122. package/dist/cjs/openai/index.cjs.map +1 -1
  123. package/dist/cjs/responses/index.cjs +302 -391
  124. package/dist/cjs/responses/index.cjs.map +1 -1
  125. package/dist/cjs/run.cjs +903 -1113
  126. package/dist/cjs/run.cjs.map +1 -1
  127. package/dist/cjs/session/AgentSession.cjs +805 -986
  128. package/dist/cjs/session/AgentSession.cjs.map +1 -1
  129. package/dist/cjs/session/JsonlSessionStore.cjs +327 -410
  130. package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -1
  131. package/dist/cjs/session/handlers.cjs +192 -208
  132. package/dist/cjs/session/handlers.cjs.map +1 -1
  133. package/dist/cjs/session/ids.cjs +9 -10
  134. package/dist/cjs/session/ids.cjs.map +1 -1
  135. package/dist/cjs/session/index.cjs +4 -0
  136. package/dist/cjs/session/messageSerialization.cjs +94 -156
  137. package/dist/cjs/session/messageSerialization.cjs.map +1 -1
  138. package/dist/cjs/splitStream.cjs +147 -206
  139. package/dist/cjs/splitStream.cjs.map +1 -1
  140. package/dist/cjs/stream.cjs +874 -1344
  141. package/dist/cjs/stream.cjs.map +1 -1
  142. package/dist/cjs/summarization/index.cjs +57 -101
  143. package/dist/cjs/summarization/index.cjs.map +1 -1
  144. package/dist/cjs/summarization/node.cjs +643 -796
  145. package/dist/cjs/summarization/node.cjs.map +1 -1
  146. package/dist/cjs/tools/BashExecutor.cjs +110 -136
  147. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  148. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +165 -245
  149. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  150. package/dist/cjs/tools/Calculator.cjs +36 -57
  151. package/dist/cjs/tools/Calculator.cjs.map +1 -1
  152. package/dist/cjs/tools/CodeExecutor.cjs +126 -168
  153. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  154. package/dist/cjs/tools/CodeSessionFileSummary.cjs +36 -46
  155. package/dist/cjs/tools/CodeSessionFileSummary.cjs.map +1 -1
  156. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +459 -649
  157. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  158. package/dist/cjs/tools/ReadFile.cjs +17 -20
  159. package/dist/cjs/tools/ReadFile.cjs.map +1 -1
  160. package/dist/cjs/tools/SkillTool.cjs +26 -27
  161. package/dist/cjs/tools/SkillTool.cjs.map +1 -1
  162. package/dist/cjs/tools/SubagentTool.cjs +59 -61
  163. package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
  164. package/dist/cjs/tools/ToolNode.cjs +2146 -2686
  165. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  166. package/dist/cjs/tools/ToolSearch.cjs +663 -825
  167. package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
  168. package/dist/cjs/tools/cloudflare/CloudflareBridgeRuntime.cjs +248 -340
  169. package/dist/cjs/tools/cloudflare/CloudflareBridgeRuntime.cjs.map +1 -1
  170. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +170 -197
  171. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -1
  172. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs +425 -520
  173. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs.map +1 -1
  174. package/dist/cjs/tools/cloudflare/CloudflareSandboxTools.cjs +91 -124
  175. package/dist/cjs/tools/cloudflare/CloudflareSandboxTools.cjs.map +1 -1
  176. package/dist/cjs/tools/cloudflare/index.cjs +4 -0
  177. package/dist/cjs/tools/eagerEventExecution.cjs +75 -99
  178. package/dist/cjs/tools/eagerEventExecution.cjs.map +1 -1
  179. package/dist/cjs/tools/handlers.cjs +200 -262
  180. package/dist/cjs/tools/handlers.cjs.map +1 -1
  181. package/dist/cjs/tools/local/CompileCheckTool.cjs +150 -212
  182. package/dist/cjs/tools/local/CompileCheckTool.cjs.map +1 -1
  183. package/dist/cjs/tools/local/FileCheckpointer.cjs +77 -85
  184. package/dist/cjs/tools/local/FileCheckpointer.cjs.map +1 -1
  185. package/dist/cjs/tools/local/LocalCodingTools.cjs +763 -1022
  186. package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
  187. package/dist/cjs/tools/local/LocalExecutionEngine.cjs +666 -941
  188. package/dist/cjs/tools/local/LocalExecutionEngine.cjs.map +1 -1
  189. package/dist/cjs/tools/local/LocalExecutionTools.cjs +49 -92
  190. package/dist/cjs/tools/local/LocalExecutionTools.cjs.map +1 -1
  191. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +286 -354
  192. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
  193. package/dist/cjs/tools/local/attachments.cjs +108 -165
  194. package/dist/cjs/tools/local/attachments.cjs.map +1 -1
  195. package/dist/cjs/tools/local/bashAst.cjs +99 -113
  196. package/dist/cjs/tools/local/bashAst.cjs.map +1 -1
  197. package/dist/cjs/tools/local/editStrategies.cjs +126 -169
  198. package/dist/cjs/tools/local/editStrategies.cjs.map +1 -1
  199. package/dist/cjs/tools/local/index.cjs +12 -0
  200. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +136 -218
  201. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -1
  202. package/dist/cjs/tools/local/syntaxCheck.cjs +142 -161
  203. package/dist/cjs/tools/local/syntaxCheck.cjs.map +1 -1
  204. package/dist/cjs/tools/local/textEncoding.cjs +25 -23
  205. package/dist/cjs/tools/local/textEncoding.cjs.map +1 -1
  206. package/dist/cjs/tools/local/workspaceFS.cjs +38 -46
  207. package/dist/cjs/tools/local/workspaceFS.cjs.map +1 -1
  208. package/dist/cjs/tools/ptcTimeout.cjs +27 -47
  209. package/dist/cjs/tools/ptcTimeout.cjs.map +1 -1
  210. package/dist/cjs/tools/schema.cjs +24 -23
  211. package/dist/cjs/tools/schema.cjs.map +1 -1
  212. package/dist/cjs/tools/search/anthropic.cjs +24 -33
  213. package/dist/cjs/tools/search/anthropic.cjs.map +1 -1
  214. package/dist/cjs/tools/search/content.cjs +95 -137
  215. package/dist/cjs/tools/search/content.cjs.map +1 -1
  216. package/dist/cjs/tools/search/firecrawl.cjs +141 -172
  217. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
  218. package/dist/cjs/tools/search/format.cjs +128 -196
  219. package/dist/cjs/tools/search/format.cjs.map +1 -1
  220. package/dist/cjs/tools/search/highlights.cjs +165 -232
  221. package/dist/cjs/tools/search/highlights.cjs.map +1 -1
  222. package/dist/cjs/tools/search/index.cjs +2 -0
  223. package/dist/cjs/tools/search/rerankers.cjs +151 -174
  224. package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
  225. package/dist/cjs/tools/search/schema.cjs +40 -39
  226. package/dist/cjs/tools/search/schema.cjs.map +1 -1
  227. package/dist/cjs/tools/search/search.cjs +428 -530
  228. package/dist/cjs/tools/search/search.cjs.map +1 -1
  229. package/dist/cjs/tools/search/serper-scraper.cjs +106 -127
  230. package/dist/cjs/tools/search/serper-scraper.cjs.map +1 -1
  231. package/dist/cjs/tools/search/tavily-scraper.cjs +129 -181
  232. package/dist/cjs/tools/search/tavily-scraper.cjs.map +1 -1
  233. package/dist/cjs/tools/search/tavily-search.cjs +295 -359
  234. package/dist/cjs/tools/search/tavily-search.cjs.map +1 -1
  235. package/dist/cjs/tools/search/tool.cjs +260 -299
  236. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  237. package/dist/cjs/tools/search/utils.cjs +74 -117
  238. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  239. package/dist/cjs/tools/skillCatalog.cjs +54 -72
  240. package/dist/cjs/tools/skillCatalog.cjs.map +1 -1
  241. package/dist/cjs/tools/streamedToolCallSeals.cjs +46 -34
  242. package/dist/cjs/tools/streamedToolCallSeals.cjs.map +1 -1
  243. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +612 -771
  244. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  245. package/dist/cjs/tools/subagent/index.cjs +1 -0
  246. package/dist/cjs/tools/toolOutputReferences.cjs +523 -630
  247. package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
  248. package/dist/cjs/utils/callbacks.cjs +11 -21
  249. package/dist/cjs/utils/callbacks.cjs.map +1 -1
  250. package/dist/cjs/utils/errors.cjs +70 -95
  251. package/dist/cjs/utils/errors.cjs.map +1 -1
  252. package/dist/cjs/utils/events.cjs +32 -42
  253. package/dist/cjs/utils/events.cjs.map +1 -1
  254. package/dist/cjs/utils/graph.cjs +8 -12
  255. package/dist/cjs/utils/graph.cjs.map +1 -1
  256. package/dist/cjs/utils/handlers.cjs +60 -82
  257. package/dist/cjs/utils/handlers.cjs.map +1 -1
  258. package/dist/cjs/utils/index.cjs +9 -0
  259. package/dist/cjs/utils/llm.cjs +19 -27
  260. package/dist/cjs/utils/llm.cjs.map +1 -1
  261. package/dist/cjs/utils/misc.cjs +30 -46
  262. package/dist/cjs/utils/misc.cjs.map +1 -1
  263. package/dist/cjs/utils/run.cjs +50 -66
  264. package/dist/cjs/utils/run.cjs.map +1 -1
  265. package/dist/cjs/utils/schema.cjs +11 -19
  266. package/dist/cjs/utils/schema.cjs.map +1 -1
  267. package/dist/cjs/utils/title.cjs +71 -106
  268. package/dist/cjs/utils/title.cjs.map +1 -1
  269. package/dist/cjs/utils/tokens.cjs +186 -283
  270. package/dist/cjs/utils/tokens.cjs.map +1 -1
  271. package/dist/cjs/utils/truncation.cjs +95 -114
  272. package/dist/cjs/utils/truncation.cjs.map +1 -1
  273. package/dist/esm/agents/AgentContext.mjs +844 -1044
  274. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  275. package/dist/esm/common/constants.mjs +13 -11
  276. package/dist/esm/common/constants.mjs.map +1 -1
  277. package/dist/esm/common/enum.mjs +221 -238
  278. package/dist/esm/common/enum.mjs.map +1 -1
  279. package/dist/esm/common/index.mjs +3 -0
  280. package/dist/esm/events.mjs +121 -167
  281. package/dist/esm/events.mjs.map +1 -1
  282. package/dist/esm/graphs/Graph.mjs +1388 -1804
  283. package/dist/esm/graphs/Graph.mjs.map +1 -1
  284. package/dist/esm/graphs/MultiAgentGraph.mjs +713 -943
  285. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  286. package/dist/esm/graphs/index.mjs +3 -0
  287. package/dist/esm/hitl/askUserQuestion.mjs +60 -60
  288. package/dist/esm/hitl/askUserQuestion.mjs.map +1 -1
  289. package/dist/esm/hitl/index.mjs +2 -0
  290. package/dist/esm/hooks/HookRegistry.mjs +176 -200
  291. package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
  292. package/dist/esm/hooks/createToolPolicyHook.mjs +71 -99
  293. package/dist/esm/hooks/createToolPolicyHook.mjs.map +1 -1
  294. package/dist/esm/hooks/createWorkspacePolicyHook.mjs +170 -271
  295. package/dist/esm/hooks/createWorkspacePolicyHook.mjs.map +1 -1
  296. package/dist/esm/hooks/executeHooks.mjs +227 -280
  297. package/dist/esm/hooks/executeHooks.mjs.map +1 -1
  298. package/dist/esm/hooks/index.mjs +7 -0
  299. package/dist/esm/hooks/matchers.mjs +196 -228
  300. package/dist/esm/hooks/matchers.mjs.map +1 -1
  301. package/dist/esm/hooks/types.mjs +24 -22
  302. package/dist/esm/hooks/types.mjs.map +1 -1
  303. package/dist/esm/instrumentation.mjs +109 -132
  304. package/dist/esm/instrumentation.mjs.map +1 -1
  305. package/dist/esm/langchain/google-common.mjs +1 -2
  306. package/dist/esm/langchain/index.mjs +5 -5
  307. package/dist/esm/langchain/language_models/chat_models.mjs +1 -2
  308. package/dist/esm/langchain/messages/tool.mjs +1 -2
  309. package/dist/esm/langchain/messages.mjs +2 -2
  310. package/dist/esm/langchain/openai.mjs +1 -2
  311. package/dist/esm/langchain/prompts.mjs +2 -2
  312. package/dist/esm/langchain/runnables.mjs +2 -2
  313. package/dist/esm/langchain/tools.mjs +2 -2
  314. package/dist/esm/langchain/utils/env.mjs +2 -2
  315. package/dist/esm/langfuse.mjs +60 -76
  316. package/dist/esm/langfuse.mjs.map +1 -1
  317. package/dist/esm/langfuseToolOutputTracing.mjs +267 -395
  318. package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
  319. package/dist/esm/llm/anthropic/index.mjs +432 -559
  320. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  321. package/dist/esm/llm/anthropic/types.mjs +23 -45
  322. package/dist/esm/llm/anthropic/types.mjs.map +1 -1
  323. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +439 -725
  324. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  325. package/dist/esm/llm/anthropic/utils/message_outputs.mjs +171 -253
  326. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  327. package/dist/esm/llm/anthropic/utils/output_parsers.mjs +3 -0
  328. package/dist/esm/llm/anthropic/utils/tools.mjs +12 -24
  329. package/dist/esm/llm/anthropic/utils/tools.mjs.map +1 -1
  330. package/dist/esm/llm/bedrock/index.mjs +214 -238
  331. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  332. package/dist/esm/llm/bedrock/toolCache.mjs +84 -104
  333. package/dist/esm/llm/bedrock/toolCache.mjs.map +1 -1
  334. package/dist/esm/llm/bedrock/utils/index.mjs +3 -0
  335. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +357 -618
  336. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  337. package/dist/esm/llm/bedrock/utils/message_outputs.mjs +140 -147
  338. package/dist/esm/llm/bedrock/utils/message_outputs.mjs.map +1 -1
  339. package/dist/esm/llm/fake.mjs +86 -94
  340. package/dist/esm/llm/fake.mjs.map +1 -1
  341. package/dist/esm/llm/google/index.mjs +183 -235
  342. package/dist/esm/llm/google/index.mjs.map +1 -1
  343. package/dist/esm/llm/google/utils/common.mjs +403 -666
  344. package/dist/esm/llm/google/utils/common.mjs.map +1 -1
  345. package/dist/esm/llm/google/utils/zod_to_genai_parameters.mjs +3 -0
  346. package/dist/esm/llm/init.mjs +44 -51
  347. package/dist/esm/llm/init.mjs.map +1 -1
  348. package/dist/esm/llm/invoke.mjs +142 -180
  349. package/dist/esm/llm/invoke.mjs.map +1 -1
  350. package/dist/esm/llm/openai/index.mjs +1035 -1268
  351. package/dist/esm/llm/openai/index.mjs.map +1 -1
  352. package/dist/esm/llm/openai/utils/index.mjs +188 -312
  353. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  354. package/dist/esm/llm/openrouter/index.mjs +102 -151
  355. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  356. package/dist/esm/llm/openrouter/toolCache.mjs +35 -42
  357. package/dist/esm/llm/openrouter/toolCache.mjs.map +1 -1
  358. package/dist/esm/llm/providers.mjs +29 -34
  359. package/dist/esm/llm/providers.mjs.map +1 -1
  360. package/dist/esm/llm/request.mjs +20 -31
  361. package/dist/esm/llm/request.mjs.map +1 -1
  362. package/dist/esm/llm/vertexai/index.mjs +446 -449
  363. package/dist/esm/llm/vertexai/index.mjs.map +1 -1
  364. package/dist/esm/main.mjs +99 -87
  365. package/dist/esm/messages/anthropicToolCache.mjs +68 -117
  366. package/dist/esm/messages/anthropicToolCache.mjs.map +1 -1
  367. package/dist/esm/messages/cache.mjs +305 -416
  368. package/dist/esm/messages/cache.mjs.map +1 -1
  369. package/dist/esm/messages/content.mjs +36 -47
  370. package/dist/esm/messages/content.mjs.map +1 -1
  371. package/dist/esm/messages/contextPruning.mjs +112 -143
  372. package/dist/esm/messages/contextPruning.mjs.map +1 -1
  373. package/dist/esm/messages/contextPruningSettings.mjs +36 -44
  374. package/dist/esm/messages/contextPruningSettings.mjs.map +1 -1
  375. package/dist/esm/messages/core.mjs +254 -393
  376. package/dist/esm/messages/core.mjs.map +1 -1
  377. package/dist/esm/messages/format.mjs +902 -1383
  378. package/dist/esm/messages/format.mjs.map +1 -1
  379. package/dist/esm/messages/ids.mjs +16 -18
  380. package/dist/esm/messages/ids.mjs.map +1 -1
  381. package/dist/esm/messages/index.mjs +13 -0
  382. package/dist/esm/messages/langchain.mjs +18 -16
  383. package/dist/esm/messages/langchain.mjs.map +1 -1
  384. package/dist/esm/messages/prune.mjs +1053 -1514
  385. package/dist/esm/messages/prune.mjs.map +1 -1
  386. package/dist/esm/messages/recency.mjs +77 -93
  387. package/dist/esm/messages/recency.mjs.map +1 -1
  388. package/dist/esm/messages/reducer.mjs +63 -76
  389. package/dist/esm/messages/reducer.mjs.map +1 -1
  390. package/dist/esm/messages/tools.mjs +49 -75
  391. package/dist/esm/messages/tools.mjs.map +1 -1
  392. package/dist/esm/openai/index.mjs +170 -215
  393. package/dist/esm/openai/index.mjs.map +1 -1
  394. package/dist/esm/responses/index.mjs +301 -389
  395. package/dist/esm/responses/index.mjs.map +1 -1
  396. package/dist/esm/run.mjs +903 -1111
  397. package/dist/esm/run.mjs.map +1 -1
  398. package/dist/esm/session/AgentSession.mjs +806 -985
  399. package/dist/esm/session/AgentSession.mjs.map +1 -1
  400. package/dist/esm/session/JsonlSessionStore.mjs +326 -407
  401. package/dist/esm/session/JsonlSessionStore.mjs.map +1 -1
  402. package/dist/esm/session/handlers.mjs +192 -206
  403. package/dist/esm/session/handlers.mjs.map +1 -1
  404. package/dist/esm/session/ids.mjs +9 -8
  405. package/dist/esm/session/ids.mjs.map +1 -1
  406. package/dist/esm/session/index.mjs +5 -0
  407. package/dist/esm/session/messageSerialization.mjs +94 -154
  408. package/dist/esm/session/messageSerialization.mjs.map +1 -1
  409. package/dist/esm/splitStream.mjs +147 -204
  410. package/dist/esm/splitStream.mjs.map +1 -1
  411. package/dist/esm/stream.mjs +872 -1341
  412. package/dist/esm/stream.mjs.map +1 -1
  413. package/dist/esm/summarization/index.mjs +57 -99
  414. package/dist/esm/summarization/index.mjs.map +1 -1
  415. package/dist/esm/summarization/node.mjs +640 -790
  416. package/dist/esm/summarization/node.mjs.map +1 -1
  417. package/dist/esm/tools/BashExecutor.mjs +103 -129
  418. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  419. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +162 -239
  420. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  421. package/dist/esm/tools/Calculator.mjs +34 -36
  422. package/dist/esm/tools/Calculator.mjs.map +1 -1
  423. package/dist/esm/tools/CodeExecutor.mjs +123 -164
  424. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  425. package/dist/esm/tools/CodeSessionFileSummary.mjs +36 -44
  426. package/dist/esm/tools/CodeSessionFileSummary.mjs.map +1 -1
  427. package/dist/esm/tools/ProgrammaticToolCalling.mjs +454 -644
  428. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  429. package/dist/esm/tools/ReadFile.mjs +17 -18
  430. package/dist/esm/tools/ReadFile.mjs.map +1 -1
  431. package/dist/esm/tools/SkillTool.mjs +26 -25
  432. package/dist/esm/tools/SkillTool.mjs.map +1 -1
  433. package/dist/esm/tools/SubagentTool.mjs +59 -59
  434. package/dist/esm/tools/SubagentTool.mjs.map +1 -1
  435. package/dist/esm/tools/ToolNode.mjs +2144 -2684
  436. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  437. package/dist/esm/tools/ToolSearch.mjs +659 -804
  438. package/dist/esm/tools/ToolSearch.mjs.map +1 -1
  439. package/dist/esm/tools/cloudflare/CloudflareBridgeRuntime.mjs +248 -338
  440. package/dist/esm/tools/cloudflare/CloudflareBridgeRuntime.mjs.map +1 -1
  441. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +170 -195
  442. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -1
  443. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs +424 -517
  444. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs.map +1 -1
  445. package/dist/esm/tools/cloudflare/CloudflareSandboxTools.mjs +91 -122
  446. package/dist/esm/tools/cloudflare/CloudflareSandboxTools.mjs.map +1 -1
  447. package/dist/esm/tools/cloudflare/index.mjs +5 -0
  448. package/dist/esm/tools/eagerEventExecution.mjs +75 -96
  449. package/dist/esm/tools/eagerEventExecution.mjs.map +1 -1
  450. package/dist/esm/tools/handlers.mjs +200 -260
  451. package/dist/esm/tools/handlers.mjs.map +1 -1
  452. package/dist/esm/tools/local/CompileCheckTool.mjs +150 -210
  453. package/dist/esm/tools/local/CompileCheckTool.mjs.map +1 -1
  454. package/dist/esm/tools/local/FileCheckpointer.mjs +77 -83
  455. package/dist/esm/tools/local/FileCheckpointer.mjs.map +1 -1
  456. package/dist/esm/tools/local/LocalCodingTools.mjs +760 -1017
  457. package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
  458. package/dist/esm/tools/local/LocalExecutionEngine.mjs +663 -936
  459. package/dist/esm/tools/local/LocalExecutionEngine.mjs.map +1 -1
  460. package/dist/esm/tools/local/LocalExecutionTools.mjs +49 -90
  461. package/dist/esm/tools/local/LocalExecutionTools.mjs.map +1 -1
  462. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +283 -349
  463. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
  464. package/dist/esm/tools/local/attachments.mjs +108 -163
  465. package/dist/esm/tools/local/attachments.mjs.map +1 -1
  466. package/dist/esm/tools/local/bashAst.mjs +99 -111
  467. package/dist/esm/tools/local/bashAst.mjs.map +1 -1
  468. package/dist/esm/tools/local/editStrategies.mjs +126 -167
  469. package/dist/esm/tools/local/editStrategies.mjs.map +1 -1
  470. package/dist/esm/tools/local/index.mjs +13 -0
  471. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +136 -216
  472. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -1
  473. package/dist/esm/tools/local/syntaxCheck.mjs +138 -155
  474. package/dist/esm/tools/local/syntaxCheck.mjs.map +1 -1
  475. package/dist/esm/tools/local/textEncoding.mjs +25 -21
  476. package/dist/esm/tools/local/textEncoding.mjs.map +1 -1
  477. package/dist/esm/tools/local/workspaceFS.mjs +38 -44
  478. package/dist/esm/tools/local/workspaceFS.mjs.map +1 -1
  479. package/dist/esm/tools/ptcTimeout.mjs +27 -42
  480. package/dist/esm/tools/ptcTimeout.mjs.map +1 -1
  481. package/dist/esm/tools/schema.mjs +24 -21
  482. package/dist/esm/tools/schema.mjs.map +1 -1
  483. package/dist/esm/tools/search/anthropic.mjs +24 -31
  484. package/dist/esm/tools/search/anthropic.mjs.map +1 -1
  485. package/dist/esm/tools/search/content.mjs +93 -116
  486. package/dist/esm/tools/search/content.mjs.map +1 -1
  487. package/dist/esm/tools/search/firecrawl.mjs +139 -169
  488. package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
  489. package/dist/esm/tools/search/format.mjs +128 -194
  490. package/dist/esm/tools/search/format.mjs.map +1 -1
  491. package/dist/esm/tools/search/highlights.mjs +165 -230
  492. package/dist/esm/tools/search/highlights.mjs.map +1 -1
  493. package/dist/esm/tools/search/index.mjs +3 -0
  494. package/dist/esm/tools/search/rerankers.mjs +149 -168
  495. package/dist/esm/tools/search/rerankers.mjs.map +1 -1
  496. package/dist/esm/tools/search/schema.mjs +39 -37
  497. package/dist/esm/tools/search/schema.mjs.map +1 -1
  498. package/dist/esm/tools/search/search.mjs +426 -528
  499. package/dist/esm/tools/search/search.mjs.map +1 -1
  500. package/dist/esm/tools/search/serper-scraper.mjs +104 -124
  501. package/dist/esm/tools/search/serper-scraper.mjs.map +1 -1
  502. package/dist/esm/tools/search/tavily-scraper.mjs +127 -178
  503. package/dist/esm/tools/search/tavily-scraper.mjs.map +1 -1
  504. package/dist/esm/tools/search/tavily-search.mjs +293 -357
  505. package/dist/esm/tools/search/tavily-search.mjs.map +1 -1
  506. package/dist/esm/tools/search/tool.mjs +259 -297
  507. package/dist/esm/tools/search/tool.mjs.map +1 -1
  508. package/dist/esm/tools/search/utils.mjs +74 -115
  509. package/dist/esm/tools/search/utils.mjs.map +1 -1
  510. package/dist/esm/tools/skillCatalog.mjs +54 -70
  511. package/dist/esm/tools/skillCatalog.mjs.map +1 -1
  512. package/dist/esm/tools/streamedToolCallSeals.mjs +42 -31
  513. package/dist/esm/tools/streamedToolCallSeals.mjs.map +1 -1
  514. package/dist/esm/tools/subagent/SubagentExecutor.mjs +612 -768
  515. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  516. package/dist/esm/tools/subagent/index.mjs +2 -0
  517. package/dist/esm/tools/toolOutputReferences.mjs +523 -624
  518. package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
  519. package/dist/esm/utils/callbacks.mjs +11 -19
  520. package/dist/esm/utils/callbacks.mjs.map +1 -1
  521. package/dist/esm/utils/errors.mjs +70 -93
  522. package/dist/esm/utils/errors.mjs.map +1 -1
  523. package/dist/esm/utils/events.mjs +32 -40
  524. package/dist/esm/utils/events.mjs.map +1 -1
  525. package/dist/esm/utils/graph.mjs +8 -10
  526. package/dist/esm/utils/graph.mjs.map +1 -1
  527. package/dist/esm/utils/handlers.mjs +60 -80
  528. package/dist/esm/utils/handlers.mjs.map +1 -1
  529. package/dist/esm/utils/index.mjs +10 -0
  530. package/dist/esm/utils/llm.mjs +19 -25
  531. package/dist/esm/utils/llm.mjs.map +1 -1
  532. package/dist/esm/utils/misc.mjs +30 -44
  533. package/dist/esm/utils/misc.mjs.map +1 -1
  534. package/dist/esm/utils/run.mjs +50 -64
  535. package/dist/esm/utils/run.mjs.map +1 -1
  536. package/dist/esm/utils/schema.mjs +11 -17
  537. package/dist/esm/utils/schema.mjs.map +1 -1
  538. package/dist/esm/utils/title.mjs +71 -104
  539. package/dist/esm/utils/title.mjs.map +1 -1
  540. package/dist/esm/utils/tokens.mjs +186 -281
  541. package/dist/esm/utils/tokens.mjs.map +1 -1
  542. package/dist/esm/utils/truncation.mjs +95 -112
  543. package/dist/esm/utils/truncation.mjs.map +1 -1
  544. package/dist/types/llm/bedrock/utils/index.d.ts +1 -1
  545. package/dist/types/llm/bedrock/utils/message_outputs.d.ts +9 -0
  546. package/dist/types/llm/vertexai/index.d.ts +10 -0
  547. package/dist/types/tools/ToolNode.d.ts +8 -0
  548. package/dist/types/tools/search/tool.d.ts +17 -0
  549. package/dist/types/tools/search/types.d.ts +4 -0
  550. package/dist/types/tools/streamedToolCallSeals.d.ts +5 -1
  551. package/dist/types/types/tools.d.ts +10 -0
  552. package/package.json +4 -10
  553. package/src/__tests__/stream.eagerEventExecution.test.ts +703 -0
  554. package/src/llm/bedrock/index.ts +40 -0
  555. package/src/llm/bedrock/streamSealDispatch.test.ts +158 -0
  556. package/src/llm/bedrock/utils/index.ts +1 -0
  557. package/src/llm/bedrock/utils/message_outputs.test.ts +85 -0
  558. package/src/llm/bedrock/utils/message_outputs.ts +43 -0
  559. package/src/llm/google/utils/common.test.ts +64 -0
  560. package/src/llm/google/utils/common.ts +18 -0
  561. package/src/llm/openai/index.ts +95 -1
  562. package/src/llm/openai/sequentialToolCallSeals.test.ts +199 -0
  563. package/src/llm/vertexai/index.ts +31 -0
  564. package/src/llm/vertexai/sealStreamedToolCalls.test.ts +88 -0
  565. package/src/llm/vertexai/streamSealDispatch.test.ts +148 -0
  566. package/src/stream.ts +40 -6
  567. package/src/tools/ToolNode.ts +85 -3
  568. package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +368 -0
  569. package/src/tools/search/highlights.ts +9 -1
  570. package/src/tools/search/search.ts +41 -3
  571. package/src/tools/search/source-processing.test.ts +373 -0
  572. package/src/tools/search/tool.ts +22 -2
  573. package/src/tools/search/types.ts +4 -0
  574. package/src/tools/streamedToolCallSeals.ts +37 -9
  575. package/src/types/tools.ts +10 -0
  576. package/dist/cjs/langchain/google-common.cjs.map +0 -1
  577. package/dist/cjs/langchain/index.cjs.map +0 -1
  578. package/dist/cjs/langchain/language_models/chat_models.cjs.map +0 -1
  579. package/dist/cjs/langchain/messages/tool.cjs.map +0 -1
  580. package/dist/cjs/langchain/messages.cjs.map +0 -1
  581. package/dist/cjs/langchain/openai.cjs.map +0 -1
  582. package/dist/cjs/langchain/prompts.cjs.map +0 -1
  583. package/dist/cjs/langchain/runnables.cjs.map +0 -1
  584. package/dist/cjs/langchain/tools.cjs.map +0 -1
  585. package/dist/cjs/langchain/utils/env.cjs.map +0 -1
  586. package/dist/cjs/main.cjs.map +0 -1
  587. package/dist/esm/langchain/google-common.mjs.map +0 -1
  588. package/dist/esm/langchain/index.mjs.map +0 -1
  589. package/dist/esm/langchain/language_models/chat_models.mjs.map +0 -1
  590. package/dist/esm/langchain/messages/tool.mjs.map +0 -1
  591. package/dist/esm/langchain/messages.mjs.map +0 -1
  592. package/dist/esm/langchain/openai.mjs.map +0 -1
  593. package/dist/esm/langchain/prompts.mjs.map +0 -1
  594. package/dist/esm/langchain/runnables.mjs.map +0 -1
  595. package/dist/esm/langchain/tools.mjs.map +0 -1
  596. package/dist/esm/langchain/utils/env.mjs.map +0 -1
  597. package/dist/esm/main.mjs.map +0 -1
@@ -0,0 +1,199 @@
1
+ import { AIMessageChunk } from '@langchain/core/messages';
2
+ import { expect, test, describe, beforeEach, afterAll } from '@jest/globals';
3
+ import type { BaseMessageChunk } from '@langchain/core/messages';
4
+ import {
5
+ STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY,
6
+ OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER,
7
+ } from '@/tools/streamedToolCallSeals';
8
+ import { ChatOpenAI, AzureChatOpenAI } from './index';
9
+
10
+ type DeltaConverter = {
11
+ _convertCompletionsDeltaToBaseMessageChunk(
12
+ delta: Record<string, unknown>,
13
+ rawResponse: Record<string, unknown>
14
+ ): BaseMessageChunk;
15
+ };
16
+
17
+ const rawResponse = {
18
+ id: 'chatcmpl-1',
19
+ object: 'chat.completion.chunk',
20
+ created: 1,
21
+ model: 'gpt-5.5',
22
+ choices: [],
23
+ };
24
+
25
+ const toolCallDelta = {
26
+ role: 'assistant',
27
+ tool_calls: [
28
+ {
29
+ index: 0,
30
+ id: 'call_1',
31
+ type: 'function',
32
+ function: { name: 'weather', arguments: '{"ci' },
33
+ },
34
+ ],
35
+ };
36
+
37
+ function convertDelta(
38
+ model: unknown,
39
+ delta: Record<string, unknown>
40
+ ): AIMessageChunk {
41
+ const converter = (model as { completions: DeltaConverter }).completions;
42
+ const message = converter._convertCompletionsDeltaToBaseMessageChunk(
43
+ delta,
44
+ rawResponse
45
+ );
46
+ expect(message).toBeInstanceOf(AIMessageChunk);
47
+ return message as AIMessageChunk;
48
+ }
49
+
50
+ function adapterOf(message: AIMessageChunk): unknown {
51
+ return (message.response_metadata as Record<string, unknown>)[
52
+ STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY
53
+ ];
54
+ }
55
+
56
+ describe('Chat Completions sequential tool-call seal stamping', () => {
57
+ // Both the implementation (OPENAI_BASE_URL fallback) and the Azure
58
+ // constructor (AZURE_OPENAI_BASE_PATH fallback) read the environment, so
59
+ // isolate these vars to keep the suite deterministic across shells.
60
+ const ISOLATED_ENV_VARS = ['OPENAI_BASE_URL', 'AZURE_OPENAI_BASE_PATH'];
61
+ const originalEnv = new Map(
62
+ ISOLATED_ENV_VARS.map((name) => [name, process.env[name]])
63
+ );
64
+
65
+ beforeEach(() => {
66
+ for (const name of ISOLATED_ENV_VARS) {
67
+ delete process.env[name];
68
+ }
69
+ });
70
+
71
+ afterAll(() => {
72
+ for (const [name, value] of originalEnv) {
73
+ if (value == null) {
74
+ delete process.env[name];
75
+ } else {
76
+ process.env[name] = value;
77
+ }
78
+ }
79
+ });
80
+
81
+ test('stamps tool-call deltas when no baseURL is configured (official)', () => {
82
+ const model = new ChatOpenAI({ model: 'gpt-5.5', apiKey: 'test' });
83
+ const message = convertDelta(model, toolCallDelta);
84
+ expect(adapterOf(message)).toBe(
85
+ OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER
86
+ );
87
+ });
88
+
89
+ test('stamps tool-call deltas for an explicit api.openai.com baseURL', () => {
90
+ const model = new ChatOpenAI({
91
+ model: 'gpt-5.5',
92
+ apiKey: 'test',
93
+ configuration: { baseURL: 'https://api.openai.com/v1' },
94
+ });
95
+ const message = convertDelta(model, toolCallDelta);
96
+ expect(adapterOf(message)).toBe(
97
+ OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER
98
+ );
99
+ });
100
+
101
+ test('does not stamp tool-call deltas for OpenAI-compatible endpoints', () => {
102
+ const model = new ChatOpenAI({
103
+ model: 'kimi-k2',
104
+ apiKey: 'test',
105
+ configuration: { baseURL: 'https://api.moonshot.ai/v1' },
106
+ });
107
+ const message = convertDelta(model, toolCallDelta);
108
+ expect(adapterOf(message)).toBeUndefined();
109
+ });
110
+
111
+ test('does not stamp text-only deltas', () => {
112
+ const model = new ChatOpenAI({ model: 'gpt-5.5', apiKey: 'test' });
113
+ const message = convertDelta(model, {
114
+ role: 'assistant',
115
+ content: 'hello',
116
+ });
117
+ expect(adapterOf(message)).toBeUndefined();
118
+ });
119
+
120
+ test('does not stamp when OPENAI_BASE_URL routes to a compatible endpoint', () => {
121
+ process.env.OPENAI_BASE_URL = 'https://api.moonshot.ai/v1';
122
+ const model = new ChatOpenAI({ model: 'gpt-5.5', apiKey: 'test' });
123
+ const message = convertDelta(model, toolCallDelta);
124
+ expect(adapterOf(message)).toBeUndefined();
125
+ });
126
+
127
+ test('stamps when OPENAI_BASE_URL points at api.openai.com', () => {
128
+ process.env.OPENAI_BASE_URL = 'https://api.openai.com/v1';
129
+ const model = new ChatOpenAI({ model: 'gpt-5.5', apiKey: 'test' });
130
+ const message = convertDelta(model, toolCallDelta);
131
+ expect(adapterOf(message)).toBe(
132
+ OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER
133
+ );
134
+ });
135
+
136
+ test('stamps Azure OpenAI tool-call deltas (first-party endpoint)', () => {
137
+ const model = new AzureChatOpenAI({
138
+ azureOpenAIApiKey: 'test',
139
+ azureOpenAIApiInstanceName: 'test-instance',
140
+ azureOpenAIApiDeploymentName: 'test-deployment',
141
+ azureOpenAIApiVersion: '2024-08-01-preview',
142
+ });
143
+ const message = convertDelta(model, toolCallDelta);
144
+ expect(adapterOf(message)).toBe(
145
+ OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER
146
+ );
147
+ });
148
+
149
+ test('stamps Azure deltas for an *.openai.azure.com base path', () => {
150
+ const model = new AzureChatOpenAI({
151
+ azureOpenAIApiKey: 'test',
152
+ azureOpenAIApiDeploymentName: 'test-deployment',
153
+ azureOpenAIApiVersion: '2024-08-01-preview',
154
+ azureOpenAIBasePath:
155
+ 'https://test-resource.openai.azure.com/openai/deployments',
156
+ });
157
+ const message = convertDelta(model, toolCallDelta);
158
+ expect(adapterOf(message)).toBe(
159
+ OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER
160
+ );
161
+ });
162
+
163
+ test('stamps Azure deltas for a regional cognitive services base path', () => {
164
+ const model = new AzureChatOpenAI({
165
+ azureOpenAIApiKey: 'test',
166
+ azureOpenAIApiDeploymentName: 'test-deployment',
167
+ azureOpenAIApiVersion: '2024-08-01-preview',
168
+ azureOpenAIBasePath:
169
+ 'https://westeurope.api.cognitive.microsoft.com/openai/deployments',
170
+ });
171
+ const message = convertDelta(model, toolCallDelta);
172
+ expect(adapterOf(message)).toBe(
173
+ OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER
174
+ );
175
+ });
176
+
177
+ test('does not stamp Azure deltas routed through a proxy base path', () => {
178
+ const model = new AzureChatOpenAI({
179
+ azureOpenAIApiKey: 'test',
180
+ azureOpenAIApiDeploymentName: 'test-deployment',
181
+ azureOpenAIApiVersion: '2024-08-01-preview',
182
+ azureOpenAIBasePath: 'https://proxy.example.com/openai/deployments',
183
+ });
184
+ const message = convertDelta(model, toolCallDelta);
185
+ expect(adapterOf(message)).toBeUndefined();
186
+ });
187
+
188
+ test('does not stamp Azure deltas with a custom client baseURL', () => {
189
+ const model = new AzureChatOpenAI({
190
+ azureOpenAIApiKey: 'test',
191
+ azureOpenAIApiInstanceName: 'test-instance',
192
+ azureOpenAIApiDeploymentName: 'test-deployment',
193
+ azureOpenAIApiVersion: '2024-08-01-preview',
194
+ configuration: { baseURL: 'https://gateway.example.com/azure' },
195
+ } as unknown as ConstructorParameters<typeof AzureChatOpenAI>[0]);
196
+ const message = convertDelta(model, toolCallDelta);
197
+ expect(adapterOf(message)).toBeUndefined();
198
+ });
199
+ });
@@ -11,6 +11,11 @@ import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager
11
11
  import type { BaseMessage, UsageMetadata } from '@langchain/core/messages';
12
12
  import type { ChatGenerationChunk } from '@langchain/core/outputs';
13
13
  import type { GoogleThinkingConfig, VertexAIClientOptions } from '@/types';
14
+ import {
15
+ STREAMED_TOOL_CALL_SEAL_METADATA_KEY,
16
+ STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY,
17
+ GOOGLE_STREAMED_TOOL_CALL_ADAPTER,
18
+ } from '@/tools/streamedToolCallSeals';
14
19
 
15
20
  /**
16
21
  * `@langchain/google-common`'s `_streamResponseChunks` emits usage on TWO
@@ -48,6 +53,31 @@ export function repairStreamUsageMetadata(
48
53
  return generationInfoUsage;
49
54
  }
50
55
 
56
+ /**
57
+ * The Gemini API delivers function calls as complete objects — never as
58
+ * partial arg deltas. `@langchain/google-common` pre-parses each streamed
59
+ * functionCall part into `tool_calls` (invalid args land in
60
+ * `invalid_tool_calls` instead), so a chunk whose tool-call chunks all parsed
61
+ * cleanly is sealed on arrival for eager tool execution. Anything that fails
62
+ * the parse check is left unstamped and falls back to the lazy path.
63
+ */
64
+ export function sealCompleteStreamedToolCalls(message: AIMessageChunk): void {
65
+ const chunkCount = message.tool_call_chunks?.length ?? 0;
66
+ if (
67
+ chunkCount === 0 ||
68
+ (message.invalid_tool_calls?.length ?? 0) > 0 ||
69
+ (message.tool_calls?.length ?? 0) !== chunkCount
70
+ ) {
71
+ return;
72
+ }
73
+ message.response_metadata = {
74
+ ...message.response_metadata,
75
+ [STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY]:
76
+ GOOGLE_STREAMED_TOOL_CALL_ADAPTER,
77
+ [STREAMED_TOOL_CALL_SEAL_METADATA_KEY]: { kind: 'all' },
78
+ };
79
+ }
80
+
51
81
  type AdditionalKwargs =
52
82
  | undefined
53
83
  | (BaseMessage['additional_kwargs'] & {
@@ -503,6 +533,7 @@ export class ChatVertexAI extends ChatGoogle {
503
533
  if (repaired !== chunk.message.usage_metadata) {
504
534
  chunk.message.usage_metadata = repaired;
505
535
  }
536
+ sealCompleteStreamedToolCalls(chunk.message);
506
537
  }
507
538
  yield chunk;
508
539
  }
@@ -0,0 +1,88 @@
1
+ import { expect, test, describe } from '@jest/globals';
2
+ import { AIMessageChunk } from '@langchain/core/messages';
3
+ import {
4
+ STREAMED_TOOL_CALL_SEAL_METADATA_KEY,
5
+ STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY,
6
+ GOOGLE_STREAMED_TOOL_CALL_ADAPTER,
7
+ } from '@/tools/streamedToolCallSeals';
8
+ import { sealCompleteStreamedToolCalls } from './index';
9
+
10
+ describe('sealCompleteStreamedToolCalls', () => {
11
+ test('stamps an on-arrival seal when every tool-call chunk parsed cleanly', () => {
12
+ const message = new AIMessageChunk({
13
+ content: '',
14
+ tool_call_chunks: [
15
+ {
16
+ id: 'call_1',
17
+ name: 'weather',
18
+ args: '{"city":"NYC"}',
19
+ type: 'tool_call_chunk',
20
+ },
21
+ ],
22
+ });
23
+
24
+ sealCompleteStreamedToolCalls(message);
25
+
26
+ expect(message.response_metadata).toMatchObject({
27
+ [STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY]:
28
+ GOOGLE_STREAMED_TOOL_CALL_ADAPTER,
29
+ [STREAMED_TOOL_CALL_SEAL_METADATA_KEY]: { kind: 'all' },
30
+ });
31
+ });
32
+
33
+ test('stamps multi-call chunks when all calls are complete', () => {
34
+ const message = new AIMessageChunk({
35
+ content: '',
36
+ tool_call_chunks: [
37
+ {
38
+ id: 'call_1',
39
+ name: 'weather',
40
+ args: '{"city":"NYC"}',
41
+ type: 'tool_call_chunk',
42
+ },
43
+ {
44
+ id: 'call_2',
45
+ name: 'stock',
46
+ args: '{"ticker":"CH"}',
47
+ type: 'tool_call_chunk',
48
+ },
49
+ ],
50
+ });
51
+
52
+ sealCompleteStreamedToolCalls(message);
53
+
54
+ expect(
55
+ message.response_metadata[STREAMED_TOOL_CALL_SEAL_METADATA_KEY]
56
+ ).toEqual({ kind: 'all' });
57
+ });
58
+
59
+ test('leaves chunks without tool calls unstamped', () => {
60
+ const message = new AIMessageChunk({ content: 'hello' });
61
+
62
+ sealCompleteStreamedToolCalls(message);
63
+
64
+ expect(
65
+ message.response_metadata[STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY]
66
+ ).toBeUndefined();
67
+ });
68
+
69
+ test('leaves chunks with unparsable tool calls unstamped', () => {
70
+ // No id forces the parse into invalid_tool_calls.
71
+ const message = new AIMessageChunk({
72
+ content: '',
73
+ tool_call_chunks: [
74
+ {
75
+ name: 'weather',
76
+ args: '{"city":',
77
+ type: 'tool_call_chunk',
78
+ },
79
+ ],
80
+ });
81
+
82
+ sealCompleteStreamedToolCalls(message);
83
+
84
+ expect(
85
+ message.response_metadata[STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY]
86
+ ).toBeUndefined();
87
+ });
88
+ });
@@ -0,0 +1,148 @@
1
+ import { expect, test, describe, jest } from '@jest/globals';
2
+ import { HumanMessage, AIMessageChunk } from '@langchain/core/messages';
3
+ import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
4
+ import type { ChatGenerationChunk } from '@langchain/core/outputs';
5
+ import {
6
+ STREAMED_TOOL_CALL_SEAL_METADATA_KEY,
7
+ STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY,
8
+ GOOGLE_STREAMED_TOOL_CALL_ADAPTER,
9
+ } from '@/tools/streamedToolCallSeals';
10
+ import { ChatVertexAI } from './index';
11
+
12
+ /**
13
+ * Registered stream handlers consume chunks through `handleLLMNewToken`
14
+ * callback events. `@langchain/google-common` yields each chunk BEFORE
15
+ * dispatching that callback, and the generator only resumes (firing the
16
+ * callback) after this package's `_streamResponseChunks` override has
17
+ * stamped the seal on the same message object — so callback consumers must
18
+ * observe sealed chunks. This drives the real google-common stream loop and
19
+ * conversion with a stubbed connection to lock that ordering in.
20
+ */
21
+ describe('Vertex stream seal dispatch', () => {
22
+ async function runStream(outputs: unknown[]): Promise<{
23
+ yielded: AIMessageChunk[];
24
+ dispatched: AIMessageChunk[];
25
+ }> {
26
+ const model = new ChatVertexAI({
27
+ model: 'gemini-2.5-flash',
28
+ authOptions: {
29
+ projectId: 'test-project',
30
+ credentials: { client_email: 'test@test', private_key: 'test' },
31
+ },
32
+ });
33
+
34
+ let index = 0;
35
+ const fakeStream = {
36
+ get streamDone(): boolean {
37
+ return index > outputs.length;
38
+ },
39
+ async nextChunk(): Promise<unknown> {
40
+ const output = index < outputs.length ? outputs[index] : null;
41
+ index += 1;
42
+ return output;
43
+ },
44
+ };
45
+ (
46
+ model as unknown as {
47
+ streamedConnection: { request: unknown };
48
+ }
49
+ ).streamedConnection.request = jest.fn(async () => ({ data: fakeStream }));
50
+
51
+ const dispatched: AIMessageChunk[] = [];
52
+ const runManager = {
53
+ handleCustomEvent: jest.fn(async () => undefined),
54
+ handleLLMNewToken: jest.fn(
55
+ async (
56
+ _token: string,
57
+ _idx?: unknown,
58
+ _runId?: unknown,
59
+ _parentRunId?: unknown,
60
+ _tags?: unknown,
61
+ fields?: { chunk?: ChatGenerationChunk }
62
+ ) => {
63
+ const message = fields?.chunk?.message;
64
+ if (message instanceof AIMessageChunk) {
65
+ dispatched.push(message);
66
+ }
67
+ }
68
+ ),
69
+ } as unknown as CallbackManagerForLLMRun;
70
+
71
+ const yielded: AIMessageChunk[] = [];
72
+ for await (const chunk of model._streamResponseChunks(
73
+ [new HumanMessage('hi')],
74
+ {} as Parameters<ChatVertexAI['_streamResponseChunks']>[1],
75
+ runManager
76
+ )) {
77
+ if (chunk.message instanceof AIMessageChunk) {
78
+ yielded.push(chunk.message);
79
+ }
80
+ }
81
+ return { yielded, dispatched };
82
+ }
83
+
84
+ test('callback consumers receive function-call chunks already sealed', async () => {
85
+ const { yielded, dispatched } = await runStream([
86
+ {
87
+ candidates: [
88
+ {
89
+ content: {
90
+ role: 'model',
91
+ parts: [
92
+ { functionCall: { name: 'weather', args: { city: 'NYC' } } },
93
+ ],
94
+ },
95
+ index: 0,
96
+ },
97
+ ],
98
+ },
99
+ ]);
100
+
101
+ const metadataOf = (m: AIMessageChunk): Record<string, unknown> =>
102
+ m.response_metadata as Record<string, unknown>;
103
+
104
+ const yieldedCall = yielded.find(
105
+ (m) => (m.tool_call_chunks?.length ?? 0) > 0
106
+ );
107
+ expect(yieldedCall).toBeDefined();
108
+ expect(
109
+ metadataOf(yieldedCall!)[STREAMED_TOOL_CALL_SEAL_METADATA_KEY]
110
+ ).toEqual({ kind: 'all' });
111
+
112
+ const dispatchedCall = dispatched.find(
113
+ (m) => (m.tool_call_chunks?.length ?? 0) > 0
114
+ );
115
+ expect(dispatchedCall).toBeDefined();
116
+ expect(dispatchedCall!.tool_calls?.[0]).toMatchObject({
117
+ name: 'weather',
118
+ args: { city: 'NYC' },
119
+ });
120
+ expect(
121
+ metadataOf(dispatchedCall!)[STREAMED_TOOL_CALL_SEAL_METADATA_KEY]
122
+ ).toEqual({ kind: 'all' });
123
+ expect(
124
+ metadataOf(dispatchedCall!)[STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY]
125
+ ).toBe(GOOGLE_STREAMED_TOOL_CALL_ADAPTER);
126
+ });
127
+
128
+ test('text-only chunks are not sealed on either path', async () => {
129
+ const { yielded, dispatched } = await runStream([
130
+ {
131
+ candidates: [
132
+ {
133
+ content: { role: 'model', parts: [{ text: 'hello' }] },
134
+ index: 0,
135
+ },
136
+ ],
137
+ },
138
+ ]);
139
+
140
+ const hasSeal = (m: AIMessageChunk): boolean =>
141
+ (m.response_metadata as Record<string, unknown>)[
142
+ STREAMED_TOOL_CALL_SEAL_METADATA_KEY
143
+ ] != null;
144
+
145
+ expect(yielded.some(hasSeal)).toBe(false);
146
+ expect(dispatched.some(hasSeal)).toBe(false);
147
+ });
148
+ });
package/src/stream.ts CHANGED
@@ -5,6 +5,12 @@ import type { AIMessageChunk } from '@langchain/core/messages';
5
5
  import type { AgentContext } from '@/agents/AgentContext';
6
6
  import type { StandardGraph } from '@/graphs';
7
7
  import type * as t from '@/types';
8
+ import {
9
+ getStreamedToolCallSeal,
10
+ getStreamedToolCallAdapter,
11
+ streamedToolCallAdapterAllowsSequentialSeal,
12
+ type StreamedToolCallSeal,
13
+ } from '@/tools/streamedToolCallSeals';
8
14
  import {
9
15
  ToolCallTypes,
10
16
  ContentTypes,
@@ -15,11 +21,6 @@ import {
15
21
  CODE_EXECUTION_TOOLS,
16
22
  LOCAL_CODING_BUNDLE_NAMES,
17
23
  } from '@/common';
18
- import {
19
- getStreamedToolCallSeal,
20
- getStreamedToolCallAdapter,
21
- type StreamedToolCallSeal,
22
- } from '@/tools/streamedToolCallSeals';
23
24
  import {
24
25
  buildToolExecutionRequestPlan,
25
26
  coerceRecordArgs,
@@ -265,6 +266,21 @@ function hasExplicitStreamedToolCallSeals(
265
266
  );
266
267
  }
267
268
 
269
+ /**
270
+ * True when a provider adapter marked every tool call on this chunk as
271
+ * complete on arrival (seal kind `all`), e.g. Google GenAI / Vertex AI, whose
272
+ * protocol delivers function calls as whole objects rather than arg deltas.
273
+ */
274
+ function hasOnArrivalToolCallSeal(chunk: Partial<AIMessageChunk>): boolean {
275
+ const metadata = chunk.response_metadata as
276
+ | Record<string, unknown>
277
+ | undefined;
278
+ return (
279
+ getStreamedToolCallAdapter(metadata) != null &&
280
+ getStreamedToolCallSeal(metadata)?.kind === 'all'
281
+ );
282
+ }
283
+
268
284
  function hasDirectToolCallInBatch(args: {
269
285
  graph: StandardGraph;
270
286
  agentContext?: AgentContext;
@@ -1405,6 +1421,21 @@ export class ChatModelStreamHandler implements t.EventHandler {
1405
1421
  if (!hasToolCallChunks) {
1406
1422
  pruneEagerToolCallChunkStates({ graph, stepKey, clearStep: true });
1407
1423
  }
1424
+ } else if (
1425
+ hasOnArrivalToolCallSeal(chunk) &&
1426
+ !hasPotentialDirectToolInStreamContext({ graph, agentContext })
1427
+ ) {
1428
+ // Providers like Google never signal `tool_calls`/`tool_use` as the
1429
+ // finish reason, but their adapters seal calls on arrival — prestart
1430
+ // these mid-stream under the same direct-tool guard as streamed
1431
+ // chunk sealing.
1432
+ startEagerToolExecutions({
1433
+ graph,
1434
+ metadata,
1435
+ agentContext,
1436
+ toolCalls: chunk.tool_calls,
1437
+ skipExisting: true,
1438
+ });
1408
1439
  }
1409
1440
  }
1410
1441
 
@@ -1435,7 +1466,10 @@ export class ChatModelStreamHandler implements t.EventHandler {
1435
1466
  chunk.response_metadata as Record<string, unknown> | undefined
1436
1467
  );
1437
1468
  const allowSequentialSeal =
1438
- canPrestartSequentialStreamedToolChunks(agentContext);
1469
+ canPrestartSequentialStreamedToolChunks(agentContext) ||
1470
+ streamedToolCallAdapterAllowsSequentialSeal(
1471
+ chunk.response_metadata as Record<string, unknown> | undefined
1472
+ );
1439
1473
  const canStreamEager =
1440
1474
  (allowSequentialSeal || hasExplicitStreamedToolCallSeals(chunk)) &&
1441
1475
  !hasPotentialDirectToolInStreamContext({ graph, agentContext }) &&
@@ -2476,6 +2476,49 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2476
2476
  }
2477
2477
  }
2478
2478
 
2479
+ /**
2480
+ * Per-call completion fast-path: when the host reports a result
2481
+ * through `onResult` before the batch resolves, emit that call's
2482
+ * completed run step immediately instead of waiting for the slowest
2483
+ * call in the batch. Safe only when nothing can change the result
2484
+ * after execution — post-tool hooks may rewrite output and HITL may
2485
+ * deny a call, so those configurations keep batch-time emission.
2486
+ * Ids are claimed synchronously before the async dispatch and
2487
+ * released if the dispatch fails, letting the batch path re-emit.
2488
+ */
2489
+ const canEmitEarlyCompletions =
2490
+ this.hookRegistry == null && this.humanInTheLoop?.enabled !== true;
2491
+ const earlyCompletionDispatchedIds = new Set<string>();
2492
+ const earlyCompletionDispatches: Array<Promise<void>> = [];
2493
+ const dispatchRequestById = new Map(
2494
+ dispatchRequests.map((request) => [request.id, request])
2495
+ );
2496
+ const onResult = (result: t.ToolExecuteResult): void => {
2497
+ const request =
2498
+ result.toolCallId != null
2499
+ ? dispatchRequestById.get(result.toolCallId)
2500
+ : undefined;
2501
+ if (
2502
+ request == null ||
2503
+ earlyCompletionDispatchedIds.has(result.toolCallId)
2504
+ ) {
2505
+ return;
2506
+ }
2507
+ earlyCompletionDispatchedIds.add(result.toolCallId);
2508
+ earlyCompletionDispatches.push(
2509
+ this.dispatchEarlyToolCompletion(result, request, config).then(
2510
+ (dispatched) => {
2511
+ if (!dispatched) {
2512
+ earlyCompletionDispatchedIds.delete(result.toolCallId);
2513
+ }
2514
+ },
2515
+ () => {
2516
+ earlyCompletionDispatchedIds.delete(result.toolCallId);
2517
+ }
2518
+ )
2519
+ );
2520
+ };
2521
+
2479
2522
  const dispatchPromise =
2480
2523
  dispatchRequests.length === 0
2481
2524
  ? Promise.resolve([] as t.ToolExecuteResult[])
@@ -2506,6 +2549,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2506
2549
  maybeResolve();
2507
2550
  },
2508
2551
  reject,
2552
+ ...(canEmitEarlyCompletions && { onResult }),
2509
2553
  };
2510
2554
 
2511
2555
  void safeDispatchCustomEvent(
@@ -2540,6 +2584,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2540
2584
  eagerResultsPromise,
2541
2585
  dispatchPromise,
2542
2586
  ]);
2587
+ // Settle in-flight early completion dispatches before the batch loop
2588
+ // below decides which completions still need emitting.
2589
+ await Promise.allSettled(earlyCompletionDispatches);
2543
2590
  const eagerCompletionDispatchedIds = new Set(
2544
2591
  eagerResults
2545
2592
  .filter((result) => result.completionDispatched)
@@ -2728,7 +2775,10 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2728
2775
  });
2729
2776
  }
2730
2777
 
2731
- if (!eagerCompletionDispatchedIds.has(result.toolCallId)) {
2778
+ if (
2779
+ !eagerCompletionDispatchedIds.has(result.toolCallId) &&
2780
+ !earlyCompletionDispatchedIds.has(result.toolCallId)
2781
+ ) {
2732
2782
  await this.dispatchStepCompleted(
2733
2783
  result.toolCallId,
2734
2784
  toolName,
@@ -2946,7 +2996,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2946
2996
  output: string,
2947
2997
  config: RunnableConfig,
2948
2998
  turn?: number
2949
- ): Promise<void> {
2999
+ ): Promise<boolean> {
2950
3000
  const stepId = this.toolCallStepIds?.get(toolCallId) ?? '';
2951
3001
  if (!stepId) {
2952
3002
  // eslint-disable-next-line no-console
@@ -2957,7 +3007,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2957
3007
  );
2958
3008
  }
2959
3009
 
2960
- await safeDispatchCustomEvent(
3010
+ const dispatched = await safeDispatchCustomEvent(
2961
3011
  GraphEvents.ON_RUN_STEP_COMPLETED,
2962
3012
  {
2963
3013
  result: {
@@ -2975,6 +3025,38 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2975
3025
  },
2976
3026
  config
2977
3027
  );
3028
+ return dispatched !== false;
3029
+ }
3030
+
3031
+ /**
3032
+ * Emits the completed run step for a single host-reported result before
3033
+ * the batch resolves. Mirrors the batch loop's output formatting exactly;
3034
+ * callers gate on the no-hooks/no-HITL configuration, so the raw result
3035
+ * content here is also the final content. Returns whether the event was
3036
+ * actually dispatched so the caller can fall back to batch-time emission.
3037
+ */
3038
+ private async dispatchEarlyToolCompletion(
3039
+ result: t.ToolExecuteResult,
3040
+ request: t.ToolCallRequest,
3041
+ config: RunnableConfig
3042
+ ): Promise<boolean> {
3043
+ const output =
3044
+ result.status === 'error'
3045
+ ? `Error: ${result.errorMessage ?? 'Unknown error'}\n Please fix your mistakes.`
3046
+ : truncateToolResultContent(
3047
+ typeof result.content === 'string'
3048
+ ? result.content
3049
+ : JSON.stringify(result.content),
3050
+ this.maxToolResultChars
3051
+ );
3052
+ return this.dispatchStepCompleted(
3053
+ result.toolCallId,
3054
+ request.name,
3055
+ request.args,
3056
+ output,
3057
+ config,
3058
+ request.turn
3059
+ );
2978
3060
  }
2979
3061
 
2980
3062
  /**