@librechat/agents 3.2.21 → 3.2.32

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 (402) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +3 -2
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/events.cjs.map +1 -1
  4. package/dist/cjs/graphs/Graph.cjs +200 -54
  5. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  6. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  7. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +13 -7
  8. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs.map +1 -1
  9. package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
  10. package/dist/cjs/hooks/types.cjs.map +1 -1
  11. package/dist/cjs/instrumentation.cjs +2 -2
  12. package/dist/cjs/instrumentation.cjs.map +1 -1
  13. package/dist/cjs/langfuse.cjs +17 -1
  14. package/dist/cjs/langfuse.cjs.map +1 -1
  15. package/dist/cjs/langfuseToolOutputTracing.cjs +2 -2
  16. package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
  17. package/dist/cjs/llm/anthropic/index.cjs +1 -1
  18. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  19. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +38 -3
  20. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  21. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +6 -2
  22. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
  23. package/dist/cjs/llm/bedrock/index.cjs +2 -2
  24. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  25. package/dist/cjs/llm/bedrock/toolCache.cjs +8 -5
  26. package/dist/cjs/llm/bedrock/toolCache.cjs.map +1 -1
  27. package/dist/cjs/llm/fake.cjs +16 -14
  28. package/dist/cjs/llm/fake.cjs.map +1 -1
  29. package/dist/cjs/llm/google/index.cjs +22 -0
  30. package/dist/cjs/llm/google/index.cjs.map +1 -1
  31. package/dist/cjs/llm/google/utils/common.cjs +88 -27
  32. package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
  33. package/dist/cjs/llm/init.cjs +2 -2
  34. package/dist/cjs/llm/invoke.cjs +108 -11
  35. package/dist/cjs/llm/invoke.cjs.map +1 -1
  36. package/dist/cjs/llm/openai/index.cjs +1 -1
  37. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  38. package/dist/cjs/llm/openai/utils/index.cjs +1 -1
  39. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  40. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  41. package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
  42. package/dist/cjs/main.cjs +1 -0
  43. package/dist/cjs/main.cjs.map +1 -1
  44. package/dist/cjs/messages/cache.cjs +29 -8
  45. package/dist/cjs/messages/cache.cjs.map +1 -1
  46. package/dist/cjs/messages/content.cjs.map +1 -1
  47. package/dist/cjs/messages/contextPruning.cjs.map +1 -1
  48. package/dist/cjs/messages/format.cjs +129 -17
  49. package/dist/cjs/messages/format.cjs.map +1 -1
  50. package/dist/cjs/messages/prune.cjs.map +1 -1
  51. package/dist/cjs/messages/reducer.cjs +1 -1
  52. package/dist/cjs/messages/reducer.cjs.map +1 -1
  53. package/dist/cjs/messages/tools.cjs +1 -1
  54. package/dist/cjs/messages/tools.cjs.map +1 -1
  55. package/dist/cjs/openai/index.cjs.map +1 -1
  56. package/dist/cjs/responses/index.cjs.map +1 -1
  57. package/dist/cjs/run.cjs +41 -20
  58. package/dist/cjs/run.cjs.map +1 -1
  59. package/dist/cjs/session/AgentSession.cjs +4 -4
  60. package/dist/cjs/session/AgentSession.cjs.map +1 -1
  61. package/dist/cjs/session/JsonlSessionStore.cjs +2 -2
  62. package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -1
  63. package/dist/cjs/session/handlers.cjs +2 -2
  64. package/dist/cjs/session/handlers.cjs.map +1 -1
  65. package/dist/cjs/stream.cjs +248 -25
  66. package/dist/cjs/stream.cjs.map +1 -1
  67. package/dist/cjs/summarization/node.cjs.map +1 -1
  68. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +1 -1
  69. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  70. package/dist/cjs/tools/Calculator.cjs +1 -1
  71. package/dist/cjs/tools/Calculator.cjs.map +1 -1
  72. package/dist/cjs/tools/CodeExecutor.cjs +1 -1
  73. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  74. package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
  75. package/dist/cjs/tools/ToolNode.cjs +37 -18
  76. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  77. package/dist/cjs/tools/ToolSearch.cjs +1 -1
  78. package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
  79. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs +7 -4
  80. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs.map +1 -1
  81. package/dist/cjs/tools/cloudflare/CloudflareSandboxTools.cjs +4 -4
  82. package/dist/cjs/tools/cloudflare/CloudflareSandboxTools.cjs.map +1 -1
  83. package/dist/cjs/tools/handlers.cjs +2 -1
  84. package/dist/cjs/tools/handlers.cjs.map +1 -1
  85. package/dist/cjs/tools/local/CompileCheckTool.cjs.map +1 -1
  86. package/dist/cjs/tools/local/FileCheckpointer.cjs +2 -1
  87. package/dist/cjs/tools/local/FileCheckpointer.cjs.map +1 -1
  88. package/dist/cjs/tools/local/LocalCodingTools.cjs +45 -19
  89. package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
  90. package/dist/cjs/tools/local/LocalExecutionEngine.cjs +3 -3
  91. package/dist/cjs/tools/local/LocalExecutionEngine.cjs.map +1 -1
  92. package/dist/cjs/tools/local/LocalExecutionTools.cjs +2 -2
  93. package/dist/cjs/tools/local/LocalExecutionTools.cjs.map +1 -1
  94. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +4 -3
  95. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
  96. package/dist/cjs/tools/local/attachments.cjs +0 -5
  97. package/dist/cjs/tools/local/attachments.cjs.map +1 -1
  98. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +4 -4
  99. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -1
  100. package/dist/cjs/tools/search/firecrawl.cjs +1 -1
  101. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
  102. package/dist/cjs/tools/search/rerankers.cjs +7 -3
  103. package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
  104. package/dist/cjs/tools/search/tavily-search.cjs +1 -1
  105. package/dist/cjs/tools/search/tavily-search.cjs.map +1 -1
  106. package/dist/cjs/tools/search/utils.cjs +76 -8
  107. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  108. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -1
  109. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  110. package/dist/cjs/utils/handlers.cjs +1 -1
  111. package/dist/cjs/utils/handlers.cjs.map +1 -1
  112. package/dist/cjs/utils/run.cjs +1 -1
  113. package/dist/cjs/utils/run.cjs.map +1 -1
  114. package/dist/esm/agents/AgentContext.mjs +3 -2
  115. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  116. package/dist/esm/events.mjs.map +1 -1
  117. package/dist/esm/graphs/Graph.mjs +200 -54
  118. package/dist/esm/graphs/Graph.mjs.map +1 -1
  119. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  120. package/dist/esm/hooks/createWorkspacePolicyHook.mjs +13 -7
  121. package/dist/esm/hooks/createWorkspacePolicyHook.mjs.map +1 -1
  122. package/dist/esm/hooks/executeHooks.mjs.map +1 -1
  123. package/dist/esm/hooks/types.mjs.map +1 -1
  124. package/dist/esm/instrumentation.mjs +2 -2
  125. package/dist/esm/instrumentation.mjs.map +1 -1
  126. package/dist/esm/langfuse.mjs +17 -2
  127. package/dist/esm/langfuse.mjs.map +1 -1
  128. package/dist/esm/langfuseToolOutputTracing.mjs +2 -2
  129. package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
  130. package/dist/esm/llm/anthropic/index.mjs +1 -1
  131. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  132. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +38 -3
  133. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  134. package/dist/esm/llm/anthropic/utils/message_outputs.mjs +6 -2
  135. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  136. package/dist/esm/llm/bedrock/index.mjs +2 -2
  137. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  138. package/dist/esm/llm/bedrock/toolCache.mjs +8 -5
  139. package/dist/esm/llm/bedrock/toolCache.mjs.map +1 -1
  140. package/dist/esm/llm/fake.mjs +16 -14
  141. package/dist/esm/llm/fake.mjs.map +1 -1
  142. package/dist/esm/llm/google/index.mjs +23 -1
  143. package/dist/esm/llm/google/index.mjs.map +1 -1
  144. package/dist/esm/llm/google/utils/common.mjs +88 -27
  145. package/dist/esm/llm/google/utils/common.mjs.map +1 -1
  146. package/dist/esm/llm/init.mjs +2 -2
  147. package/dist/esm/llm/invoke.mjs +104 -7
  148. package/dist/esm/llm/invoke.mjs.map +1 -1
  149. package/dist/esm/llm/openai/index.mjs +1 -1
  150. package/dist/esm/llm/openai/index.mjs.map +1 -1
  151. package/dist/esm/llm/openai/utils/index.mjs +1 -1
  152. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  153. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  154. package/dist/esm/llm/vertexai/index.mjs.map +1 -1
  155. package/dist/esm/main.mjs +1 -1
  156. package/dist/esm/messages/cache.mjs +29 -8
  157. package/dist/esm/messages/cache.mjs.map +1 -1
  158. package/dist/esm/messages/content.mjs.map +1 -1
  159. package/dist/esm/messages/contextPruning.mjs.map +1 -1
  160. package/dist/esm/messages/format.mjs +129 -18
  161. package/dist/esm/messages/format.mjs.map +1 -1
  162. package/dist/esm/messages/prune.mjs.map +1 -1
  163. package/dist/esm/messages/reducer.mjs +1 -1
  164. package/dist/esm/messages/reducer.mjs.map +1 -1
  165. package/dist/esm/messages/tools.mjs +1 -1
  166. package/dist/esm/messages/tools.mjs.map +1 -1
  167. package/dist/esm/openai/index.mjs.map +1 -1
  168. package/dist/esm/responses/index.mjs.map +1 -1
  169. package/dist/esm/run.mjs +41 -20
  170. package/dist/esm/run.mjs.map +1 -1
  171. package/dist/esm/session/AgentSession.mjs +4 -4
  172. package/dist/esm/session/AgentSession.mjs.map +1 -1
  173. package/dist/esm/session/JsonlSessionStore.mjs +2 -2
  174. package/dist/esm/session/JsonlSessionStore.mjs.map +1 -1
  175. package/dist/esm/session/handlers.mjs +2 -2
  176. package/dist/esm/session/handlers.mjs.map +1 -1
  177. package/dist/esm/stream.mjs +248 -25
  178. package/dist/esm/stream.mjs.map +1 -1
  179. package/dist/esm/summarization/node.mjs.map +1 -1
  180. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +1 -1
  181. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  182. package/dist/esm/tools/Calculator.mjs +1 -1
  183. package/dist/esm/tools/Calculator.mjs.map +1 -1
  184. package/dist/esm/tools/CodeExecutor.mjs +1 -1
  185. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  186. package/dist/esm/tools/SubagentTool.mjs.map +1 -1
  187. package/dist/esm/tools/ToolNode.mjs +37 -18
  188. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  189. package/dist/esm/tools/ToolSearch.mjs +1 -1
  190. package/dist/esm/tools/ToolSearch.mjs.map +1 -1
  191. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs +7 -4
  192. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs.map +1 -1
  193. package/dist/esm/tools/cloudflare/CloudflareSandboxTools.mjs +4 -4
  194. package/dist/esm/tools/cloudflare/CloudflareSandboxTools.mjs.map +1 -1
  195. package/dist/esm/tools/handlers.mjs +2 -1
  196. package/dist/esm/tools/handlers.mjs.map +1 -1
  197. package/dist/esm/tools/local/CompileCheckTool.mjs.map +1 -1
  198. package/dist/esm/tools/local/FileCheckpointer.mjs +2 -1
  199. package/dist/esm/tools/local/FileCheckpointer.mjs.map +1 -1
  200. package/dist/esm/tools/local/LocalCodingTools.mjs +45 -19
  201. package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
  202. package/dist/esm/tools/local/LocalExecutionEngine.mjs +3 -3
  203. package/dist/esm/tools/local/LocalExecutionEngine.mjs.map +1 -1
  204. package/dist/esm/tools/local/LocalExecutionTools.mjs +2 -2
  205. package/dist/esm/tools/local/LocalExecutionTools.mjs.map +1 -1
  206. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +4 -3
  207. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
  208. package/dist/esm/tools/local/attachments.mjs +0 -5
  209. package/dist/esm/tools/local/attachments.mjs.map +1 -1
  210. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +4 -4
  211. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -1
  212. package/dist/esm/tools/search/firecrawl.mjs +1 -1
  213. package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
  214. package/dist/esm/tools/search/rerankers.mjs +8 -4
  215. package/dist/esm/tools/search/rerankers.mjs.map +1 -1
  216. package/dist/esm/tools/search/tavily-search.mjs +1 -1
  217. package/dist/esm/tools/search/tavily-search.mjs.map +1 -1
  218. package/dist/esm/tools/search/utils.mjs +76 -9
  219. package/dist/esm/tools/search/utils.mjs.map +1 -1
  220. package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -1
  221. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  222. package/dist/esm/utils/handlers.mjs +1 -1
  223. package/dist/esm/utils/handlers.mjs.map +1 -1
  224. package/dist/esm/utils/run.mjs +1 -1
  225. package/dist/esm/utils/run.mjs.map +1 -1
  226. package/dist/types/agents/__tests__/promptCacheLiveHelpers.d.ts +1 -1
  227. package/dist/types/events.d.ts +1 -1
  228. package/dist/types/graphs/Graph.d.ts +7 -1
  229. package/dist/types/hooks/executeHooks.d.ts +1 -1
  230. package/dist/types/hooks/types.d.ts +5 -0
  231. package/dist/types/langfuse.d.ts +4 -0
  232. package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -1
  233. package/dist/types/llm/anthropic/utils/message_outputs.d.ts +1 -1
  234. package/dist/types/llm/anthropic/utils/output_parsers.d.ts +2 -2
  235. package/dist/types/llm/bedrock/index.d.ts +2 -2
  236. package/dist/types/llm/fake.d.ts +3 -3
  237. package/dist/types/llm/google/index.d.ts +1 -0
  238. package/dist/types/llm/google/types.d.ts +1 -1
  239. package/dist/types/llm/google/utils/common.d.ts +2 -2
  240. package/dist/types/llm/google/utils/tools.d.ts +1 -1
  241. package/dist/types/llm/google/utils/zod_to_genai_parameters.d.ts +1 -1
  242. package/dist/types/llm/openai/index.d.ts +2 -2
  243. package/dist/types/llm/openai/utils/index.d.ts +1 -1
  244. package/dist/types/llm/openrouter/index.d.ts +4 -4
  245. package/dist/types/messages/contextPruning.d.ts +1 -1
  246. package/dist/types/messages/format.d.ts +14 -4
  247. package/dist/types/messages/prune.d.ts +1 -1
  248. package/dist/types/session/JsonlSessionStore.d.ts +1 -1
  249. package/dist/types/session/handlers.d.ts +1 -1
  250. package/dist/types/session/types.d.ts +1 -1
  251. package/dist/types/summarization/node.d.ts +1 -1
  252. package/dist/types/tools/SubagentTool.d.ts +2 -2
  253. package/dist/types/tools/ToolNode.d.ts +9 -2
  254. package/dist/types/tools/cloudflare/CloudflareSandboxExecutionEngine.d.ts +1 -1
  255. package/dist/types/tools/search/types.d.ts +1 -1
  256. package/dist/types/tools/search/utils.d.ts +11 -0
  257. package/dist/types/types/graph.d.ts +4 -4
  258. package/dist/types/types/llm.d.ts +4 -3
  259. package/dist/types/types/messages.d.ts +1 -1
  260. package/dist/types/types/run.d.ts +6 -6
  261. package/dist/types/types/stream.d.ts +2 -2
  262. package/dist/types/types/tools.d.ts +5 -1
  263. package/dist/types/utils/handlers.d.ts +2 -2
  264. package/dist/types/utils/run.d.ts +1 -1
  265. package/package.json +13 -10
  266. package/src/__tests__/stream.eagerEventExecution.test.ts +543 -6
  267. package/src/agents/AgentContext.ts +2 -2
  268. package/src/agents/__tests__/AgentContext.test.ts +3 -3
  269. package/src/agents/__tests__/promptCacheLiveHelpers.ts +1 -1
  270. package/src/events.ts +1 -1
  271. package/src/graphs/Graph.ts +329 -72
  272. package/src/graphs/MultiAgentGraph.ts +1 -1
  273. package/src/graphs/__tests__/Graph.reasoning.test.ts +919 -6
  274. package/src/graphs/__tests__/MultiAgentGraph.test.ts +1 -1
  275. package/src/graphs/__tests__/composition.smoke.test.ts +1 -1
  276. package/src/hooks/__tests__/HookRegistry.test.ts +1 -1
  277. package/src/hooks/__tests__/compactHooks.test.ts +8 -8
  278. package/src/hooks/__tests__/createWorkspacePolicyHook.test.ts +34 -22
  279. package/src/hooks/__tests__/executeHooks.test.ts +3 -3
  280. package/src/hooks/__tests__/integration.test.ts +3 -3
  281. package/src/hooks/__tests__/toolHooks.test.ts +10 -10
  282. package/src/hooks/createWorkspacePolicyHook.ts +17 -14
  283. package/src/hooks/executeHooks.ts +1 -1
  284. package/src/hooks/types.ts +5 -0
  285. package/src/instrumentation.ts +11 -11
  286. package/src/langfuse.ts +35 -1
  287. package/src/langfuseToolOutputTracing.ts +2 -2
  288. package/src/llm/anthropic/index.ts +1 -1
  289. package/src/llm/anthropic/llm.spec.ts +36 -0
  290. package/src/llm/anthropic/utils/message_inputs.ts +46 -4
  291. package/src/llm/anthropic/utils/message_outputs.ts +9 -7
  292. package/src/llm/anthropic/utils/output_parsers.ts +5 -5
  293. package/src/llm/anthropic/utils/streaming-tool-input.test.ts +186 -0
  294. package/src/llm/bedrock/index.ts +4 -4
  295. package/src/llm/bedrock/toolCache.test.ts +48 -9
  296. package/src/llm/bedrock/toolCache.ts +11 -6
  297. package/src/llm/fake.ts +30 -25
  298. package/src/llm/google/index.ts +43 -1
  299. package/src/llm/google/llm.spec.ts +173 -1
  300. package/src/llm/google/types.ts +1 -1
  301. package/src/llm/google/utils/common.ts +154 -45
  302. package/src/llm/google/utils/tools.ts +8 -8
  303. package/src/llm/google/utils/zod_to_genai_parameters.ts +4 -4
  304. package/src/llm/invoke.test.ts +3 -3
  305. package/src/llm/invoke.ts +170 -10
  306. package/src/llm/openai/index.ts +4 -4
  307. package/src/llm/openai/utils/index.ts +14 -14
  308. package/src/llm/openrouter/index.ts +4 -4
  309. package/src/llm/openrouter/reasoning.test.ts +2 -2
  310. package/src/llm/vertexai/fixThoughtSignatures.test.ts +1 -1
  311. package/src/llm/vertexai/index.ts +1 -1
  312. package/src/messages/cache.test.ts +144 -0
  313. package/src/messages/cache.ts +50 -13
  314. package/src/messages/content.ts +1 -1
  315. package/src/messages/contextPruning.ts +1 -1
  316. package/src/messages/format.ts +236 -43
  317. package/src/messages/formatAgentMessages.skills.test.ts +205 -26
  318. package/src/messages/formatAgentMessages.test.ts +841 -10
  319. package/src/messages/labelContentByAgent.test.ts +2 -2
  320. package/src/messages/prune.ts +1 -1
  321. package/src/messages/reducer.ts +1 -1
  322. package/src/messages/tools.ts +1 -1
  323. package/src/openai/__tests__/openai.test.ts +2 -2
  324. package/src/openai/index.ts +1 -1
  325. package/src/responses/__tests__/responses.test.ts +2 -2
  326. package/src/responses/index.ts +1 -1
  327. package/src/run.ts +68 -41
  328. package/src/session/AgentSession.ts +6 -6
  329. package/src/session/JsonlSessionStore.ts +3 -3
  330. package/src/session/__tests__/JsonlSessionStore.test.ts +5 -5
  331. package/src/session/__tests__/handlers.test.ts +2 -2
  332. package/src/session/handlers.ts +5 -5
  333. package/src/session/types.ts +1 -1
  334. package/src/specs/agent-handoffs.test.ts +1 -1
  335. package/src/specs/langfuse-callbacks.test.ts +2 -2
  336. package/src/specs/langfuse-metadata.test.ts +39 -0
  337. package/src/specs/langfuse-tool-output-tracing.test.ts +1 -1
  338. package/src/specs/multi-agent-summarization.test.ts +4 -4
  339. package/src/specs/subagent.test.ts +3 -3
  340. package/src/specs/summarization-unit.test.ts +1 -1
  341. package/src/specs/thinking-handoff.test.ts +1 -1
  342. package/src/splitStream.test.ts +48 -0
  343. package/src/stream.test.ts +53 -3
  344. package/src/stream.ts +450 -39
  345. package/src/summarization/__tests__/aggregator.test.ts +2 -2
  346. package/src/summarization/__tests__/node.test.ts +2 -2
  347. package/src/summarization/node.ts +1 -1
  348. package/src/tools/BashProgrammaticToolCalling.ts +5 -5
  349. package/src/tools/Calculator.ts +1 -1
  350. package/src/tools/CodeExecutor.ts +2 -4
  351. package/src/tools/SubagentTool.ts +2 -2
  352. package/src/tools/ToolNode.ts +37 -16
  353. package/src/tools/ToolSearch.ts +1 -1
  354. package/src/tools/__tests__/CloudflareSandboxExecution.test.ts +4 -4
  355. package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +12 -12
  356. package/src/tools/__tests__/LocalExecutionTools.test.ts +125 -93
  357. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +29 -5
  358. package/src/tools/__tests__/ReadFile.test.ts +1 -1
  359. package/src/tools/__tests__/SkillTool.test.ts +4 -4
  360. package/src/tools/__tests__/SubagentExecutor.test.ts +17 -13
  361. package/src/tools/__tests__/SubagentTool.test.ts +2 -2
  362. package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +1 -1
  363. package/src/tools/__tests__/ToolNode.outputReferences.test.ts +2 -5
  364. package/src/tools/__tests__/ToolNode.session.test.ts +1 -1
  365. package/src/tools/__tests__/ToolSearch.test.ts +1 -1
  366. package/src/tools/__tests__/annotateMessagesForLLM.test.ts +1 -1
  367. package/src/tools/__tests__/directToolHITLResumeScope.test.ts +35 -32
  368. package/src/tools/__tests__/directToolHooks.test.ts +41 -0
  369. package/src/tools/__tests__/handlers.test.ts +2 -2
  370. package/src/tools/__tests__/hitl.test.ts +11 -11
  371. package/src/tools/__tests__/localToolNames.test.ts +8 -6
  372. package/src/tools/__tests__/skillCatalog.test.ts +1 -1
  373. package/src/tools/__tests__/subagentHooks.test.ts +20 -10
  374. package/src/tools/__tests__/workspaceSeam.test.ts +20 -7
  375. package/src/tools/cloudflare/CloudflareSandboxExecutionEngine.ts +9 -6
  376. package/src/tools/cloudflare/CloudflareSandboxTools.ts +19 -19
  377. package/src/tools/handlers.ts +5 -5
  378. package/src/tools/local/CompileCheckTool.ts +4 -7
  379. package/src/tools/local/FileCheckpointer.ts +6 -5
  380. package/src/tools/local/LocalCodingTools.ts +100 -45
  381. package/src/tools/local/LocalExecutionEngine.ts +5 -5
  382. package/src/tools/local/LocalExecutionTools.ts +9 -9
  383. package/src/tools/local/LocalProgrammaticToolCalling.ts +5 -4
  384. package/src/tools/local/attachments.ts +0 -6
  385. package/src/tools/local/resolveLocalExecutionTools.ts +15 -15
  386. package/src/tools/search/firecrawl.ts +1 -1
  387. package/src/tools/search/jina-reranker.test.ts +148 -37
  388. package/src/tools/search/rerankers.ts +14 -4
  389. package/src/tools/search/tavily-search.ts +2 -2
  390. package/src/tools/search/types.ts +1 -1
  391. package/src/tools/search/utils.ts +99 -9
  392. package/src/tools/subagent/SubagentExecutor.ts +12 -6
  393. package/src/types/graph.ts +12 -12
  394. package/src/types/llm.ts +7 -6
  395. package/src/types/messages.ts +1 -1
  396. package/src/types/run.ts +7 -7
  397. package/src/types/stream.ts +2 -2
  398. package/src/types/tools.ts +5 -1
  399. package/src/utils/handlers.ts +2 -2
  400. package/src/utils/llmConfig.ts +1 -1
  401. package/src/utils/logging.ts +20 -10
  402. package/src/utils/run.ts +2 -2
@@ -2,18 +2,18 @@ import { z } from 'zod';
2
2
  import { tool } from '@langchain/core/tools';
3
3
  import { describe, it, expect } from '@jest/globals';
4
4
  import { AIMessage, HumanMessage } from '@langchain/core/messages';
5
- import type { BaseMessage } from '@langchain/core/messages';
6
5
  import type { StructuredToolInterface } from '@langchain/core/tools';
6
+ import type { BaseMessage } from '@langchain/core/messages';
7
7
  import type * as t from '@/types';
8
- import * as events from '@/utils/events';
9
- import { ToolNode } from '../ToolNode';
10
- import { Constants } from '@/common';
11
8
  import {
12
9
  SkillToolDescription,
13
10
  SkillToolDefinition,
14
11
  SkillToolSchema,
15
12
  SkillToolName,
16
13
  } from '../SkillTool';
14
+ import * as events from '@/utils/events';
15
+ import { ToolNode } from '../ToolNode';
16
+ import { Constants } from '@/common';
17
17
 
18
18
  describe('SkillTool', () => {
19
19
  describe('schema structure', () => {
@@ -1,10 +1,6 @@
1
1
  import { describe, it, expect, beforeEach } from '@jest/globals';
2
2
  import { AIMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';
3
3
  import type { BaseMessage } from '@langchain/core/messages';
4
- import { HookRegistry } from '@/hooks/HookRegistry';
5
- import { Providers, GraphEvents, StepTypes } from '@/common';
6
- import { HandlerRegistry } from '@/events';
7
- import { AgentContext } from '@/agents/AgentContext';
8
4
  import type {
9
5
  AgentInputs,
10
6
  ResolvedSubagentConfig,
@@ -12,6 +8,7 @@ import type {
12
8
  ToolExecuteBatchRequest,
13
9
  ToolExecuteResult,
14
10
  } from '@/types';
11
+ import type { StandardGraph } from '@/graphs/Graph';
15
12
  import {
16
13
  SubagentExecutor,
17
14
  filterSubagentResult,
@@ -20,7 +17,10 @@ import {
20
17
  summarizeEvent,
21
18
  } from '../subagent';
22
19
  import { sanitizeForwardedSubagentUpdateData } from '../subagent/SubagentExecutor';
23
- import type { StandardGraph } from '@/graphs/Graph';
20
+ import { Providers, GraphEvents, StepTypes } from '@/common';
21
+ import { AgentContext } from '@/agents/AgentContext';
22
+ import { HookRegistry } from '@/hooks/HookRegistry';
23
+ import { HandlerRegistry } from '@/events';
24
24
 
25
25
  jest.setTimeout(15000);
26
26
 
@@ -635,7 +635,10 @@ describe('SubagentExecutor', () => {
635
635
 
636
636
  const invokeConfig = getInvokeConfig();
637
637
  expect(invokeConfig).toBeDefined();
638
- const configurable = invokeConfig!.configurable as Record<string, unknown>;
638
+ const configurable = invokeConfig!.configurable as Record<
639
+ string,
640
+ unknown
641
+ >;
639
642
  expect(configurable.requestBody).toEqual({
640
643
  messageId: 'msg-123',
641
644
  conversationId: 'conv-456',
@@ -1183,11 +1186,13 @@ describe('SubagentExecutor', () => {
1183
1186
  handle: (_event, rawData): void => {
1184
1187
  const request = rawData as ToolExecuteBatchRequest;
1185
1188
  toolRequests.push(request);
1186
- const results: ToolExecuteResult[] = request.toolCalls.map((call) => ({
1187
- toolCallId: call.id,
1188
- status: 'success',
1189
- content: `ran ${call.name}`,
1190
- }));
1189
+ const results: ToolExecuteResult[] = request.toolCalls.map(
1190
+ (call) => ({
1191
+ toolCallId: call.id,
1192
+ status: 'success',
1193
+ content: `ran ${call.name}`,
1194
+ })
1195
+ );
1191
1196
  request.resolve(results);
1192
1197
  },
1193
1198
  });
@@ -1268,8 +1273,7 @@ describe('SubagentExecutor', () => {
1268
1273
 
1269
1274
  const toolUpdate = subagentUpdates.find(
1270
1275
  (update) =>
1271
- update.phase === 'run_step' &&
1272
- update.label === 'Calling calculator'
1276
+ update.phase === 'run_step' && update.label === 'Calling calculator'
1273
1277
  );
1274
1278
  expect(toolUpdate?.data).toEqual({
1275
1279
  agentId: 'researcher',
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from '@jest/globals';
2
- import { Constants } from '@/common';
2
+ import type { SubagentConfig } from '@/types';
3
3
  import {
4
4
  SubagentToolName,
5
5
  SubagentToolDescription,
@@ -8,7 +8,7 @@ import {
8
8
  createSubagentToolDefinition,
9
9
  buildSubagentToolParams,
10
10
  } from '../SubagentTool';
11
- import type { SubagentConfig } from '@/types';
11
+ import { Constants } from '@/common';
12
12
 
13
13
  describe('SubagentTool', () => {
14
14
  describe('schema structure', () => {
@@ -5,9 +5,9 @@ import { describe, it, expect, jest, afterEach } from '@jest/globals';
5
5
  import type { StructuredToolInterface } from '@langchain/core/tools';
6
6
  import type { PreToolUseHookInput, PreToolUseHookOutput } from '@/hooks';
7
7
  import type * as t from '@/types';
8
+ import * as events from '@/utils/events';
8
9
  import { GraphEvents } from '@/common';
9
10
  import { HookRegistry } from '@/hooks';
10
- import * as events from '@/utils/events';
11
11
  import { ToolNode } from '../ToolNode';
12
12
 
13
13
  function createDummyTool(name: string): StructuredToolInterface {
@@ -3,15 +3,12 @@ import { tool } from '@langchain/core/tools';
3
3
  import { AIMessage, ToolMessage } from '@langchain/core/messages';
4
4
  import { describe, it, expect, jest, afterEach } from '@jest/globals';
5
5
  import type { StructuredToolInterface } from '@langchain/core/tools';
6
+ import type { PostToolUseHookOutput, PreToolUseHookOutput } from '@/hooks';
6
7
  import type * as t from '@/types';
8
+ import { ToolOutputReferenceRegistry } from '../toolOutputReferences';
7
9
  import * as events from '@/utils/events';
8
- import type {
9
- PostToolUseHookOutput,
10
- PreToolUseHookOutput,
11
- } from '@/hooks';
12
10
  import { HookRegistry } from '@/hooks';
13
11
  import { ToolNode } from '../ToolNode';
14
- import { ToolOutputReferenceRegistry } from '../toolOutputReferences';
15
12
 
16
13
  /**
17
14
  * Reads the lazy ref-metadata stamped onto a `ToolMessage` by ToolNode.
@@ -4,9 +4,9 @@ import { AIMessage } from '@langchain/core/messages';
4
4
  import { describe, it, expect, jest, afterEach } from '@jest/globals';
5
5
  import type { StructuredToolInterface } from '@langchain/core/tools';
6
6
  import type * as t from '@/types';
7
+ import * as events from '@/utils/events';
7
8
  import { ToolNode } from '../ToolNode';
8
9
  import { Constants } from '@/common';
9
- import * as events from '@/utils/events';
10
10
 
11
11
  /**
12
12
  * Creates a mock execute_code tool that captures the toolCall config it receives.
@@ -4,6 +4,7 @@
4
4
  * Tests helper functions and sanitization logic without hitting the API.
5
5
  */
6
6
  import { describe, it, expect } from '@jest/globals';
7
+ import type { ToolMetadata, LCToolRegistry } from '@/types';
7
8
  import {
8
9
  sanitizeRegex,
9
10
  escapeRegexSpecialChars,
@@ -20,7 +21,6 @@ import {
20
21
  getBaseToolName,
21
22
  formatServerListing,
22
23
  } from '../ToolSearch';
23
- import type { ToolMetadata, LCToolRegistry } from '@/types';
24
24
 
25
25
  describe('ToolSearch', () => {
26
26
  describe('escapeRegexSpecialChars', () => {
@@ -1,5 +1,5 @@
1
- import { AIMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';
2
1
  import { describe, it, expect } from '@jest/globals';
2
+ import { AIMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';
3
3
  import {
4
4
  annotateMessagesForLLM,
5
5
  ToolOutputReferenceRegistry,
@@ -1,5 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { tool } from '@langchain/core/tools';
3
+ import { AIMessage, ToolMessage } from '@langchain/core/messages';
4
+ import { describe, it, expect, jest, afterEach } from '@jest/globals';
3
5
  import {
4
6
  END,
5
7
  START,
@@ -9,11 +11,9 @@ import {
9
11
  MessagesAnnotation,
10
12
  Command,
11
13
  } from '@langchain/langgraph';
12
- import { AIMessage, ToolMessage } from '@langchain/core/messages';
13
- import { describe, it, expect, jest, afterEach } from '@jest/globals';
14
+ import type { Runnable, RunnableConfig } from '@langchain/core/runnables';
14
15
  import type { StructuredToolInterface } from '@langchain/core/tools';
15
16
  import type { BaseMessage } from '@langchain/core/messages';
16
- import type { Runnable, RunnableConfig } from '@langchain/core/runnables';
17
17
  import type { PreToolUseHookOutput } from '@/hooks';
18
18
  import type * as t from '@/types';
19
19
  import { HookRegistry } from '@/hooks';
@@ -60,7 +60,11 @@ function buildGraph(
60
60
  .addNode('agent', (): MessagesUpdate => {
61
61
  agentInvocations += 1;
62
62
  if (agentInvocations === 1) {
63
- return { messages: [aiCall(toolCalls[0].id, toolCalls[0].name, toolCalls[0].args)] };
63
+ return {
64
+ messages: [
65
+ aiCall(toolCalls[0].id, toolCalls[0].name, toolCalls[0].args),
66
+ ],
67
+ };
64
68
  }
65
69
  return { messages: [] };
66
70
  })
@@ -80,14 +84,11 @@ describe('direct-path HITL: resume scope', () => {
80
84
 
81
85
  it('re-executes the direct tool body on resume when interrupt() fires from the direct path', async () => {
82
86
  const sideEffect = jest.fn(() => 'EXECUTED');
83
- const directTool = tool(
84
- async () => sideEffect(),
85
- {
86
- name: 'echo',
87
- description: 'direct tool that records every body invocation',
88
- schema: z.object({ command: z.string().optional() }).passthrough(),
89
- }
90
- ) as unknown as StructuredToolInterface;
87
+ const directTool = tool(async () => sideEffect(), {
88
+ name: 'echo',
89
+ description: 'direct tool that records every body invocation',
90
+ schema: z.object({ command: z.string().optional() }).passthrough(),
91
+ }) as unknown as StructuredToolInterface;
91
92
 
92
93
  const registry = new HookRegistry();
93
94
  let hookInvocations = 0;
@@ -149,7 +150,9 @@ describe('direct-path HITL: resume scope', () => {
149
150
 
150
151
  // Result should carry the executed output.
151
152
  const messages = (second as { messages: ToolMessage[] }).messages;
152
- const toolMsg = messages.find((m) => m instanceof ToolMessage) as ToolMessage;
153
+ const toolMsg = messages.find(
154
+ (m) => m instanceof ToolMessage
155
+ ) as ToolMessage;
153
156
  expect(String(toolMsg.content)).toBe('EXECUTED');
154
157
  });
155
158
 
@@ -199,17 +202,20 @@ describe('direct-path HITL: resume scope', () => {
199
202
  });
200
203
 
201
204
  const builder = new StateGraph(MessagesAnnotation)
202
- .addNode('agent', (): MessagesUpdate => ({
203
- messages: [
204
- new AIMessage({
205
- content: '',
206
- tool_calls: [
207
- { id: 'a1', name: 'tool_a', args: {} },
208
- { id: 'b1', name: 'tool_b', args: {} },
209
- ],
210
- }),
211
- ],
212
- }))
205
+ .addNode(
206
+ 'agent',
207
+ (): MessagesUpdate => ({
208
+ messages: [
209
+ new AIMessage({
210
+ content: '',
211
+ tool_calls: [
212
+ { id: 'a1', name: 'tool_a', args: {} },
213
+ { id: 'b1', name: 'tool_b', args: {} },
214
+ ],
215
+ }),
216
+ ],
217
+ })
218
+ )
213
219
  .addNode('tools', node)
214
220
  .addEdge(START, 'agent')
215
221
  .addEdge('agent', 'tools')
@@ -311,14 +317,11 @@ describe('direct-path HITL: resume scope', () => {
311
317
  });
312
318
 
313
319
  it('fails closed when updatedInput is missing or wrong-shaped', async () => {
314
- const directTool = tool(
315
- async () => 'should-not-execute',
316
- {
317
- name: 'echo',
318
- description: 'must not execute on malformed edit',
319
- schema: z.object({ command: z.string().optional() }).passthrough(),
320
- }
321
- ) as unknown as StructuredToolInterface;
320
+ const directTool = tool(async () => 'should-not-execute', {
321
+ name: 'echo',
322
+ description: 'must not execute on malformed edit',
323
+ schema: z.object({ command: z.string().optional() }).passthrough(),
324
+ }) as unknown as StructuredToolInterface;
322
325
 
323
326
  const registry = new HookRegistry();
324
327
  registry.register('PreToolUse', {
@@ -147,6 +147,47 @@ describe('Direct-path lifecycle hooks (in-process tools)', () => {
147
147
  expect(String(message.content)).toBe('ran:ls');
148
148
  });
149
149
 
150
+ it('executingAgentId defaults to agentId for direct callers, and an explicit value wins', async () => {
151
+ const echo = createDirectTool('echo', (args) => `ran:${args.command}`);
152
+ const captured: Array<string | undefined> = [];
153
+ const registry = new HookRegistry();
154
+ registry.register('PreToolUse', {
155
+ hooks: [
156
+ async (input): Promise<PreToolUseHookOutput> => {
157
+ captured.push(input.executingAgentId);
158
+ return { decision: 'allow' };
159
+ },
160
+ ],
161
+ });
162
+
163
+ // Only the existing `agentId` option is passed → executingAgentId defaults to it.
164
+ const defaulted = new ToolNode({
165
+ tools: [echo],
166
+ eventDrivenMode: true,
167
+ hookRegistry: registry,
168
+ directToolNames: new Set(['echo']),
169
+ agentId: 'solo-agent',
170
+ });
171
+ await defaulted.invoke({
172
+ messages: [aiCall('call_def', 'echo', { command: 'a' })],
173
+ });
174
+
175
+ // An explicit executingAgentId overrides the agentId default.
176
+ const explicit = new ToolNode({
177
+ tools: [echo],
178
+ eventDrivenMode: true,
179
+ hookRegistry: registry,
180
+ directToolNames: new Set(['echo']),
181
+ agentId: 'solo-agent',
182
+ executingAgentId: 'owner-agent',
183
+ });
184
+ await explicit.invoke({
185
+ messages: [aiCall('call_exp', 'echo', { command: 'b' })],
186
+ });
187
+
188
+ expect(captured).toEqual(['solo-agent', 'owner-agent']);
189
+ });
190
+
150
191
  it('PreToolUse updatedInput rewrites the args before runTool sees them', async () => {
151
192
  const seen: Record<string, unknown>[] = [];
152
193
  const echo = createDirectTool('echo', (args) => {
@@ -1,14 +1,14 @@
1
1
  import { describe, it, expect, beforeEach, jest } from '@jest/globals';
2
2
  import type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';
3
- import type { StandardGraph } from '@/graphs';
4
3
  import type { AgentContext } from '@/agents/AgentContext';
4
+ import type { StandardGraph } from '@/graphs';
5
5
  import type * as t from '@/types';
6
- import { StepTypes, ToolCallTypes, Providers, GraphEvents } from '@/common';
7
6
  import {
8
7
  handleToolCallChunks,
9
8
  handleToolCalls,
10
9
  handleServerToolResult,
11
10
  } from '../handlers';
11
+ import { StepTypes, ToolCallTypes, Providers, GraphEvents } from '@/common';
12
12
 
13
13
  type MockGraph = {
14
14
  getStepKey: jest.Mock;
@@ -1,14 +1,5 @@
1
1
  import { z } from 'zod';
2
2
  import { tool } from '@langchain/core/tools';
3
- import {
4
- END,
5
- START,
6
- Command,
7
- StateGraph,
8
- MemorySaver,
9
- isInterrupted,
10
- MessagesAnnotation,
11
- } from '@langchain/langgraph';
12
3
  import { AIMessage, ToolMessage } from '@langchain/core/messages';
13
4
  import {
14
5
  describe,
@@ -18,9 +9,18 @@ import {
18
9
  afterEach,
19
10
  beforeEach,
20
11
  } from '@jest/globals';
12
+ import {
13
+ END,
14
+ START,
15
+ Command,
16
+ StateGraph,
17
+ MemorySaver,
18
+ isInterrupted,
19
+ MessagesAnnotation,
20
+ } from '@langchain/langgraph';
21
+ import type { Runnable, RunnableConfig } from '@langchain/core/runnables';
21
22
  import type { StructuredToolInterface } from '@langchain/core/tools';
22
23
  import type { BaseMessage } from '@langchain/core/messages';
23
- import type { Runnable, RunnableConfig } from '@langchain/core/runnables';
24
24
  import type {
25
25
  PreToolUseHookOutput,
26
26
  PostToolUseHookOutput,
@@ -32,9 +32,9 @@ import type {
32
32
  UserPromptSubmitHookOutput,
33
33
  } from '@/hooks';
34
34
  import type * as t from '@/types';
35
+ import { Providers as providers, GraphEvents } from '@/common';
35
36
  import * as events from '@/utils/events';
36
37
  import { HookRegistry } from '@/hooks';
37
- import { Providers as providers, GraphEvents } from '@/common';
38
38
  import { ToolNode } from '../ToolNode';
39
39
 
40
40
  async function flushAsyncWork(): Promise<void> {
@@ -1,9 +1,4 @@
1
1
  import { describe, it, expect } from '@jest/globals';
2
- import {
3
- Constants,
4
- LOCAL_CODING_BUNDLE_NAMES,
5
- LOCAL_CODING_TOOL_NAMES,
6
- } from '@/common';
7
2
  import {
8
3
  createLocalCodingTools,
9
4
  createLocalCodingToolDefinitions,
@@ -13,6 +8,11 @@ import {
13
8
  LocalListDirectoryToolName,
14
9
  LocalWriteFileToolName,
15
10
  } from '../local/LocalCodingTools';
11
+ import {
12
+ Constants,
13
+ LOCAL_CODING_BUNDLE_NAMES,
14
+ LOCAL_CODING_TOOL_NAMES,
15
+ } from '@/common';
16
16
  import { CompileCheckToolName } from '../local/CompileCheckTool';
17
17
 
18
18
  /**
@@ -45,7 +45,9 @@ describe('local coding tool names', () => {
45
45
  expect(Constants.BASH_TOOL).toBe('bash_tool');
46
46
  expect(Constants.EXECUTE_CODE).toBe('execute_code');
47
47
  expect(Constants.PROGRAMMATIC_TOOL_CALLING).toBe('run_tools_with_code');
48
- expect(Constants.BASH_PROGRAMMATIC_TOOL_CALLING).toBe('run_tools_with_bash');
48
+ expect(Constants.BASH_PROGRAMMATIC_TOOL_CALLING).toBe(
49
+ 'run_tools_with_bash'
50
+ );
49
51
  });
50
52
 
51
53
  it('LOCAL_CODING_BUNDLE_NAMES matches every name the bundle ships', () => {
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect } from '@jest/globals';
2
- import { formatSkillCatalog } from '../skillCatalog';
3
2
  import type { SkillCatalogEntry } from '@/types';
3
+ import { formatSkillCatalog } from '../skillCatalog';
4
4
 
5
5
  describe('formatSkillCatalog', () => {
6
6
  it('returns empty string for empty array', () => {
@@ -1,7 +1,6 @@
1
1
  import { HumanMessage } from '@langchain/core/messages';
2
2
  import { FakeListChatModel } from '@langchain/core/utils/testing';
3
3
  import type { ToolCall } from '@langchain/core/messages/tool';
4
- import type * as t from '@/types';
5
4
  import type {
6
5
  HookCallback,
7
6
  PermissionDeniedHookOutput,
@@ -12,9 +11,7 @@ import type {
12
11
  SubagentStopHookInput,
13
12
  SubagentStopHookOutput,
14
13
  } from '@/hooks/types';
15
- import { HookRegistry } from '@/hooks/HookRegistry';
16
- import { Run } from '@/run';
17
- import { FakeChatModel } from '@/llm/fake';
14
+ import type * as t from '@/types';
18
15
  import {
19
16
  Constants,
20
17
  GraphEvents,
@@ -22,7 +19,10 @@ import {
22
19
  ToolEndHandler,
23
20
  ModelEndHandler,
24
21
  } from '@/index';
22
+ import { HookRegistry } from '@/hooks/HookRegistry';
25
23
  import * as providers from '@/llm/providers';
24
+ import { FakeChatModel } from '@/llm/fake';
25
+ import { Run } from '@/run';
26
26
 
27
27
  const CHILD_RESPONSE = 'Hook test child response.';
28
28
 
@@ -275,9 +275,7 @@ describe('Subagent hook integration (end-to-end via Run)', () => {
275
275
  toolCalls: [createCalculatorToolCall()],
276
276
  });
277
277
  }
278
- bindTools(
279
- tools: unknown
280
- ): ReturnType<FakeChatModel['withConfig']> {
278
+ bindTools(tools: unknown): ReturnType<FakeChatModel['withConfig']> {
281
279
  const config = {
282
280
  tools,
283
281
  } as Parameters<FakeChatModel['withConfig']>[0];
@@ -292,11 +290,16 @@ describe('Subagent hook integration (end-to-end via Run)', () => {
292
290
  const registry = new HookRegistry();
293
291
  const preToolEvents: string[] = [];
294
292
  const postToolEvents: string[] = [];
293
+ // `executingAgentId` is always the owning agent (incl. top-level), unlike
294
+ // `agentId` which stays undefined outside a subagent scope.
295
+ const preExecEvents: string[] = [];
296
+ const postExecEvents: string[] = [];
295
297
 
296
298
  const preHook: HookCallback<'PreToolUse'> = async (
297
299
  input
298
300
  ): Promise<PreToolUseHookOutput> => {
299
301
  preToolEvents.push(`${input.agentId ?? '-'}:${input.toolName}`);
302
+ preExecEvents.push(`${input.executingAgentId ?? '-'}:${input.toolName}`);
300
303
  return { decision: 'allow' };
301
304
  };
302
305
  registry.register('PreToolUse', { hooks: [preHook] });
@@ -305,6 +308,7 @@ describe('Subagent hook integration (end-to-end via Run)', () => {
305
308
  input
306
309
  ): Promise<PostToolUseHookOutput> => {
307
310
  postToolEvents.push(`${input.agentId ?? '-'}:${input.toolName}`);
311
+ postExecEvents.push(`${input.executingAgentId ?? '-'}:${input.toolName}`);
308
312
  return {};
309
313
  };
310
314
  registry.register('PostToolUse', { hooks: [postHook] });
@@ -351,6 +355,14 @@ describe('Subagent hook integration (end-to-end via Run)', () => {
351
355
  expect(preToolEvents).toContain('researcher-child:calculator');
352
356
  expect(postToolEvents).toContain('-:subagent');
353
357
  expect(postToolEvents).toContain('researcher-child:calculator');
358
+
359
+ // `agentId` stays the subagent-scope marker (undefined at the top level),
360
+ // but `executingAgentId` attributes every batch to its owning agent — incl.
361
+ // the top-level parent, which a multi-agent host needs and `agentId` can't give.
362
+ expect(preExecEvents).toContain('hook-parent:subagent');
363
+ expect(preExecEvents).toContain('researcher-child:calculator');
364
+ expect(postExecEvents).toContain('hook-parent:subagent');
365
+ expect(postExecEvents).toContain('researcher-child:calculator');
354
366
  });
355
367
 
356
368
  it('child subagent tool ask hooks fail closed instead of starting unsupported nested HITL', async () => {
@@ -365,9 +377,7 @@ describe('Subagent hook integration (end-to-end via Run)', () => {
365
377
  toolCalls: [createCalculatorToolCall()],
366
378
  });
367
379
  }
368
- bindTools(
369
- tools: unknown
370
- ): ReturnType<FakeChatModel['withConfig']> {
380
+ bindTools(tools: unknown): ReturnType<FakeChatModel['withConfig']> {
371
381
  const config = {
372
382
  tools,
373
383
  } as Parameters<FakeChatModel['withConfig']>[0];
@@ -1,15 +1,22 @@
1
1
  import { tmpdir } from 'os';
2
2
  import { join } from 'path';
3
3
  import { mkdtemp, rm, writeFile } from 'fs/promises';
4
- import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
5
- import { Constants } from '@/common';
6
- import { createLocalCodingToolBundle } from '../local/LocalCodingTools';
4
+ import {
5
+ describe,
6
+ it,
7
+ expect,
8
+ beforeEach,
9
+ afterEach,
10
+ jest,
11
+ } from '@jest/globals';
12
+ import type { WorkspaceFS } from '../local/workspaceFS';
7
13
  import {
8
14
  resolveWorkspacePathSafe,
9
15
  getWorkspaceFS,
10
16
  } from '../local/LocalExecutionEngine';
17
+ import { createLocalCodingToolBundle } from '../local/LocalCodingTools';
11
18
  import { nodeWorkspaceFS } from '../local/workspaceFS';
12
- import type { WorkspaceFS } from '../local/workspaceFS';
19
+ import { Constants } from '@/common';
13
20
 
14
21
  describe('workspace seam', () => {
15
22
  let workspace: string;
@@ -97,10 +104,14 @@ describe('workspace seam', () => {
97
104
  // the real Node impl so the tool actually completes; the spy just
98
105
  // proves the seam.
99
106
  const tracked: WorkspaceFS = {
100
- readFile: jest.fn(nodeWorkspaceFS.readFile) as unknown as WorkspaceFS['readFile'],
107
+ readFile: jest.fn(
108
+ nodeWorkspaceFS.readFile
109
+ ) as unknown as WorkspaceFS['readFile'],
101
110
  writeFile: jest.fn(nodeWorkspaceFS.writeFile),
102
111
  stat: jest.fn(nodeWorkspaceFS.stat),
103
- readdir: jest.fn(nodeWorkspaceFS.readdir) as unknown as WorkspaceFS['readdir'],
112
+ readdir: jest.fn(
113
+ nodeWorkspaceFS.readdir
114
+ ) as unknown as WorkspaceFS['readdir'],
104
115
  mkdir: jest.fn(nodeWorkspaceFS.mkdir),
105
116
  realpath: jest.fn(nodeWorkspaceFS.realpath),
106
117
  unlink: jest.fn(nodeWorkspaceFS.unlink),
@@ -120,7 +131,9 @@ describe('workspace seam', () => {
120
131
  expect(tracked.writeFile).toHaveBeenCalled();
121
132
  expect(tracked.mkdir).toHaveBeenCalled();
122
133
 
123
- const readTool = bundle.tools.find((t) => t.name === Constants.READ_FILE)!;
134
+ const readTool = bundle.tools.find(
135
+ (t) => t.name === Constants.READ_FILE
136
+ )!;
124
137
  await readTool.invoke({
125
138
  id: 'c2',
126
139
  name: Constants.READ_FILE,
@@ -1,15 +1,15 @@
1
- import { EventEmitter } from 'events';
2
1
  import { PassThrough } from 'stream';
3
2
  import { posix as path } from 'path';
4
- import type { ChildProcessWithoutNullStreams } from 'child_process';
3
+ import { EventEmitter } from 'events';
5
4
  import type { WriteFileOptions, MakeDirectoryOptions, Stats } from 'fs';
5
+ import type { ChildProcessWithoutNullStreams } from 'child_process';
6
6
  import type { FileHandle } from 'fs/promises';
7
+ import type { WorkspaceFS, ReaddirEntry } from '@/tools/local/workspaceFS';
7
8
  import type * as t from '@/types';
8
9
  import {
9
10
  LOCAL_SPAWN_TIMEOUT_MS,
10
11
  validateBashCommand,
11
12
  } from '@/tools/local/LocalExecutionEngine';
12
- import type { WorkspaceFS, ReaddirEntry } from '@/tools/local/workspaceFS';
13
13
 
14
14
  const DEFAULT_WORKSPACE_ROOT = '/workspace';
15
15
  const DEFAULT_TIMEOUT_MS = 60000;
@@ -403,6 +403,9 @@ function createCloudflareSpawn(
403
403
  const child = new EventEmitter() as ChildProcessWithoutNullStreams;
404
404
  const abortController = new AbortController();
405
405
  const state = { closed: false };
406
+ /** Read through a function so concurrent `closeOnce` mutation (via
407
+ * `kill()`/abort during an `await`) isn't statically narrowed away. */
408
+ const isClosed = (): boolean => state.closed;
406
409
  const closeOnce = (
407
410
  exitCode: number | null,
408
411
  signal: NodeJS.Signals | null
@@ -451,7 +454,7 @@ function createCloudflareSpawn(
451
454
  const timedCommand = withInSandboxTimeout(rendered, timeoutMs);
452
455
  const cwd =
453
456
  options.cwd == null ? ctx.workspaceRoot : options.cwd.toString();
454
- if (state.closed) {
457
+ if (isClosed()) {
455
458
  return;
456
459
  }
457
460
  const execOptions: t.CloudflareSandboxExecOptions = {
@@ -464,14 +467,14 @@ function createCloudflareSpawn(
464
467
  }
465
468
  try {
466
469
  const result = await ctx.sandbox.exec(timedCommand, execOptions);
467
- if (state.closed) {
470
+ if (isClosed()) {
468
471
  return;
469
472
  }
470
473
  if (result.stdout) stdout.write(result.stdout);
471
474
  if (result.stderr) stderr.write(result.stderr);
472
475
  closeOnce(result.exitCode, null);
473
476
  } catch (error) {
474
- if (state.closed) {
477
+ if (isClosed()) {
475
478
  return;
476
479
  }
477
480
  stderr.write((error as Error).message);