@librechat/agents 3.2.32 → 3.2.33

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 (573) 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 +195 -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 +105 -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 +398 -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 +991 -1276
  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 +427 -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 +856 -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 +2109 -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 +19 -36
  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 +195 -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 +105 -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 +397 -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 +991 -1271
  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 +427 -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 +854 -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 +2107 -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 +19 -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/tools/search/tool.d.ts +17 -0
  545. package/dist/types/tools/search/types.d.ts +4 -0
  546. package/package.json +4 -10
  547. package/src/tools/search/highlights.ts +9 -1
  548. package/src/tools/search/search.ts +41 -3
  549. package/src/tools/search/source-processing.test.ts +373 -0
  550. package/src/tools/search/tool.ts +22 -2
  551. package/src/tools/search/types.ts +4 -0
  552. package/dist/cjs/langchain/google-common.cjs.map +0 -1
  553. package/dist/cjs/langchain/index.cjs.map +0 -1
  554. package/dist/cjs/langchain/language_models/chat_models.cjs.map +0 -1
  555. package/dist/cjs/langchain/messages/tool.cjs.map +0 -1
  556. package/dist/cjs/langchain/messages.cjs.map +0 -1
  557. package/dist/cjs/langchain/openai.cjs.map +0 -1
  558. package/dist/cjs/langchain/prompts.cjs.map +0 -1
  559. package/dist/cjs/langchain/runnables.cjs.map +0 -1
  560. package/dist/cjs/langchain/tools.cjs.map +0 -1
  561. package/dist/cjs/langchain/utils/env.cjs.map +0 -1
  562. package/dist/cjs/main.cjs.map +0 -1
  563. package/dist/esm/langchain/google-common.mjs.map +0 -1
  564. package/dist/esm/langchain/index.mjs.map +0 -1
  565. package/dist/esm/langchain/language_models/chat_models.mjs.map +0 -1
  566. package/dist/esm/langchain/messages/tool.mjs.map +0 -1
  567. package/dist/esm/langchain/messages.mjs.map +0 -1
  568. package/dist/esm/langchain/openai.mjs.map +0 -1
  569. package/dist/esm/langchain/prompts.mjs.map +0 -1
  570. package/dist/esm/langchain/runnables.mjs.map +0 -1
  571. package/dist/esm/langchain/tools.mjs.map +0 -1
  572. package/dist/esm/langchain/utils/env.mjs.map +0 -1
  573. package/dist/esm/main.mjs.map +0 -1
package/dist/esm/run.mjs CHANGED
@@ -1,1126 +1,918 @@
1
- import { HumanMessage } from '@langchain/core/messages';
2
- import { PromptTemplate } from '@langchain/core/prompts';
3
- import { RunnableLambda } from '@langchain/core/runnables';
4
- import { ChatOpenAI as ChatOpenAI$1, AzureChatOpenAI as AzureChatOpenAI$1 } from '@langchain/openai';
5
- import { BaseCallbackHandler } from '@langchain/core/callbacks/base';
6
- import { MemorySaver, Command, isInterrupted, INTERRUPT } from '@langchain/langgraph';
7
- import { createLangfuseTraceMetadata, getLangfuseTraceName, createLangfuseHandler, withLangfuseAttributes, disposeLangfuseHandler, isLangfuseCallbackHandler } from './langfuse.mjs';
8
- import { resolveLangfuseConfig, withLangfuseToolOutputTracingConfig } from './langfuseToolOutputTracing.mjs';
9
- import { appendCallbacks, findCallback } from './utils/callbacks.mjs';
10
- import { createCompletionTitleRunnable, createTitleRunnable } from './utils/title.mjs';
11
- import { initializeLangfuseTracing, runWithTraceIdSeed } from './instrumentation.mjs';
12
- import { createTokenCounter, encodingForModel } from './utils/tokens.mjs';
13
- import { GraphEvents, Callback, TitleMethod } from './common/enum.mjs';
14
- import { MultiAgentGraph } from './graphs/MultiAgentGraph.mjs';
15
- import { StandardGraph } from './graphs/Graph.mjs';
16
- import { initializeModel } from './llm/init.mjs';
17
- import { HandlerRegistry } from './events.mjs';
18
- import { isOpenAILike } from './utils/llm.mjs';
19
- import { executeHooks } from './hooks/executeHooks.mjs';
20
- import './hooks/createWorkspacePolicyHook.mjs';
21
-
22
- // src/run.ts
1
+ import { createLangfuseHandler, createLangfuseTraceMetadata, disposeLangfuseHandler, getLangfuseTraceName, isLangfuseCallbackHandler, withLangfuseAttributes } from "./langfuse.mjs";
2
+ import { resolveLangfuseConfig, withLangfuseToolOutputTracingConfig } from "./langfuseToolOutputTracing.mjs";
3
+ import { appendCallbacks, findCallback } from "./utils/callbacks.mjs";
4
+ import "./common/enum.mjs";
5
+ import "./common/index.mjs";
6
+ import { createCompletionTitleRunnable, createTitleRunnable } from "./utils/title.mjs";
7
+ import { initializeLangfuseTracing, runWithTraceIdSeed } from "./instrumentation.mjs";
8
+ import { createTokenCounter, encodingForModel } from "./utils/tokens.mjs";
9
+ import { isOpenAILike } from "./utils/llm.mjs";
10
+ import { HandlerRegistry } from "./events.mjs";
11
+ import { executeHooks } from "./hooks/executeHooks.mjs";
12
+ import "./hooks/index.mjs";
13
+ import { initializeModel } from "./llm/init.mjs";
14
+ import { StandardGraph } from "./graphs/Graph.mjs";
15
+ import { MultiAgentGraph } from "./graphs/MultiAgentGraph.mjs";
16
+ import { HumanMessage } from "@langchain/core/messages";
17
+ import { PromptTemplate } from "@langchain/core/prompts";
18
+ import { RunnableLambda } from "@langchain/core/runnables";
19
+ import { AzureChatOpenAI, ChatOpenAI } from "@langchain/openai";
20
+ import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
21
+ import { Command, INTERRUPT, MemorySaver, isInterrupted } from "@langchain/langgraph";
22
+ //#region src/run.ts
23
23
  const defaultOmitOptions = new Set([
24
- 'stream',
25
- 'thinking',
26
- 'streaming',
27
- 'maxTokens',
28
- 'clientOptions',
29
- 'thinkingConfig',
30
- 'thinkingBudget',
31
- 'includeThoughts',
32
- 'maxOutputTokens',
33
- 'additionalModelRequestFields',
24
+ "stream",
25
+ "thinking",
26
+ "streaming",
27
+ "maxTokens",
28
+ "clientOptions",
29
+ "thinkingConfig",
30
+ "thinkingBudget",
31
+ "includeThoughts",
32
+ "maxOutputTokens",
33
+ "additionalModelRequestFields"
34
34
  ]);
35
35
  const CUSTOM_GRAPH_EVENTS = new Set([
36
- GraphEvents.ON_AGENT_UPDATE,
37
- GraphEvents.ON_RUN_STEP,
38
- GraphEvents.ON_RUN_STEP_DELTA,
39
- GraphEvents.ON_RUN_STEP_COMPLETED,
40
- GraphEvents.ON_MESSAGE_DELTA,
41
- GraphEvents.ON_REASONING_DELTA,
42
- GraphEvents.ON_TOOL_EXECUTE,
43
- GraphEvents.ON_SUMMARIZE_START,
44
- GraphEvents.ON_SUMMARIZE_DELTA,
45
- GraphEvents.ON_SUMMARIZE_COMPLETE,
46
- GraphEvents.ON_SUBAGENT_UPDATE,
47
- GraphEvents.ON_AGENT_LOG,
48
- GraphEvents.ON_CUSTOM_EVENT,
36
+ "on_agent_update",
37
+ "on_run_step",
38
+ "on_run_step_delta",
39
+ "on_run_step_completed",
40
+ "on_message_delta",
41
+ "on_reasoning_delta",
42
+ "on_tool_execute",
43
+ "on_summarize_start",
44
+ "on_summarize_delta",
45
+ "on_summarize_complete",
46
+ "on_subagent_update",
47
+ "on_agent_log",
48
+ "on_custom_event"
49
49
  ]);
50
50
  const DIRECT_DISPATCHED_STEP_EVENTS = new Set([
51
- GraphEvents.ON_RUN_STEP,
52
- GraphEvents.ON_RUN_STEP_DELTA,
53
- GraphEvents.ON_MESSAGE_DELTA,
54
- GraphEvents.ON_REASONING_DELTA,
51
+ "on_run_step",
52
+ "on_run_step_delta",
53
+ "on_message_delta",
54
+ "on_reasoning_delta"
55
55
  ]);
56
56
  function getStepScopedEventId(data) {
57
- if (data == null || typeof data !== 'object') {
58
- return undefined;
59
- }
60
- const candidate = data;
61
- return typeof candidate.id === 'string' ? candidate.id : undefined;
57
+ if (data == null || typeof data !== "object") return;
58
+ const candidate = data;
59
+ return typeof candidate.id === "string" ? candidate.id : void 0;
62
60
  }
63
61
  function isLangGraphResumeMapForInterrupt(value, interruptId) {
64
- if (value === null || typeof value !== 'object' || Array.isArray(value)) {
65
- return false;
66
- }
67
- return Object.prototype.hasOwnProperty.call(value, interruptId);
68
- }
69
- class Run {
70
- id;
71
- tokenCounter;
72
- handlerRegistry;
73
- hookRegistry;
74
- humanInTheLoop;
75
- langfuse;
76
- toolOutputReferences;
77
- eagerEventToolExecution;
78
- toolExecution;
79
- indexTokenCountMap;
80
- calibrationRatio = 1;
81
- graphRunnable;
82
- Graph;
83
- returnContent = false;
84
- skipCleanup = false;
85
- _streamResult;
86
- /**
87
- * Captured interrupt payload typed as `unknown` because the SDK
88
- * does not validate the runtime shape — custom graph nodes can
89
- * raise interrupts with arbitrary payloads (not just the SDK's
90
- * `HumanInterruptPayload` union). The public `getInterrupt<T>()`
91
- * lets callers assert the type they expect.
92
- */
93
- _interrupt;
94
- _haltedReason;
95
- constructor(config) {
96
- const runId = config.runId ?? '';
97
- if (!runId) {
98
- throw new Error('Run ID not provided');
99
- }
100
- this.id = runId;
101
- this.tokenCounter = config.tokenCounter;
102
- this.indexTokenCountMap = config.indexTokenCountMap;
103
- if (config.calibrationRatio != null && config.calibrationRatio > 0) {
104
- this.calibrationRatio = config.calibrationRatio;
105
- }
106
- const handlerRegistry = new HandlerRegistry();
107
- if (config.customHandlers) {
108
- for (const [eventType, handler] of Object.entries(config.customHandlers)) {
109
- handlerRegistry.register(eventType, handler);
110
- }
111
- }
112
- this.handlerRegistry = handlerRegistry;
113
- this.hookRegistry = config.hooks;
114
- this.humanInTheLoop = config.humanInTheLoop;
115
- this.langfuse = config.langfuse;
116
- this.toolOutputReferences = config.toolOutputReferences;
117
- this.eagerEventToolExecution = config.eagerEventToolExecution;
118
- this.toolExecution = config.toolExecution;
119
- if (!config.graphConfig) {
120
- throw new Error('Graph config not provided');
121
- }
122
- /** Handle different graph types */
123
- if (config.graphConfig.type === 'multi-agent') {
124
- this.graphRunnable = this.createMultiAgentGraph(config.graphConfig);
125
- if (this.Graph) {
126
- this.Graph.handlerRegistry = handlerRegistry;
127
- }
128
- }
129
- else {
130
- /** Default to legacy graph for 'standard' or undefined type */
131
- this.graphRunnable = this.createLegacyGraph(config.graphConfig);
132
- if (this.Graph) {
133
- this.Graph.compileOptions =
134
- config.graphConfig.compileOptions ?? this.Graph.compileOptions;
135
- this.Graph.handlerRegistry = handlerRegistry;
136
- }
137
- }
138
- if (config.initialSessions && this.Graph) {
139
- for (const [key, value] of config.initialSessions) {
140
- this.Graph.sessions.set(key, value);
141
- }
142
- }
143
- this.returnContent = config.returnContent ?? false;
144
- this.skipCleanup = config.skipCleanup ?? false;
145
- }
146
- createLegacyGraph(config) {
147
- let agentConfig;
148
- let signal;
149
- /** Check if this is a multi-agent style config (has agents array) */
150
- if ('agents' in config && Array.isArray(config.agents)) {
151
- if (config.agents.length === 0) {
152
- throw new Error('At least one agent must be provided');
153
- }
154
- agentConfig = config.agents[0];
155
- signal = config.signal;
156
- }
157
- else {
158
- /** Legacy path: build agent config from llmConfig */
159
- const { type: _type, llmConfig, signal: legacySignal, tools = [], ...agentInputs } = config;
160
- const { provider, ...clientOptions } = llmConfig;
161
- agentConfig = {
162
- ...agentInputs,
163
- tools,
164
- provider,
165
- clientOptions,
166
- agentId: 'default',
167
- };
168
- signal = legacySignal;
169
- }
170
- const standardGraph = new StandardGraph({
171
- signal,
172
- runId: this.id,
173
- agents: [agentConfig],
174
- langfuse: this.langfuse,
175
- tokenCounter: this.tokenCounter,
176
- indexTokenCountMap: this.indexTokenCountMap,
177
- calibrationRatio: this.calibrationRatio,
178
- });
179
- /** Propagate compile options from graph config */
180
- standardGraph.compileOptions = this.applyHITLCheckpointerFallback(config.compileOptions);
181
- standardGraph.hookRegistry = this.hookRegistry;
182
- standardGraph.humanInTheLoop = this.humanInTheLoop;
183
- standardGraph.toolOutputReferences = this.toolOutputReferences;
184
- standardGraph.eagerEventToolExecution = this.eagerEventToolExecution;
185
- standardGraph.toolExecution = this.toolExecution;
186
- this.Graph = standardGraph;
187
- return standardGraph.createWorkflow();
188
- }
189
- createMultiAgentGraph(config) {
190
- const { agents, edges, compileOptions } = config;
191
- const multiAgentGraph = new MultiAgentGraph({
192
- runId: this.id,
193
- agents,
194
- edges,
195
- langfuse: this.langfuse,
196
- tokenCounter: this.tokenCounter,
197
- indexTokenCountMap: this.indexTokenCountMap,
198
- calibrationRatio: this.calibrationRatio,
199
- });
200
- multiAgentGraph.compileOptions =
201
- this.applyHITLCheckpointerFallback(compileOptions);
202
- multiAgentGraph.hookRegistry = this.hookRegistry;
203
- multiAgentGraph.humanInTheLoop = this.humanInTheLoop;
204
- multiAgentGraph.toolOutputReferences = this.toolOutputReferences;
205
- multiAgentGraph.eagerEventToolExecution = this.eagerEventToolExecution;
206
- multiAgentGraph.toolExecution = this.toolExecution;
207
- this.Graph = multiAgentGraph;
208
- return multiAgentGraph.createWorkflow();
209
- }
210
- /**
211
- * When the host opted into HITL via `humanInTheLoop: { enabled: true }`
212
- * and did not supply a checkpointer, install an in-memory `MemorySaver`
213
- * so `interrupt()` can persist checkpoints and `Command({ resume })`
214
- * can rebuild state. The fallback is intentionally process-local:
215
- * production hosts that need durable resumption across processes /
216
- * restarts must provide their own checkpointer (Redis, Postgres, etc.)
217
- * on `compileOptions.checkpointer`.
218
- *
219
- * No-op when HITL is off (the default — omitted, or
220
- * `{ enabled: false }`) or the host already supplied a checkpointer
221
- * of their own. See `HumanInTheLoopConfig` JSDoc for the rationale
222
- * behind the default-off stance.
223
- */
224
- applyHITLCheckpointerFallback(compileOptions) {
225
- if (this.humanInTheLoop?.enabled !== true) {
226
- return compileOptions;
227
- }
228
- if (compileOptions?.checkpointer != null) {
229
- return compileOptions;
230
- }
231
- return {
232
- ...(compileOptions ?? {}),
233
- checkpointer: new MemorySaver(),
234
- };
235
- }
236
- /**
237
- * Run RunStart + UserPromptSubmit hooks before the graph stream
238
- * begins, accumulate any `additionalContext` strings into the input
239
- * messages, and short-circuit when a hook signals the run should not
240
- * proceed (deny / ask decision on the prompt, or `preventContinuation`
241
- * on either hook).
242
- *
243
- * Returns `true` when the caller should bail with `undefined` (run
244
- * was halted before any model call); returns `false` to proceed
245
- * into the stream loop.
246
- *
247
- * ## Side effects
248
- *
249
- * On the success path:
250
- * - Mutates `stateInputs.messages` in place to append a
251
- * consolidated `HumanMessage` carrying any hook
252
- * `additionalContext` strings. Safe because the host owns the
253
- * array and `processStream` is the only consumer until LangGraph
254
- * reads it.
255
- *
256
- * On the halt path (returning `true`):
257
- * - Sets `this._haltedReason` so callers (and the eventual host)
258
- * can distinguish a hook-driven halt from a natural completion.
259
- * - Calls `registry.clearSession(this.id)` and
260
- * `registry.clearHaltSignal(this.id)` because no resume is
261
- * expected from a pre-stream halt — the run never entered the
262
- * graph, so the session/halt state for this run would otherwise
263
- * leak to the next `processStream` invocation on the same
264
- * registry. Other concurrent runs on the same registry are
265
- * untouched (halt signals are scoped per session id).
266
- * - Sets `config.callbacks = undefined` to drop the callback
267
- * references the caller built (langfuse handler, custom event
268
- * handler, etc.) since they won't be exercised. Mirrors the
269
- * equivalent cleanup the `processStream` `finally` block does
270
- * on the natural-completion path.
271
- */
272
- async runPreStreamHooks(stateInputs, threadId, config) {
273
- const registry = this.hookRegistry;
274
- /**
275
- * Defensive guard: `processStream` already validated `this.Graph`
276
- * before calling this helper, but TypeScript can't propagate that
277
- * narrowing across method boundaries. The check keeps the body
278
- * free of `this.Graph!` non-null assertions.
279
- */
280
- if (registry == null || this.Graph == null) {
281
- return false;
282
- }
283
- const preStreamContexts = [];
284
- const runStartResult = await executeHooks({
285
- registry,
286
- input: {
287
- hook_event_name: 'RunStart',
288
- runId: this.id,
289
- threadId,
290
- agentId: this.Graph.defaultAgentId,
291
- messages: stateInputs.messages,
292
- },
293
- sessionId: this.id,
294
- });
295
- for (const ctx of runStartResult.additionalContexts) {
296
- preStreamContexts.push(ctx);
297
- }
298
- /**
299
- * Honor `preventContinuation` from RunStart before the stream
300
- * starts. Mid-flight halts (from tool/compact/subagent hooks)
301
- * route through `HookRegistry.haltRun` and are polled by the
302
- * stream loop in `processStream` — different mechanism, same
303
- * intent.
304
- */
305
- if (runStartResult.preventContinuation === true) {
306
- this._haltedReason = runStartResult.stopReason ?? 'preventContinuation';
307
- registry.clearSession(this.id);
308
- registry.clearHaltSignal(this.id);
309
- config.callbacks = undefined;
310
- return true;
311
- }
312
- const lastHuman = findLastMessageOfType(stateInputs.messages, 'human');
313
- if (lastHuman != null) {
314
- const promptResult = await executeHooks({
315
- registry,
316
- input: {
317
- hook_event_name: 'UserPromptSubmit',
318
- runId: this.id,
319
- threadId,
320
- agentId: this.Graph.defaultAgentId,
321
- prompt: extractPromptText(lastHuman),
322
- // attachments: not yet wired — Phase 2 will extract
323
- // non-text content blocks (images, files) from messages
324
- },
325
- sessionId: this.id,
326
- });
327
- if (promptResult.decision === 'deny' ||
328
- promptResult.decision === 'ask' ||
329
- promptResult.preventContinuation === true) {
330
- /**
331
- * Always set `_haltedReason` so the host can call
332
- * `getHaltReason()` and distinguish a hook-blocked prompt
333
- * from a natural empty-output completion. Three signals can
334
- * land here, each with its own canonical reason string when
335
- * the hook didn't supply one.
336
- */
337
- if (promptResult.preventContinuation === true) {
338
- this._haltedReason = promptResult.stopReason ?? 'preventContinuation';
339
- }
340
- else if (promptResult.decision === 'deny') {
341
- this._haltedReason = promptResult.reason ?? 'prompt_denied';
342
- }
343
- else {
344
- this._haltedReason =
345
- promptResult.reason ?? 'prompt_requires_approval';
346
- }
347
- registry.clearSession(this.id);
348
- registry.clearHaltSignal(this.id);
349
- config.callbacks = undefined;
350
- return true;
351
- }
352
- for (const ctx of promptResult.additionalContexts) {
353
- preStreamContexts.push(ctx);
354
- }
355
- }
356
- if (preStreamContexts.length > 0) {
357
- /**
358
- * Wraps the joined hook contexts as a `HumanMessage` even though
359
- * the intent is system-level guidance. Using a `SystemMessage`
360
- * mid-conversation is rejected by Anthropic and Google providers
361
- * (system messages must be the leading entry), so the LangChain
362
- * convention — also used by `ToolNode.convertInjectedMessages`
363
- * — is `HumanMessage` carrying `additional_kwargs.role` as a
364
- * marker for hosts inspecting state. The model still sees a
365
- * user-role message; the `role: 'system'` field is metadata
366
- * only. Hosts that want a true system message should compose
367
- * it into the agent's `instructions` config instead.
368
- */
369
- stateInputs.messages.push(new HumanMessage({
370
- content: preStreamContexts.join('\n\n'),
371
- additional_kwargs: { role: 'system', source: 'hook' },
372
- }));
373
- }
374
- return false;
375
- }
376
- static async create(config) {
377
- /** Create tokenCounter if indexTokenCountMap is provided but tokenCounter is not */
378
- if (config.indexTokenCountMap && !config.tokenCounter) {
379
- const gc = config.graphConfig;
380
- const clientOpts = 'agents' in gc ? gc.agents[0]?.clientOptions : gc.clientOptions;
381
- const model = clientOpts?.model ?? '';
382
- config.tokenCounter = await createTokenCounter(encodingForModel(model));
383
- }
384
- return new Run(config);
385
- }
386
- getRunMessages() {
387
- if (!this.Graph) {
388
- throw new Error('Graph not initialized. Make sure to use Run.create() to instantiate the Run.');
389
- }
390
- return this.Graph.getRunMessages();
391
- }
392
- /**
393
- * Returns the current calibration ratio (EMA of provider-vs-estimate token ratios).
394
- * Hosts should persist this value and pass it back as `RunConfig.calibrationRatio`
395
- * on the next run for the same conversation so the pruner starts with an accurate
396
- * scaling factor instead of the default (1).
397
- */
398
- getCalibrationRatio() {
399
- return this.calibrationRatio;
400
- }
401
- getResolvedInstructionOverhead() {
402
- return this.Graph?.getResolvedInstructionOverhead();
403
- }
404
- getToolCount() {
405
- return this.Graph?.getToolCount() ?? 0;
406
- }
407
- /**
408
- * Creates a custom event callback handler that intercepts custom events
409
- * and processes them through our handler registry instead of EventStreamCallbackHandler
410
- */
411
- createCustomEventCallback() {
412
- return async (eventName, data, runId, tags, metadata) => {
413
- // Step-scoped SDK events are dispatched directly via the handler
414
- // registry first. Skip callback-based echoes to prevent double
415
- // handling when LangGraph invokes custom callbacks more than once.
416
- const stepScopedEventId = getStepScopedEventId(data);
417
- if (DIRECT_DISPATCHED_STEP_EVENTS.has(eventName) &&
418
- this.Graph != null &&
419
- stepScopedEventId != null &&
420
- this.Graph.hasHandlerDispatchedEvent(eventName, stepScopedEventId)) {
421
- return;
422
- }
423
- const handler = this.handlerRegistry?.getHandler(eventName);
424
- if (handler && this.Graph) {
425
- return await handler.handle(eventName, data, metadata, this.Graph);
426
- }
427
- };
428
- }
429
- shouldClearHookSession(streamThrew) {
430
- return this._interrupt == null || this._haltedReason != null || streamThrew;
431
- }
432
- isAwaitingResume(streamThrew) {
433
- return (this._interrupt != null && this._haltedReason == null && !streamThrew);
434
- }
435
- getStreamLangfuseConfig(graph) {
436
- const primaryContext = graph.agentContexts.get(graph.defaultAgentId);
437
- if (primaryContext != null) {
438
- return resolveLangfuseConfig(this.langfuse, primaryContext.langfuse);
439
- }
440
- for (const context of graph.agentContexts.values()) {
441
- const langfuse = resolveLangfuseConfig(this.langfuse, context.langfuse);
442
- if (langfuse != null) {
443
- return langfuse;
444
- }
445
- }
446
- return this.langfuse;
447
- }
448
- getStreamToolOutputTracingLangfuseConfig(graph) {
449
- const toolOutputTracingConfigs = Array.from(graph.agentContexts.values())
450
- .map((context) => {
451
- return resolveLangfuseConfig(this.langfuse, context.langfuse)
452
- ?.toolOutputTracing;
453
- })
454
- .filter((config) => {
455
- return config != null;
456
- });
457
- if (toolOutputTracingConfigs.length === 0) {
458
- return this.langfuse?.toolOutputTracing != null
459
- ? { toolOutputTracing: this.langfuse.toolOutputTracing }
460
- : undefined;
461
- }
462
- if (toolOutputTracingConfigs.length === 1) {
463
- return { toolOutputTracing: toolOutputTracingConfigs[0] };
464
- }
465
- let enabled;
466
- let redactionText;
467
- let redactedToolNameMatchMode;
468
- const redactedToolNames = new Set();
469
- for (const config of toolOutputTracingConfigs) {
470
- if (config.enabled === false) {
471
- enabled = false;
472
- }
473
- else if (enabled !== false && config.enabled != null) {
474
- enabled = config.enabled;
475
- }
476
- redactionText ??= config.redactionText;
477
- if (config.redactedToolNameMatchMode === 'partial') {
478
- redactedToolNameMatchMode = 'partial';
479
- }
480
- else {
481
- redactedToolNameMatchMode ??= config.redactedToolNameMatchMode;
482
- }
483
- for (const toolName of config.redactedToolNames ?? []) {
484
- redactedToolNames.add(toolName);
485
- }
486
- }
487
- return {
488
- toolOutputTracing: {
489
- ...(enabled != null ? { enabled } : {}),
490
- ...(redactedToolNames.size > 0
491
- ? { redactedToolNames: Array.from(redactedToolNames) }
492
- : {}),
493
- ...(redactedToolNameMatchMode != null
494
- ? { redactedToolNameMatchMode }
495
- : {}),
496
- ...(redactionText != null ? { redactionText } : {}),
497
- },
498
- };
499
- }
500
- async processStream(inputs, callerConfig, streamOptions) {
501
- if (this.graphRunnable == null) {
502
- throw new Error('Run not initialized. Make sure to use Run.create() to instantiate the Run.');
503
- }
504
- if (!this.Graph) {
505
- throw new Error('Graph not initialized. Make sure to use Run.create() to instantiate the Run.');
506
- }
507
- const graphRunnable = this.graphRunnable;
508
- const graph = this.Graph;
509
- /**
510
- * `Command` inputs (currently only `Command({ resume })`) are
511
- * resume-mode invocations: LangGraph rebuilds graph state from the
512
- * checkpointer, so we skip RunStart / UserPromptSubmit hooks (no
513
- * new prompt to evaluate) and read run-state from the Graph wrapper
514
- * instead of `inputs.messages`.
515
- */
516
- const isResume = inputs instanceof Command;
517
- const stateInputs = isResume ? undefined : inputs;
518
- const config = {
519
- recursionLimit: 50,
520
- ...callerConfig,
521
- configurable: { ...callerConfig.configurable },
522
- };
523
- /**
524
- * Skip `resetValues` on resume — we're continuing an in-flight
525
- * run, not starting a fresh one. Resetting would wipe the
526
- * sidecars (`toolCallStepIds`, `stepKeyIds`, accumulated
527
- * `messages`, etc.) the resumed `ToolNode` needs to dispatch
528
- * tool completions with the correct step ids and re-resolve
529
- * `{{tool<i>turn<n>}}` references. Pairs with the
530
- * `awaitingResume` gate on `clearHeavyState` in the `finally`
531
- * block so the sidecars survive both ends of the interrupt
532
- * boundary.
533
- */
534
- if (!isResume) {
535
- graph.resetValues(streamOptions?.keepContent);
536
- }
537
- this._interrupt = undefined;
538
- this._haltedReason = undefined;
539
- this.hookRegistry?.clearHaltSignal(this.id);
540
- /** Custom event callback to intercept and handle custom events */
541
- const customEventCallback = this.createCustomEventCallback();
542
- const streamCallbacks = streamOptions?.callbacks
543
- ? this.getCallbacks(streamOptions.callbacks)
544
- : undefined;
545
- const customHandler = BaseCallbackHandler.fromMethods({
546
- [Callback.CUSTOM_EVENT]: customEventCallback,
547
- });
548
- customHandler.awaitHandlers = true;
549
- config.callbacks = appendCallbacks(config.callbacks, streamCallbacks ? [streamCallbacks, customHandler] : [customHandler]);
550
- const primaryContext = graph.agentContexts.get(graph.defaultAgentId);
551
- const userId = typeof config.configurable?.user_id === 'string'
552
- ? config.configurable.user_id
553
- : undefined;
554
- const sessionId = typeof config.configurable?.thread_id === 'string'
555
- ? config.configurable.thread_id
556
- : undefined;
557
- const traceMetadata = createLangfuseTraceMetadata({
558
- messageId: this.id,
559
- parentMessageId: config.configurable?.requestBody?.parentMessageId,
560
- agentId: graph.defaultAgentId,
561
- agentName: primaryContext?.name,
562
- });
563
- const traceName = config.runName ?? getLangfuseTraceName(traceMetadata);
564
- const streamLangfuseConfig = this.getStreamLangfuseConfig(graph);
565
- initializeLangfuseTracing(streamLangfuseConfig);
566
- const langfuseHandler = createLangfuseHandler({
567
- langfuse: streamLangfuseConfig,
568
- userId,
569
- sessionId,
570
- traceMetadata,
571
- tags: ['librechat', 'agent'],
572
- });
573
- if (langfuseHandler != null) {
574
- config.runName = traceName;
575
- config.callbacks = appendCallbacks(config.callbacks, [langfuseHandler]);
576
- }
577
- if (!this.id) {
578
- throw new Error('Run ID not provided');
579
- }
580
- config.run_id = this.id;
581
- config.configurable = Object.assign(config.configurable ?? {}, {
582
- run_id: this.id,
583
- });
584
- const threadId = config.configurable.thread_id;
585
- if (this.hookRegistry != null && stateInputs != null) {
586
- const shouldHalt = await this.runPreStreamHooks(stateInputs, threadId, config);
587
- if (shouldHalt) {
588
- return undefined;
589
- }
590
- }
591
- /**
592
- * Tracks whether the stream loop threw. Used by the `finally`
593
- * block to decide whether to honor the interrupt-preservation
594
- * guard for session hooks: a captured `_interrupt` is only
595
- * meaningful if the stream completed cleanly. If the loop errored
596
- * after stashing an interrupt (e.g. a downstream handler throws
597
- * after the interrupt event landed), the interrupt is stale —
598
- * preserving session hooks would leak them into the next run.
599
- */
600
- let streamThrew = false;
601
- const consumeStream = async () => {
602
- /**
603
- * `streamEvents` accepts both state inputs and `Command` (resume) at
604
- * runtime, but our `CompiledStateWorkflow` type narrows the first
605
- * arg to `BaseGraphState`. Cast on the call so the resume path
606
- * type-checks without widening the wrapper for every caller.
607
- */
608
- const stream = graphRunnable.streamEvents(inputs, config, {
609
- raiseError: true,
610
- /**
611
- * Prevent EventStreamCallbackHandler from processing custom events.
612
- * Custom events are already handled via our createCustomEventCallback()
613
- * which routes them through the handlerRegistry.
614
- * Without this flag, EventStreamCallbackHandler throws errors when
615
- * custom events are dispatched for run IDs not in its internal map
616
- * (due to timing issues in parallel execution or after run cleanup).
617
- */
618
- ignoreCustomEvent: true,
619
- });
620
- for await (const event of stream) {
621
- const { data, metadata, ...info } = event;
622
- const eventName = info.event;
623
- /** Skip custom events as they're handled by our callback */
624
- if (CUSTOM_GRAPH_EVENTS.has(eventName)) {
625
- continue;
626
- }
627
- /**
628
- * Detect interrupts surfaced by LangGraph as a synthetic
629
- * `__interrupt__` field on the streamed chunk and stash the
630
- * first one for the host to read via `run.getInterrupt()`
631
- * once the stream drains. Captured as `unknown` because the
632
- * SDK does not validate the runtime payload shape — the
633
- * built-in ToolNode raises a `HumanInterruptPayload`
634
- * (`tool_approval` / `ask_user_question`), but custom nodes
635
- * can pass any payload to `interrupt()`. Callers narrow with
636
- * the `isToolApprovalInterrupt` / `isAskUserQuestionInterrupt`
637
- * guards or assert via `getInterrupt<T>()`.
638
- */
639
- if (this._interrupt == null &&
640
- data.chunk != null &&
641
- isInterrupted(data.chunk)) {
642
- const interrupts = data.chunk[INTERRUPT];
643
- if (interrupts.length > 0) {
644
- const first = interrupts[0];
645
- /**
646
- * Capture the interrupt unconditionally — `interrupt(null)`
647
- * and `interrupt(undefined)` are valid pauses (a custom
648
- * node may want to pause without metadata) and the host
649
- * still needs to know the run is awaiting resume. Gating
650
- * on `payload != null` would silently downgrade a paused
651
- * run to "completed" and let the `Stop` hook fire,
652
- * breaking host resume handling.
653
- */
654
- this._interrupt = {
655
- interruptId: first.id ?? '',
656
- threadId,
657
- payload: first.value,
658
- };
659
- }
660
- }
661
- const handler = this.handlerRegistry?.getHandler(eventName);
662
- if (handler) {
663
- await handler.handle(eventName, data, metadata, this.Graph);
664
- }
665
- /**
666
- * Mid-flight halt: any hook (PreToolUse, PostToolUse,
667
- * PostToolBatch, SubagentStart/Stop, PreCompact, PostCompact)
668
- * that returned `preventContinuation: true` raises a halt
669
- * signal on the registry via `executeHooks`. We poll between
670
- * stream events and break out as soon as one is set so the
671
- * graph doesn't take another model turn after the halting
672
- * operation completes.
673
- *
674
- * Limitation: the current step (in-flight model call, ongoing
675
- * tool batch) is not aborted — only the next step is skipped.
676
- * This matches Claude Code's `continue: false` semantic where
677
- * the active operation finishes before halting takes effect.
678
- */
679
- const haltSignal = this.hookRegistry?.getHaltSignal(this.id);
680
- if (haltSignal != null) {
681
- this._haltedReason = haltSignal.reason;
682
- break;
683
- }
684
- }
685
- if (this._interrupt != null) {
686
- await this.resolveInterruptResumeConfig(config);
687
- }
688
- /**
689
- * Skip the Stop hook when the run paused on a HITL interrupt
690
- * (still pending human input) or was halted by a hook (the host
691
- * already chose to stop, so a Stop hook firing now would be
692
- * misleading). The host fires Stop on the resumed-and-completed
693
- * run instead.
694
- */
695
- if (this._interrupt == null &&
696
- this._haltedReason == null &&
697
- this.hookRegistry?.hasHookFor('Stop', this.id) === true) {
698
- await executeHooks({
699
- registry: this.hookRegistry,
700
- input: {
701
- hook_event_name: 'Stop',
702
- runId: this.id,
703
- threadId,
704
- agentId: graph.defaultAgentId,
705
- messages: graph.getRunMessages() ?? stateInputs?.messages ?? [],
706
- stopHookActive: false, // will be true when stop is triggered by a hook (Phase 2)
707
- },
708
- sessionId: this.id,
709
- }).catch(() => {
710
- /* Stop hook errors must not masquerade as stream failures */
711
- });
712
- }
713
- };
714
- try {
715
- // When opted in, seed the root trace id from this run's id so feedback /
716
- // other external signals can be attached to the trace later without a
717
- // lookup (see SeededTraceIdGenerator in ./instrumentation).
718
- await runWithTraceIdSeed(streamLangfuseConfig?.deterministicTraceId === true
719
- ? this.id
720
- : undefined, () => withLangfuseToolOutputTracingConfig(streamLangfuseConfig, () => withLangfuseAttributes({
721
- langfuse: streamLangfuseConfig,
722
- userId,
723
- sessionId,
724
- traceName,
725
- traceMetadata,
726
- tags: ['librechat', 'agent'],
727
- }, consumeStream), this.getStreamToolOutputTracingLangfuseConfig(graph)));
728
- }
729
- catch (err) {
730
- streamThrew = true;
731
- if (this.hookRegistry?.hasHookFor('StopFailure', this.id) === true) {
732
- const runMessages = this.Graph.getRunMessages() ?? [];
733
- await executeHooks({
734
- registry: this.hookRegistry,
735
- input: {
736
- hook_event_name: 'StopFailure',
737
- runId: this.id,
738
- threadId,
739
- agentId: this.Graph.defaultAgentId,
740
- error: err instanceof Error ? err.message : String(err),
741
- lastAssistantMessage: findLastMessageOfType(runMessages, 'ai'),
742
- },
743
- sessionId: this.id,
744
- }).catch(() => {
745
- /* swallow hook errors — the original error must propagate */
746
- });
747
- }
748
- throw err;
749
- }
750
- finally {
751
- /**
752
- * Preserve session-scoped hooks when the run paused on a HITL
753
- * interrupt — the very next call will be `Run.resume()`, which
754
- * needs the same policy hooks (e.g., the `PreToolUse` matcher
755
- * that triggered the interrupt) to fire on the re-executed node
756
- * and uphold the approval flow. Clearing here would leak the
757
- * approval gate on resume. The session is cleared instead at
758
- * natural completion, error (including errors that happen AFTER
759
- * an interrupt was captured — those interrupts are stale), or
760
- * hook-driven halt (including hooks that returned BOTH `ask`
761
- * and `preventContinuation` — the halt wins, no resume is
762
- * expected, sessions must drop). Every state where no resume
763
- * is expected clears.
764
- */
765
- if (this.shouldClearHookSession(streamThrew)) {
766
- this.hookRegistry?.clearSession(this.id);
767
- }
768
- /**
769
- * Drop any halt signal raised mid-stream for this run so a
770
- * subsequent `processStream` / `resume` starts with clean state.
771
- * The Run captured `_haltedReason` already; the registry entry
772
- * for this `sessionId` would otherwise spuriously trip the next
773
- * loop. Other concurrent runs sharing this registry are
774
- * unaffected — their entries live under their own session ids.
775
- */
776
- this.hookRegistry?.clearHaltSignal(this.id);
777
- await disposeLangfuseHandler(langfuseHandler);
778
- /**
779
- * Break the reference chain that keeps heavy data alive via
780
- * LangGraph's internal `__pregel_scratchpad.currentTaskInput` →
781
- * `@langchain/core` `RunTree.extra[lc:child_config]` →
782
- * Node.js `AsyncLocalStorage` context captured by timers/promises.
783
- *
784
- * Without this, base64-encoded images/PDFs in message content remain
785
- * reachable from lingering `Timeout` handles until GC runs.
786
- */
787
- if (!this.skipCleanup) {
788
- if (config.configurable != null) {
789
- for (const key of Object.getOwnPropertySymbols(config.configurable)) {
790
- const val = config.configurable[key];
791
- if (val != null &&
792
- typeof val === 'object' &&
793
- 'currentTaskInput' in val) {
794
- val.currentTaskInput = undefined;
795
- }
796
- delete config.configurable[key];
797
- }
798
- config.configurable = undefined;
799
- }
800
- config.callbacks = undefined;
801
- }
802
- const result = this.returnContent
803
- ? this.Graph.getContentParts()
804
- : undefined;
805
- this.calibrationRatio = this.Graph.getCalibrationRatio();
806
- /**
807
- * Skip `clearHeavyState()` when the run paused on a clean HITL
808
- * interrupt awaiting resume — `Run.resume()` re-enters the same
809
- * `ToolNode` instance and needs the sidecars `clearHeavyState`
810
- * would wipe (`toolCallStepIds` for completion-event step ids,
811
- * the `_toolOutputRegistry` for `{{tool<i>turn<n>}}`
812
- * substitutions, `sessions` for code-env continuity, plus the
813
- * `hookRegistry` and `humanInTheLoop` config the interrupt
814
- * branch itself relies on). Without preservation, the resumed
815
- * tool completion would dispatch `ON_RUN_STEP_COMPLETED` with
816
- * an empty step id and downstream stream consumers would drop
817
- * the result.
818
- *
819
- * The natural-completion / error / hook-driven-halt paths still
820
- * clean up — `_haltedReason != null` or `streamThrew` mean no
821
- * resume is expected. Cross-process resume (host rebuilds the
822
- * Run from scratch) is a separate concern; see
823
- * `HumanInTheLoopConfig` JSDoc.
824
- */
825
- const awaitingResume = this.isAwaitingResume(streamThrew);
826
- if (!this.skipCleanup && !awaitingResume) {
827
- this.Graph.clearHeavyState();
828
- }
829
- this._streamResult = result;
830
- }
831
- return this._streamResult;
832
- }
833
- /**
834
- * Returns the pending interrupt captured during the most recent
835
- * `processStream` (or `resume`) invocation. `undefined` when the run
836
- * either has not been streamed yet or completed without pausing.
837
- *
838
- * Hosts call this immediately after `processStream` returns to decide
839
- * whether the run is awaiting human input. Persist the returned
840
- * descriptor (alongside `thread_id` and the agent run config) so a
841
- * later `resume(decisions)` can rebuild the run.
842
- *
843
- * The default `TPayload` is the SDK's `HumanInterruptPayload` union
844
- * (`tool_approval` / `ask_user_question`), suitable for the common
845
- * case where interrupts come from the built-in ToolNode or
846
- * `askUserQuestion()` helper. Hosts that raise custom interrupts
847
- * from custom graph nodes pass their own type — the SDK does not
848
- * validate the runtime shape, it just transports whatever the
849
- * `interrupt()` call carried. When in doubt, narrow with the
850
- * `isToolApprovalInterrupt` / `isAskUserQuestionInterrupt` type
851
- * guards (which accept `unknown`) before reading variant-specific
852
- * fields.
853
- */
854
- getInterrupt() {
855
- return this._interrupt;
856
- }
857
- /**
858
- * Returns the reason a hook halted the run via
859
- * `preventContinuation: true`, or `undefined` if no hook halted.
860
- *
861
- * Hosts inspect this after `processStream` returns to distinguish a
862
- * natural completion (`undefined`) from a hook-driven halt (a
863
- * truthy string). Independent from `getInterrupt()` — a halted run
864
- * has no interrupt; an interrupted run has no halt reason.
865
- */
866
- getHaltReason() {
867
- return this._haltedReason;
868
- }
869
- /**
870
- * Resume a paused HITL run with the value the user (or whatever
871
- * decided the interrupt) supplied. The default `TResume` covers the
872
- * `tool_approval` interrupt (the common case): an array of decisions
873
- * in `action_requests` order, or a record keyed by `tool_call_id`.
874
- *
875
- * For other interrupt types (e.g., `ask_user_question` →
876
- * `AskUserQuestionResolution`, or any custom interrupt a host raises
877
- * from a custom node), pass the type parameter and the SDK forwards
878
- * the value through unchanged. LangGraph delivers it as the return
879
- * value of the original `interrupt()` call inside the paused node.
880
- *
881
- * The host MUST construct this Run with the same `thread_id` and the
882
- * same checkpointer as the original paused run; LangGraph rebuilds
883
- * graph state from the checkpoint and re-enters the interrupted node
884
- * from the start.
885
- */
886
- /**
887
- * Returns the per-Run file checkpointer when
888
- * `toolExecution.local.fileCheckpointing === true` was set on the
889
- * RunConfig. Hosts can capture extra paths or call `rewind()`
890
- * directly. Returns undefined when checkpointing is disabled.
891
- *
892
- * Construction-time invariant: the checkpointer is shared across
893
- * every ToolNode the graph compiles (single-agent and multi-agent),
894
- * so a `rewind()` call here unwinds writes made by ANY agent in the
895
- * run.
896
- */
897
- getFileCheckpointer() {
898
- return this.Graph?.getOrCreateFileCheckpointer();
899
- }
900
- /**
901
- * Convenience wrapper that calls `rewind()` on the per-Run file
902
- * checkpointer. Restores every file the local engine snapshotted
903
- * during this Run to its pre-write content (and deletes any path
904
- * that didn't exist before being created). Returns the count of
905
- * paths processed; returns 0 when checkpointing is disabled.
906
- */
907
- async rewindFiles() {
908
- const cp = this.getFileCheckpointer();
909
- return cp == null ? 0 : cp.rewind();
910
- }
911
- async resume(resumeValue, callerConfig, streamOptions) {
912
- const interruptId = this._interrupt?.interruptId;
913
- const scopedResume = typeof interruptId === 'string' &&
914
- interruptId.length > 0 &&
915
- !isLangGraphResumeMapForInterrupt(resumeValue, interruptId)
916
- ? { [interruptId]: resumeValue }
917
- : resumeValue;
918
- const resumeConfig = await this.resolveInterruptResumeConfig(callerConfig);
919
- return this.processStream(new Command({ resume: scopedResume }), resumeConfig, streamOptions);
920
- }
921
- async resolveInterruptResumeConfig(callerConfig) {
922
- const interrupt = this._interrupt;
923
- const interruptId = interrupt?.interruptId;
924
- const workflow = this.graphRunnable;
925
- const stateHistory = workflow?.getStateHistory;
926
- if (interrupt?.checkpointId != null && interrupt.checkpointId.length > 0) {
927
- return {
928
- ...callerConfig,
929
- configurable: {
930
- ...callerConfig.configurable,
931
- checkpoint_id: interrupt.checkpointId,
932
- ...(typeof interrupt.checkpointNs === 'string'
933
- ? { checkpoint_ns: interrupt.checkpointNs }
934
- : {}),
935
- },
936
- };
937
- }
938
- if (interrupt == null ||
939
- typeof interruptId !== 'string' ||
940
- interruptId.length === 0 ||
941
- typeof stateHistory !== 'function') {
942
- return callerConfig;
943
- }
944
- for await (const snapshot of stateHistory.call(this.graphRunnable, callerConfig)) {
945
- const hasMatchingInterrupt = snapshot.tasks?.some((task) => task.interrupts?.some((interrupt) => interrupt.id === interruptId) === true) === true;
946
- const checkpointConfigurable = snapshot.config?.configurable;
947
- if (!hasMatchingInterrupt || checkpointConfigurable == null) {
948
- continue;
949
- }
950
- const checkpointId = checkpointConfigurable.checkpoint_id;
951
- const checkpointNs = checkpointConfigurable.checkpoint_ns;
952
- if (typeof checkpointId === 'string' && checkpointId.length > 0) {
953
- this._interrupt = {
954
- ...interrupt,
955
- checkpointId,
956
- ...(typeof checkpointNs === 'string' ? { checkpointNs } : {}),
957
- };
958
- return {
959
- ...callerConfig,
960
- configurable: {
961
- ...callerConfig.configurable,
962
- checkpoint_id: checkpointId,
963
- ...(typeof checkpointNs === 'string'
964
- ? { checkpoint_ns: checkpointNs }
965
- : {}),
966
- },
967
- };
968
- }
969
- }
970
- return callerConfig;
971
- }
972
- createSystemCallback(clientCallbacks, key) {
973
- return ((...args) => {
974
- const clientCallback = clientCallbacks[key];
975
- if (clientCallback && this.Graph) {
976
- clientCallback(this.Graph, ...args);
977
- }
978
- });
979
- }
980
- getCallbacks(clientCallbacks) {
981
- return {
982
- [Callback.TOOL_ERROR]: this.createSystemCallback(clientCallbacks, Callback.TOOL_ERROR),
983
- [Callback.TOOL_START]: this.createSystemCallback(clientCallbacks, Callback.TOOL_START),
984
- [Callback.TOOL_END]: this.createSystemCallback(clientCallbacks, Callback.TOOL_END),
985
- };
986
- }
987
- async generateTitle({ provider, inputText, contentParts, titlePrompt, clientOptions, chainOptions, skipLanguage, titleMethod = TitleMethod.COMPLETION, titlePromptTemplate, }) {
988
- let titleLangfuseHandler;
989
- let titleLangfuseConfig;
990
- let titleUserId;
991
- let titleSessionId;
992
- const titleContext = this.Graph == null
993
- ? undefined
994
- : this.Graph.agentContexts.get(this.Graph.defaultAgentId);
995
- const traceMetadata = createLangfuseTraceMetadata({
996
- messageId: 'title-' + this.id,
997
- agentName: titleContext?.name,
998
- });
999
- const titleRunName = getLangfuseTraceName(traceMetadata, 'LibreChat Title');
1000
- if (chainOptions != null) {
1001
- titleUserId =
1002
- typeof chainOptions.configurable?.user_id === 'string'
1003
- ? chainOptions.configurable.user_id
1004
- : undefined;
1005
- titleSessionId =
1006
- typeof chainOptions.configurable?.thread_id === 'string'
1007
- ? chainOptions.configurable.thread_id
1008
- : undefined;
1009
- titleLangfuseConfig = resolveLangfuseConfig(this.langfuse, titleContext?.langfuse);
1010
- initializeLangfuseTracing(titleLangfuseConfig);
1011
- titleLangfuseHandler = createLangfuseHandler({
1012
- langfuse: titleLangfuseConfig,
1013
- userId: titleUserId,
1014
- sessionId: titleSessionId,
1015
- traceMetadata,
1016
- tags: ['librechat', 'title'],
1017
- });
1018
- if (titleLangfuseHandler != null) {
1019
- chainOptions.callbacks = appendCallbacks(chainOptions.callbacks, [
1020
- titleLangfuseHandler,
1021
- ]);
1022
- }
1023
- }
1024
- const convoTemplate = PromptTemplate.fromTemplate(titlePromptTemplate ?? 'User: {input}\nAI: {output}');
1025
- const response = contentParts
1026
- .map((part) => {
1027
- if (part?.type === 'text')
1028
- return part.text;
1029
- return '';
1030
- })
1031
- .join('\n');
1032
- const model = initializeModel({
1033
- provider,
1034
- clientOptions,
1035
- });
1036
- if (isOpenAILike(provider) &&
1037
- (model instanceof ChatOpenAI$1 || model instanceof AzureChatOpenAI$1)) {
1038
- model.temperature = clientOptions
1039
- ?.temperature;
1040
- model.topP = clientOptions
1041
- ?.topP;
1042
- model.frequencyPenalty = clientOptions?.frequencyPenalty;
1043
- model.presencePenalty = clientOptions?.presencePenalty;
1044
- model.n = clientOptions
1045
- ?.n;
1046
- }
1047
- const convoToTitleInput = new RunnableLambda({
1048
- func: (promptValue) => ({
1049
- convo: promptValue.value,
1050
- inputText,
1051
- skipLanguage,
1052
- }),
1053
- }).withConfig({ runName: 'ConvoTransform' });
1054
- const titleChain = titleMethod === TitleMethod.COMPLETION
1055
- ? await createCompletionTitleRunnable(model, titlePrompt)
1056
- : await createTitleRunnable(model, titlePrompt);
1057
- /** Pipes `convoTemplate` -> `transformer` -> `titleChain` */
1058
- const fullChain = convoTemplate
1059
- .withConfig({ runName: 'ConvoTemplate' })
1060
- .pipe(convoToTitleInput)
1061
- .pipe(titleChain)
1062
- .withConfig({ runName: 'TitleChain' });
1063
- const invokeConfig = Object.assign({}, chainOptions, {
1064
- run_id: this.id,
1065
- runId: this.id,
1066
- runName: chainOptions?.runName ?? titleRunName,
1067
- });
1068
- const invokeTitleChain = (runtimeConfig) => withLangfuseAttributes({
1069
- langfuse: titleLangfuseConfig,
1070
- userId: titleUserId,
1071
- sessionId: titleSessionId,
1072
- traceName: runtimeConfig.runName ?? titleRunName,
1073
- traceMetadata,
1074
- tags: ['librechat', 'title'],
1075
- }, () => fullChain.invoke({ input: inputText, output: response }, runtimeConfig));
1076
- try {
1077
- try {
1078
- return await withLangfuseToolOutputTracingConfig(this.langfuse, () => invokeTitleChain(invokeConfig), titleContext?.langfuse);
1079
- }
1080
- catch (_e) {
1081
- // Fallback: strip callbacks to avoid EventStream tracer errors in certain environments
1082
- // but preserve Langfuse tracing if it exists.
1083
- const langfuseHandler = findCallback(invokeConfig.callbacks, isLangfuseCallbackHandler);
1084
- const { callbacks: _cb, ...rest } = invokeConfig;
1085
- const safeConfig = Object.assign({}, rest, {
1086
- callbacks: langfuseHandler ? [langfuseHandler] : [],
1087
- });
1088
- return await withLangfuseToolOutputTracingConfig(this.langfuse, () => invokeTitleChain(safeConfig), titleContext?.langfuse);
1089
- }
1090
- }
1091
- finally {
1092
- await disposeLangfuseHandler(titleLangfuseHandler);
1093
- }
1094
- }
62
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
63
+ return Object.prototype.hasOwnProperty.call(value, interruptId);
1095
64
  }
65
+ var Run = class Run {
66
+ id;
67
+ tokenCounter;
68
+ handlerRegistry;
69
+ hookRegistry;
70
+ humanInTheLoop;
71
+ langfuse;
72
+ toolOutputReferences;
73
+ eagerEventToolExecution;
74
+ toolExecution;
75
+ indexTokenCountMap;
76
+ calibrationRatio = 1;
77
+ graphRunnable;
78
+ Graph;
79
+ returnContent = false;
80
+ skipCleanup = false;
81
+ _streamResult;
82
+ /**
83
+ * Captured interrupt payload typed as `unknown` because the SDK
84
+ * does not validate the runtime shape — custom graph nodes can
85
+ * raise interrupts with arbitrary payloads (not just the SDK's
86
+ * `HumanInterruptPayload` union). The public `getInterrupt<T>()`
87
+ * lets callers assert the type they expect.
88
+ */
89
+ _interrupt;
90
+ _haltedReason;
91
+ constructor(config) {
92
+ const runId = config.runId ?? "";
93
+ if (!runId) throw new Error("Run ID not provided");
94
+ this.id = runId;
95
+ this.tokenCounter = config.tokenCounter;
96
+ this.indexTokenCountMap = config.indexTokenCountMap;
97
+ if (config.calibrationRatio != null && config.calibrationRatio > 0) this.calibrationRatio = config.calibrationRatio;
98
+ const handlerRegistry = new HandlerRegistry();
99
+ if (config.customHandlers) for (const [eventType, handler] of Object.entries(config.customHandlers)) handlerRegistry.register(eventType, handler);
100
+ this.handlerRegistry = handlerRegistry;
101
+ this.hookRegistry = config.hooks;
102
+ this.humanInTheLoop = config.humanInTheLoop;
103
+ this.langfuse = config.langfuse;
104
+ this.toolOutputReferences = config.toolOutputReferences;
105
+ this.eagerEventToolExecution = config.eagerEventToolExecution;
106
+ this.toolExecution = config.toolExecution;
107
+ if (!config.graphConfig) throw new Error("Graph config not provided");
108
+ /** Handle different graph types */
109
+ if (config.graphConfig.type === "multi-agent") {
110
+ this.graphRunnable = this.createMultiAgentGraph(config.graphConfig);
111
+ if (this.Graph) this.Graph.handlerRegistry = handlerRegistry;
112
+ } else {
113
+ /** Default to legacy graph for 'standard' or undefined type */
114
+ this.graphRunnable = this.createLegacyGraph(config.graphConfig);
115
+ if (this.Graph) {
116
+ this.Graph.compileOptions = config.graphConfig.compileOptions ?? this.Graph.compileOptions;
117
+ this.Graph.handlerRegistry = handlerRegistry;
118
+ }
119
+ }
120
+ if (config.initialSessions && this.Graph) for (const [key, value] of config.initialSessions) this.Graph.sessions.set(key, value);
121
+ this.returnContent = config.returnContent ?? false;
122
+ this.skipCleanup = config.skipCleanup ?? false;
123
+ }
124
+ createLegacyGraph(config) {
125
+ let agentConfig;
126
+ let signal;
127
+ /** Check if this is a multi-agent style config (has agents array) */
128
+ if ("agents" in config && Array.isArray(config.agents)) {
129
+ if (config.agents.length === 0) throw new Error("At least one agent must be provided");
130
+ agentConfig = config.agents[0];
131
+ signal = config.signal;
132
+ } else {
133
+ /** Legacy path: build agent config from llmConfig */
134
+ const { type: _type, llmConfig, signal: legacySignal, tools = [], ...agentInputs } = config;
135
+ const { provider, ...clientOptions } = llmConfig;
136
+ agentConfig = {
137
+ ...agentInputs,
138
+ tools,
139
+ provider,
140
+ clientOptions,
141
+ agentId: "default"
142
+ };
143
+ signal = legacySignal;
144
+ }
145
+ const standardGraph = new StandardGraph({
146
+ signal,
147
+ runId: this.id,
148
+ agents: [agentConfig],
149
+ langfuse: this.langfuse,
150
+ tokenCounter: this.tokenCounter,
151
+ indexTokenCountMap: this.indexTokenCountMap,
152
+ calibrationRatio: this.calibrationRatio
153
+ });
154
+ /** Propagate compile options from graph config */
155
+ standardGraph.compileOptions = this.applyHITLCheckpointerFallback(config.compileOptions);
156
+ standardGraph.hookRegistry = this.hookRegistry;
157
+ standardGraph.humanInTheLoop = this.humanInTheLoop;
158
+ standardGraph.toolOutputReferences = this.toolOutputReferences;
159
+ standardGraph.eagerEventToolExecution = this.eagerEventToolExecution;
160
+ standardGraph.toolExecution = this.toolExecution;
161
+ this.Graph = standardGraph;
162
+ return standardGraph.createWorkflow();
163
+ }
164
+ createMultiAgentGraph(config) {
165
+ const { agents, edges, compileOptions } = config;
166
+ const multiAgentGraph = new MultiAgentGraph({
167
+ runId: this.id,
168
+ agents,
169
+ edges,
170
+ langfuse: this.langfuse,
171
+ tokenCounter: this.tokenCounter,
172
+ indexTokenCountMap: this.indexTokenCountMap,
173
+ calibrationRatio: this.calibrationRatio
174
+ });
175
+ multiAgentGraph.compileOptions = this.applyHITLCheckpointerFallback(compileOptions);
176
+ multiAgentGraph.hookRegistry = this.hookRegistry;
177
+ multiAgentGraph.humanInTheLoop = this.humanInTheLoop;
178
+ multiAgentGraph.toolOutputReferences = this.toolOutputReferences;
179
+ multiAgentGraph.eagerEventToolExecution = this.eagerEventToolExecution;
180
+ multiAgentGraph.toolExecution = this.toolExecution;
181
+ this.Graph = multiAgentGraph;
182
+ return multiAgentGraph.createWorkflow();
183
+ }
184
+ /**
185
+ * When the host opted into HITL via `humanInTheLoop: { enabled: true }`
186
+ * and did not supply a checkpointer, install an in-memory `MemorySaver`
187
+ * so `interrupt()` can persist checkpoints and `Command({ resume })`
188
+ * can rebuild state. The fallback is intentionally process-local:
189
+ * production hosts that need durable resumption across processes /
190
+ * restarts must provide their own checkpointer (Redis, Postgres, etc.)
191
+ * on `compileOptions.checkpointer`.
192
+ *
193
+ * No-op when HITL is off (the default — omitted, or
194
+ * `{ enabled: false }`) or the host already supplied a checkpointer
195
+ * of their own. See `HumanInTheLoopConfig` JSDoc for the rationale
196
+ * behind the default-off stance.
197
+ */
198
+ applyHITLCheckpointerFallback(compileOptions) {
199
+ if (this.humanInTheLoop?.enabled !== true) return compileOptions;
200
+ if (compileOptions?.checkpointer != null) return compileOptions;
201
+ return {
202
+ ...compileOptions ?? {},
203
+ checkpointer: new MemorySaver()
204
+ };
205
+ }
206
+ /**
207
+ * Run RunStart + UserPromptSubmit hooks before the graph stream
208
+ * begins, accumulate any `additionalContext` strings into the input
209
+ * messages, and short-circuit when a hook signals the run should not
210
+ * proceed (deny / ask decision on the prompt, or `preventContinuation`
211
+ * on either hook).
212
+ *
213
+ * Returns `true` when the caller should bail with `undefined` (run
214
+ * was halted before any model call); returns `false` to proceed
215
+ * into the stream loop.
216
+ *
217
+ * ## Side effects
218
+ *
219
+ * On the success path:
220
+ * - Mutates `stateInputs.messages` in place to append a
221
+ * consolidated `HumanMessage` carrying any hook
222
+ * `additionalContext` strings. Safe because the host owns the
223
+ * array and `processStream` is the only consumer until LangGraph
224
+ * reads it.
225
+ *
226
+ * On the halt path (returning `true`):
227
+ * - Sets `this._haltedReason` so callers (and the eventual host)
228
+ * can distinguish a hook-driven halt from a natural completion.
229
+ * - Calls `registry.clearSession(this.id)` and
230
+ * `registry.clearHaltSignal(this.id)` because no resume is
231
+ * expected from a pre-stream halt — the run never entered the
232
+ * graph, so the session/halt state for this run would otherwise
233
+ * leak to the next `processStream` invocation on the same
234
+ * registry. Other concurrent runs on the same registry are
235
+ * untouched (halt signals are scoped per session id).
236
+ * - Sets `config.callbacks = undefined` to drop the callback
237
+ * references the caller built (langfuse handler, custom event
238
+ * handler, etc.) since they won't be exercised. Mirrors the
239
+ * equivalent cleanup the `processStream` `finally` block does
240
+ * on the natural-completion path.
241
+ */
242
+ async runPreStreamHooks(stateInputs, threadId, config) {
243
+ const registry = this.hookRegistry;
244
+ /**
245
+ * Defensive guard: `processStream` already validated `this.Graph`
246
+ * before calling this helper, but TypeScript can't propagate that
247
+ * narrowing across method boundaries. The check keeps the body
248
+ * free of `this.Graph!` non-null assertions.
249
+ */
250
+ if (registry == null || this.Graph == null) return false;
251
+ const preStreamContexts = [];
252
+ const runStartResult = await executeHooks({
253
+ registry,
254
+ input: {
255
+ hook_event_name: "RunStart",
256
+ runId: this.id,
257
+ threadId,
258
+ agentId: this.Graph.defaultAgentId,
259
+ messages: stateInputs.messages
260
+ },
261
+ sessionId: this.id
262
+ });
263
+ for (const ctx of runStartResult.additionalContexts) preStreamContexts.push(ctx);
264
+ /**
265
+ * Honor `preventContinuation` from RunStart before the stream
266
+ * starts. Mid-flight halts (from tool/compact/subagent hooks)
267
+ * route through `HookRegistry.haltRun` and are polled by the
268
+ * stream loop in `processStream` — different mechanism, same
269
+ * intent.
270
+ */
271
+ if (runStartResult.preventContinuation === true) {
272
+ this._haltedReason = runStartResult.stopReason ?? "preventContinuation";
273
+ registry.clearSession(this.id);
274
+ registry.clearHaltSignal(this.id);
275
+ config.callbacks = void 0;
276
+ return true;
277
+ }
278
+ const lastHuman = findLastMessageOfType(stateInputs.messages, "human");
279
+ if (lastHuman != null) {
280
+ const promptResult = await executeHooks({
281
+ registry,
282
+ input: {
283
+ hook_event_name: "UserPromptSubmit",
284
+ runId: this.id,
285
+ threadId,
286
+ agentId: this.Graph.defaultAgentId,
287
+ prompt: extractPromptText(lastHuman)
288
+ },
289
+ sessionId: this.id
290
+ });
291
+ if (promptResult.decision === "deny" || promptResult.decision === "ask" || promptResult.preventContinuation === true) {
292
+ /**
293
+ * Always set `_haltedReason` so the host can call
294
+ * `getHaltReason()` and distinguish a hook-blocked prompt
295
+ * from a natural empty-output completion. Three signals can
296
+ * land here, each with its own canonical reason string when
297
+ * the hook didn't supply one.
298
+ */
299
+ if (promptResult.preventContinuation === true) this._haltedReason = promptResult.stopReason ?? "preventContinuation";
300
+ else if (promptResult.decision === "deny") this._haltedReason = promptResult.reason ?? "prompt_denied";
301
+ else this._haltedReason = promptResult.reason ?? "prompt_requires_approval";
302
+ registry.clearSession(this.id);
303
+ registry.clearHaltSignal(this.id);
304
+ config.callbacks = void 0;
305
+ return true;
306
+ }
307
+ for (const ctx of promptResult.additionalContexts) preStreamContexts.push(ctx);
308
+ }
309
+ if (preStreamContexts.length > 0)
310
+ /**
311
+ * Wraps the joined hook contexts as a `HumanMessage` even though
312
+ * the intent is system-level guidance. Using a `SystemMessage`
313
+ * mid-conversation is rejected by Anthropic and Google providers
314
+ * (system messages must be the leading entry), so the LangChain
315
+ * convention — also used by `ToolNode.convertInjectedMessages`
316
+ * — is `HumanMessage` carrying `additional_kwargs.role` as a
317
+ * marker for hosts inspecting state. The model still sees a
318
+ * user-role message; the `role: 'system'` field is metadata
319
+ * only. Hosts that want a true system message should compose
320
+ * it into the agent's `instructions` config instead.
321
+ */
322
+ stateInputs.messages.push(new HumanMessage({
323
+ content: preStreamContexts.join("\n\n"),
324
+ additional_kwargs: {
325
+ role: "system",
326
+ source: "hook"
327
+ }
328
+ }));
329
+ return false;
330
+ }
331
+ static async create(config) {
332
+ /** Create tokenCounter if indexTokenCountMap is provided but tokenCounter is not */
333
+ if (config.indexTokenCountMap && !config.tokenCounter) {
334
+ const gc = config.graphConfig;
335
+ config.tokenCounter = await createTokenCounter(encodingForModel(("agents" in gc ? gc.agents[0]?.clientOptions : gc.clientOptions)?.model ?? ""));
336
+ }
337
+ return new Run(config);
338
+ }
339
+ getRunMessages() {
340
+ if (!this.Graph) throw new Error("Graph not initialized. Make sure to use Run.create() to instantiate the Run.");
341
+ return this.Graph.getRunMessages();
342
+ }
343
+ /**
344
+ * Returns the current calibration ratio (EMA of provider-vs-estimate token ratios).
345
+ * Hosts should persist this value and pass it back as `RunConfig.calibrationRatio`
346
+ * on the next run for the same conversation so the pruner starts with an accurate
347
+ * scaling factor instead of the default (1).
348
+ */
349
+ getCalibrationRatio() {
350
+ return this.calibrationRatio;
351
+ }
352
+ getResolvedInstructionOverhead() {
353
+ return this.Graph?.getResolvedInstructionOverhead();
354
+ }
355
+ getToolCount() {
356
+ return this.Graph?.getToolCount() ?? 0;
357
+ }
358
+ /**
359
+ * Creates a custom event callback handler that intercepts custom events
360
+ * and processes them through our handler registry instead of EventStreamCallbackHandler
361
+ */
362
+ createCustomEventCallback() {
363
+ return async (eventName, data, runId, tags, metadata) => {
364
+ const stepScopedEventId = getStepScopedEventId(data);
365
+ if (DIRECT_DISPATCHED_STEP_EVENTS.has(eventName) && this.Graph != null && stepScopedEventId != null && this.Graph.hasHandlerDispatchedEvent(eventName, stepScopedEventId)) return;
366
+ const handler = this.handlerRegistry?.getHandler(eventName);
367
+ if (handler && this.Graph) return await handler.handle(eventName, data, metadata, this.Graph);
368
+ };
369
+ }
370
+ shouldClearHookSession(streamThrew) {
371
+ return this._interrupt == null || this._haltedReason != null || streamThrew;
372
+ }
373
+ isAwaitingResume(streamThrew) {
374
+ return this._interrupt != null && this._haltedReason == null && !streamThrew;
375
+ }
376
+ getStreamLangfuseConfig(graph) {
377
+ const primaryContext = graph.agentContexts.get(graph.defaultAgentId);
378
+ if (primaryContext != null) return resolveLangfuseConfig(this.langfuse, primaryContext.langfuse);
379
+ for (const context of graph.agentContexts.values()) {
380
+ const langfuse = resolveLangfuseConfig(this.langfuse, context.langfuse);
381
+ if (langfuse != null) return langfuse;
382
+ }
383
+ return this.langfuse;
384
+ }
385
+ getStreamToolOutputTracingLangfuseConfig(graph) {
386
+ const toolOutputTracingConfigs = Array.from(graph.agentContexts.values()).map((context) => {
387
+ return resolveLangfuseConfig(this.langfuse, context.langfuse)?.toolOutputTracing;
388
+ }).filter((config) => {
389
+ return config != null;
390
+ });
391
+ if (toolOutputTracingConfigs.length === 0) return this.langfuse?.toolOutputTracing != null ? { toolOutputTracing: this.langfuse.toolOutputTracing } : void 0;
392
+ if (toolOutputTracingConfigs.length === 1) return { toolOutputTracing: toolOutputTracingConfigs[0] };
393
+ let enabled;
394
+ let redactionText;
395
+ let redactedToolNameMatchMode;
396
+ const redactedToolNames = /* @__PURE__ */ new Set();
397
+ for (const config of toolOutputTracingConfigs) {
398
+ if (config.enabled === false) enabled = false;
399
+ else if (enabled !== false && config.enabled != null) enabled = config.enabled;
400
+ redactionText ??= config.redactionText;
401
+ if (config.redactedToolNameMatchMode === "partial") redactedToolNameMatchMode = "partial";
402
+ else redactedToolNameMatchMode ??= config.redactedToolNameMatchMode;
403
+ for (const toolName of config.redactedToolNames ?? []) redactedToolNames.add(toolName);
404
+ }
405
+ return { toolOutputTracing: {
406
+ ...enabled != null ? { enabled } : {},
407
+ ...redactedToolNames.size > 0 ? { redactedToolNames: Array.from(redactedToolNames) } : {},
408
+ ...redactedToolNameMatchMode != null ? { redactedToolNameMatchMode } : {},
409
+ ...redactionText != null ? { redactionText } : {}
410
+ } };
411
+ }
412
+ async processStream(inputs, callerConfig, streamOptions) {
413
+ if (this.graphRunnable == null) throw new Error("Run not initialized. Make sure to use Run.create() to instantiate the Run.");
414
+ if (!this.Graph) throw new Error("Graph not initialized. Make sure to use Run.create() to instantiate the Run.");
415
+ const graphRunnable = this.graphRunnable;
416
+ const graph = this.Graph;
417
+ /**
418
+ * `Command` inputs (currently only `Command({ resume })`) are
419
+ * resume-mode invocations: LangGraph rebuilds graph state from the
420
+ * checkpointer, so we skip RunStart / UserPromptSubmit hooks (no
421
+ * new prompt to evaluate) and read run-state from the Graph wrapper
422
+ * instead of `inputs.messages`.
423
+ */
424
+ const isResume = inputs instanceof Command;
425
+ const stateInputs = isResume ? void 0 : inputs;
426
+ const config = {
427
+ recursionLimit: 50,
428
+ ...callerConfig,
429
+ configurable: { ...callerConfig.configurable }
430
+ };
431
+ /**
432
+ * Skip `resetValues` on resume — we're continuing an in-flight
433
+ * run, not starting a fresh one. Resetting would wipe the
434
+ * sidecars (`toolCallStepIds`, `stepKeyIds`, accumulated
435
+ * `messages`, etc.) the resumed `ToolNode` needs to dispatch
436
+ * tool completions with the correct step ids and re-resolve
437
+ * `{{tool<i>turn<n>}}` references. Pairs with the
438
+ * `awaitingResume` gate on `clearHeavyState` in the `finally`
439
+ * block so the sidecars survive both ends of the interrupt
440
+ * boundary.
441
+ */
442
+ if (!isResume) graph.resetValues(streamOptions?.keepContent);
443
+ this._interrupt = void 0;
444
+ this._haltedReason = void 0;
445
+ this.hookRegistry?.clearHaltSignal(this.id);
446
+ /** Custom event callback to intercept and handle custom events */
447
+ const customEventCallback = this.createCustomEventCallback();
448
+ const streamCallbacks = streamOptions?.callbacks ? this.getCallbacks(streamOptions.callbacks) : void 0;
449
+ const customHandler = BaseCallbackHandler.fromMethods({ ["handleCustomEvent"]: customEventCallback });
450
+ customHandler.awaitHandlers = true;
451
+ config.callbacks = appendCallbacks(config.callbacks, streamCallbacks ? [streamCallbacks, customHandler] : [customHandler]);
452
+ const primaryContext = graph.agentContexts.get(graph.defaultAgentId);
453
+ const userId = typeof config.configurable?.user_id === "string" ? config.configurable.user_id : void 0;
454
+ const sessionId = typeof config.configurable?.thread_id === "string" ? config.configurable.thread_id : void 0;
455
+ const traceMetadata = createLangfuseTraceMetadata({
456
+ messageId: this.id,
457
+ parentMessageId: config.configurable?.requestBody?.parentMessageId,
458
+ agentId: graph.defaultAgentId,
459
+ agentName: primaryContext?.name
460
+ });
461
+ const traceName = config.runName ?? getLangfuseTraceName(traceMetadata);
462
+ const streamLangfuseConfig = this.getStreamLangfuseConfig(graph);
463
+ initializeLangfuseTracing(streamLangfuseConfig);
464
+ const langfuseHandler = createLangfuseHandler({
465
+ langfuse: streamLangfuseConfig,
466
+ userId,
467
+ sessionId,
468
+ traceMetadata,
469
+ tags: ["librechat", "agent"]
470
+ });
471
+ if (langfuseHandler != null) {
472
+ config.runName = traceName;
473
+ config.callbacks = appendCallbacks(config.callbacks, [langfuseHandler]);
474
+ }
475
+ if (!this.id) throw new Error("Run ID not provided");
476
+ config.run_id = this.id;
477
+ config.configurable = Object.assign(config.configurable ?? {}, { run_id: this.id });
478
+ const threadId = config.configurable.thread_id;
479
+ if (this.hookRegistry != null && stateInputs != null) {
480
+ if (await this.runPreStreamHooks(stateInputs, threadId, config)) return;
481
+ }
482
+ /**
483
+ * Tracks whether the stream loop threw. Used by the `finally`
484
+ * block to decide whether to honor the interrupt-preservation
485
+ * guard for session hooks: a captured `_interrupt` is only
486
+ * meaningful if the stream completed cleanly. If the loop errored
487
+ * after stashing an interrupt (e.g. a downstream handler throws
488
+ * after the interrupt event landed), the interrupt is stale —
489
+ * preserving session hooks would leak them into the next run.
490
+ */
491
+ let streamThrew = false;
492
+ const consumeStream = async () => {
493
+ /**
494
+ * `streamEvents` accepts both state inputs and `Command` (resume) at
495
+ * runtime, but our `CompiledStateWorkflow` type narrows the first
496
+ * arg to `BaseGraphState`. Cast on the call so the resume path
497
+ * type-checks without widening the wrapper for every caller.
498
+ */
499
+ const stream = graphRunnable.streamEvents(inputs, config, {
500
+ raiseError: true,
501
+ /**
502
+ * Prevent EventStreamCallbackHandler from processing custom events.
503
+ * Custom events are already handled via our createCustomEventCallback()
504
+ * which routes them through the handlerRegistry.
505
+ * Without this flag, EventStreamCallbackHandler throws errors when
506
+ * custom events are dispatched for run IDs not in its internal map
507
+ * (due to timing issues in parallel execution or after run cleanup).
508
+ */
509
+ ignoreCustomEvent: true
510
+ });
511
+ for await (const event of stream) {
512
+ const { data, metadata, ...info } = event;
513
+ const eventName = info.event;
514
+ /** Skip custom events as they're handled by our callback */
515
+ if (CUSTOM_GRAPH_EVENTS.has(eventName)) continue;
516
+ /**
517
+ * Detect interrupts surfaced by LangGraph as a synthetic
518
+ * `__interrupt__` field on the streamed chunk and stash the
519
+ * first one for the host to read via `run.getInterrupt()`
520
+ * once the stream drains. Captured as `unknown` because the
521
+ * SDK does not validate the runtime payload shape — the
522
+ * built-in ToolNode raises a `HumanInterruptPayload`
523
+ * (`tool_approval` / `ask_user_question`), but custom nodes
524
+ * can pass any payload to `interrupt()`. Callers narrow with
525
+ * the `isToolApprovalInterrupt` / `isAskUserQuestionInterrupt`
526
+ * guards or assert via `getInterrupt<T>()`.
527
+ */
528
+ if (this._interrupt == null && data.chunk != null && isInterrupted(data.chunk)) {
529
+ const interrupts = data.chunk[INTERRUPT];
530
+ if (interrupts.length > 0) {
531
+ const first = interrupts[0];
532
+ /**
533
+ * Capture the interrupt unconditionally — `interrupt(null)`
534
+ * and `interrupt(undefined)` are valid pauses (a custom
535
+ * node may want to pause without metadata) and the host
536
+ * still needs to know the run is awaiting resume. Gating
537
+ * on `payload != null` would silently downgrade a paused
538
+ * run to "completed" and let the `Stop` hook fire,
539
+ * breaking host resume handling.
540
+ */
541
+ this._interrupt = {
542
+ interruptId: first.id ?? "",
543
+ threadId,
544
+ payload: first.value
545
+ };
546
+ }
547
+ }
548
+ const handler = this.handlerRegistry?.getHandler(eventName);
549
+ if (handler) await handler.handle(eventName, data, metadata, this.Graph);
550
+ /**
551
+ * Mid-flight halt: any hook (PreToolUse, PostToolUse,
552
+ * PostToolBatch, SubagentStart/Stop, PreCompact, PostCompact)
553
+ * that returned `preventContinuation: true` raises a halt
554
+ * signal on the registry via `executeHooks`. We poll between
555
+ * stream events and break out as soon as one is set so the
556
+ * graph doesn't take another model turn after the halting
557
+ * operation completes.
558
+ *
559
+ * Limitation: the current step (in-flight model call, ongoing
560
+ * tool batch) is not aborted — only the next step is skipped.
561
+ * This matches Claude Code's `continue: false` semantic where
562
+ * the active operation finishes before halting takes effect.
563
+ */
564
+ const haltSignal = this.hookRegistry?.getHaltSignal(this.id);
565
+ if (haltSignal != null) {
566
+ this._haltedReason = haltSignal.reason;
567
+ break;
568
+ }
569
+ }
570
+ if (this._interrupt != null) await this.resolveInterruptResumeConfig(config);
571
+ /**
572
+ * Skip the Stop hook when the run paused on a HITL interrupt
573
+ * (still pending human input) or was halted by a hook (the host
574
+ * already chose to stop, so a Stop hook firing now would be
575
+ * misleading). The host fires Stop on the resumed-and-completed
576
+ * run instead.
577
+ */
578
+ if (this._interrupt == null && this._haltedReason == null && this.hookRegistry?.hasHookFor("Stop", this.id) === true) await executeHooks({
579
+ registry: this.hookRegistry,
580
+ input: {
581
+ hook_event_name: "Stop",
582
+ runId: this.id,
583
+ threadId,
584
+ agentId: graph.defaultAgentId,
585
+ messages: graph.getRunMessages() ?? stateInputs?.messages ?? [],
586
+ stopHookActive: false
587
+ },
588
+ sessionId: this.id
589
+ }).catch(() => {});
590
+ };
591
+ try {
592
+ await runWithTraceIdSeed(streamLangfuseConfig?.deterministicTraceId === true ? this.id : void 0, () => withLangfuseToolOutputTracingConfig(streamLangfuseConfig, () => withLangfuseAttributes({
593
+ langfuse: streamLangfuseConfig,
594
+ userId,
595
+ sessionId,
596
+ traceName,
597
+ traceMetadata,
598
+ tags: ["librechat", "agent"]
599
+ }, consumeStream), this.getStreamToolOutputTracingLangfuseConfig(graph)));
600
+ } catch (err) {
601
+ streamThrew = true;
602
+ if (this.hookRegistry?.hasHookFor("StopFailure", this.id) === true) {
603
+ const runMessages = this.Graph.getRunMessages() ?? [];
604
+ await executeHooks({
605
+ registry: this.hookRegistry,
606
+ input: {
607
+ hook_event_name: "StopFailure",
608
+ runId: this.id,
609
+ threadId,
610
+ agentId: this.Graph.defaultAgentId,
611
+ error: err instanceof Error ? err.message : String(err),
612
+ lastAssistantMessage: findLastMessageOfType(runMessages, "ai")
613
+ },
614
+ sessionId: this.id
615
+ }).catch(() => {});
616
+ }
617
+ throw err;
618
+ } finally {
619
+ /**
620
+ * Preserve session-scoped hooks when the run paused on a HITL
621
+ * interrupt — the very next call will be `Run.resume()`, which
622
+ * needs the same policy hooks (e.g., the `PreToolUse` matcher
623
+ * that triggered the interrupt) to fire on the re-executed node
624
+ * and uphold the approval flow. Clearing here would leak the
625
+ * approval gate on resume. The session is cleared instead at
626
+ * natural completion, error (including errors that happen AFTER
627
+ * an interrupt was captured — those interrupts are stale), or
628
+ * hook-driven halt (including hooks that returned BOTH `ask`
629
+ * and `preventContinuation` — the halt wins, no resume is
630
+ * expected, sessions must drop). Every state where no resume
631
+ * is expected clears.
632
+ */
633
+ if (this.shouldClearHookSession(streamThrew)) this.hookRegistry?.clearSession(this.id);
634
+ /**
635
+ * Drop any halt signal raised mid-stream for this run so a
636
+ * subsequent `processStream` / `resume` starts with clean state.
637
+ * The Run captured `_haltedReason` already; the registry entry
638
+ * for this `sessionId` would otherwise spuriously trip the next
639
+ * loop. Other concurrent runs sharing this registry are
640
+ * unaffected — their entries live under their own session ids.
641
+ */
642
+ this.hookRegistry?.clearHaltSignal(this.id);
643
+ await disposeLangfuseHandler(langfuseHandler);
644
+ /**
645
+ * Break the reference chain that keeps heavy data alive via
646
+ * LangGraph's internal `__pregel_scratchpad.currentTaskInput` →
647
+ * `@langchain/core` `RunTree.extra[lc:child_config]` →
648
+ * Node.js `AsyncLocalStorage` context captured by timers/promises.
649
+ *
650
+ * Without this, base64-encoded images/PDFs in message content remain
651
+ * reachable from lingering `Timeout` handles until GC runs.
652
+ */
653
+ if (!this.skipCleanup) {
654
+ if (config.configurable != null) {
655
+ for (const key of Object.getOwnPropertySymbols(config.configurable)) {
656
+ const val = config.configurable[key];
657
+ if (val != null && typeof val === "object" && "currentTaskInput" in val) val.currentTaskInput = void 0;
658
+ delete config.configurable[key];
659
+ }
660
+ config.configurable = void 0;
661
+ }
662
+ config.callbacks = void 0;
663
+ }
664
+ const result = this.returnContent ? this.Graph.getContentParts() : void 0;
665
+ this.calibrationRatio = this.Graph.getCalibrationRatio();
666
+ /**
667
+ * Skip `clearHeavyState()` when the run paused on a clean HITL
668
+ * interrupt awaiting resume — `Run.resume()` re-enters the same
669
+ * `ToolNode` instance and needs the sidecars `clearHeavyState`
670
+ * would wipe (`toolCallStepIds` for completion-event step ids,
671
+ * the `_toolOutputRegistry` for `{{tool<i>turn<n>}}`
672
+ * substitutions, `sessions` for code-env continuity, plus the
673
+ * `hookRegistry` and `humanInTheLoop` config the interrupt
674
+ * branch itself relies on). Without preservation, the resumed
675
+ * tool completion would dispatch `ON_RUN_STEP_COMPLETED` with
676
+ * an empty step id and downstream stream consumers would drop
677
+ * the result.
678
+ *
679
+ * The natural-completion / error / hook-driven-halt paths still
680
+ * clean up — `_haltedReason != null` or `streamThrew` mean no
681
+ * resume is expected. Cross-process resume (host rebuilds the
682
+ * Run from scratch) is a separate concern; see
683
+ * `HumanInTheLoopConfig` JSDoc.
684
+ */
685
+ const awaitingResume = this.isAwaitingResume(streamThrew);
686
+ if (!this.skipCleanup && !awaitingResume) this.Graph.clearHeavyState();
687
+ this._streamResult = result;
688
+ }
689
+ return this._streamResult;
690
+ }
691
+ /**
692
+ * Returns the pending interrupt captured during the most recent
693
+ * `processStream` (or `resume`) invocation. `undefined` when the run
694
+ * either has not been streamed yet or completed without pausing.
695
+ *
696
+ * Hosts call this immediately after `processStream` returns to decide
697
+ * whether the run is awaiting human input. Persist the returned
698
+ * descriptor (alongside `thread_id` and the agent run config) so a
699
+ * later `resume(decisions)` can rebuild the run.
700
+ *
701
+ * The default `TPayload` is the SDK's `HumanInterruptPayload` union
702
+ * (`tool_approval` / `ask_user_question`), suitable for the common
703
+ * case where interrupts come from the built-in ToolNode or
704
+ * `askUserQuestion()` helper. Hosts that raise custom interrupts
705
+ * from custom graph nodes pass their own type — the SDK does not
706
+ * validate the runtime shape, it just transports whatever the
707
+ * `interrupt()` call carried. When in doubt, narrow with the
708
+ * `isToolApprovalInterrupt` / `isAskUserQuestionInterrupt` type
709
+ * guards (which accept `unknown`) before reading variant-specific
710
+ * fields.
711
+ */
712
+ getInterrupt() {
713
+ return this._interrupt;
714
+ }
715
+ /**
716
+ * Returns the reason a hook halted the run via
717
+ * `preventContinuation: true`, or `undefined` if no hook halted.
718
+ *
719
+ * Hosts inspect this after `processStream` returns to distinguish a
720
+ * natural completion (`undefined`) from a hook-driven halt (a
721
+ * truthy string). Independent from `getInterrupt()` — a halted run
722
+ * has no interrupt; an interrupted run has no halt reason.
723
+ */
724
+ getHaltReason() {
725
+ return this._haltedReason;
726
+ }
727
+ /**
728
+ * Resume a paused HITL run with the value the user (or whatever
729
+ * decided the interrupt) supplied. The default `TResume` covers the
730
+ * `tool_approval` interrupt (the common case): an array of decisions
731
+ * in `action_requests` order, or a record keyed by `tool_call_id`.
732
+ *
733
+ * For other interrupt types (e.g., `ask_user_question` →
734
+ * `AskUserQuestionResolution`, or any custom interrupt a host raises
735
+ * from a custom node), pass the type parameter and the SDK forwards
736
+ * the value through unchanged. LangGraph delivers it as the return
737
+ * value of the original `interrupt()` call inside the paused node.
738
+ *
739
+ * The host MUST construct this Run with the same `thread_id` and the
740
+ * same checkpointer as the original paused run; LangGraph rebuilds
741
+ * graph state from the checkpoint and re-enters the interrupted node
742
+ * from the start.
743
+ */
744
+ /**
745
+ * Returns the per-Run file checkpointer when
746
+ * `toolExecution.local.fileCheckpointing === true` was set on the
747
+ * RunConfig. Hosts can capture extra paths or call `rewind()`
748
+ * directly. Returns undefined when checkpointing is disabled.
749
+ *
750
+ * Construction-time invariant: the checkpointer is shared across
751
+ * every ToolNode the graph compiles (single-agent and multi-agent),
752
+ * so a `rewind()` call here unwinds writes made by ANY agent in the
753
+ * run.
754
+ */
755
+ getFileCheckpointer() {
756
+ return this.Graph?.getOrCreateFileCheckpointer();
757
+ }
758
+ /**
759
+ * Convenience wrapper that calls `rewind()` on the per-Run file
760
+ * checkpointer. Restores every file the local engine snapshotted
761
+ * during this Run to its pre-write content (and deletes any path
762
+ * that didn't exist before being created). Returns the count of
763
+ * paths processed; returns 0 when checkpointing is disabled.
764
+ */
765
+ async rewindFiles() {
766
+ const cp = this.getFileCheckpointer();
767
+ return cp == null ? 0 : cp.rewind();
768
+ }
769
+ async resume(resumeValue, callerConfig, streamOptions) {
770
+ const interruptId = this._interrupt?.interruptId;
771
+ const scopedResume = typeof interruptId === "string" && interruptId.length > 0 && !isLangGraphResumeMapForInterrupt(resumeValue, interruptId) ? { [interruptId]: resumeValue } : resumeValue;
772
+ const resumeConfig = await this.resolveInterruptResumeConfig(callerConfig);
773
+ return this.processStream(new Command({ resume: scopedResume }), resumeConfig, streamOptions);
774
+ }
775
+ async resolveInterruptResumeConfig(callerConfig) {
776
+ const interrupt = this._interrupt;
777
+ const interruptId = interrupt?.interruptId;
778
+ const stateHistory = this.graphRunnable?.getStateHistory;
779
+ if (interrupt?.checkpointId != null && interrupt.checkpointId.length > 0) return {
780
+ ...callerConfig,
781
+ configurable: {
782
+ ...callerConfig.configurable,
783
+ checkpoint_id: interrupt.checkpointId,
784
+ ...typeof interrupt.checkpointNs === "string" ? { checkpoint_ns: interrupt.checkpointNs } : {}
785
+ }
786
+ };
787
+ if (interrupt == null || typeof interruptId !== "string" || interruptId.length === 0 || typeof stateHistory !== "function") return callerConfig;
788
+ for await (const snapshot of stateHistory.call(this.graphRunnable, callerConfig)) {
789
+ const hasMatchingInterrupt = snapshot.tasks?.some((task) => task.interrupts?.some((interrupt) => interrupt.id === interruptId) === true) === true;
790
+ const checkpointConfigurable = snapshot.config?.configurable;
791
+ if (!hasMatchingInterrupt || checkpointConfigurable == null) continue;
792
+ const checkpointId = checkpointConfigurable.checkpoint_id;
793
+ const checkpointNs = checkpointConfigurable.checkpoint_ns;
794
+ if (typeof checkpointId === "string" && checkpointId.length > 0) {
795
+ this._interrupt = {
796
+ ...interrupt,
797
+ checkpointId,
798
+ ...typeof checkpointNs === "string" ? { checkpointNs } : {}
799
+ };
800
+ return {
801
+ ...callerConfig,
802
+ configurable: {
803
+ ...callerConfig.configurable,
804
+ checkpoint_id: checkpointId,
805
+ ...typeof checkpointNs === "string" ? { checkpoint_ns: checkpointNs } : {}
806
+ }
807
+ };
808
+ }
809
+ }
810
+ return callerConfig;
811
+ }
812
+ createSystemCallback(clientCallbacks, key) {
813
+ return ((...args) => {
814
+ const clientCallback = clientCallbacks[key];
815
+ if (clientCallback && this.Graph) clientCallback(this.Graph, ...args);
816
+ });
817
+ }
818
+ getCallbacks(clientCallbacks) {
819
+ return {
820
+ ["handleToolError"]: this.createSystemCallback(clientCallbacks, "handleToolError"),
821
+ ["handleToolStart"]: this.createSystemCallback(clientCallbacks, "handleToolStart"),
822
+ ["handleToolEnd"]: this.createSystemCallback(clientCallbacks, "handleToolEnd")
823
+ };
824
+ }
825
+ async generateTitle({ provider, inputText, contentParts, titlePrompt, clientOptions, chainOptions, skipLanguage, titleMethod = "completion", titlePromptTemplate }) {
826
+ let titleLangfuseHandler;
827
+ let titleLangfuseConfig;
828
+ let titleUserId;
829
+ let titleSessionId;
830
+ const titleContext = this.Graph == null ? void 0 : this.Graph.agentContexts.get(this.Graph.defaultAgentId);
831
+ const traceMetadata = createLangfuseTraceMetadata({
832
+ messageId: "title-" + this.id,
833
+ agentName: titleContext?.name
834
+ });
835
+ const titleRunName = getLangfuseTraceName(traceMetadata, "LibreChat Title");
836
+ if (chainOptions != null) {
837
+ titleUserId = typeof chainOptions.configurable?.user_id === "string" ? chainOptions.configurable.user_id : void 0;
838
+ titleSessionId = typeof chainOptions.configurable?.thread_id === "string" ? chainOptions.configurable.thread_id : void 0;
839
+ titleLangfuseConfig = resolveLangfuseConfig(this.langfuse, titleContext?.langfuse);
840
+ initializeLangfuseTracing(titleLangfuseConfig);
841
+ titleLangfuseHandler = createLangfuseHandler({
842
+ langfuse: titleLangfuseConfig,
843
+ userId: titleUserId,
844
+ sessionId: titleSessionId,
845
+ traceMetadata,
846
+ tags: ["librechat", "title"]
847
+ });
848
+ if (titleLangfuseHandler != null) chainOptions.callbacks = appendCallbacks(chainOptions.callbacks, [titleLangfuseHandler]);
849
+ }
850
+ const convoTemplate = PromptTemplate.fromTemplate(titlePromptTemplate ?? "User: {input}\nAI: {output}");
851
+ const response = contentParts.map((part) => {
852
+ if (part?.type === "text") return part.text;
853
+ return "";
854
+ }).join("\n");
855
+ const model = initializeModel({
856
+ provider,
857
+ clientOptions
858
+ });
859
+ if (isOpenAILike(provider) && (model instanceof ChatOpenAI || model instanceof AzureChatOpenAI)) {
860
+ model.temperature = clientOptions?.temperature;
861
+ model.topP = clientOptions?.topP;
862
+ model.frequencyPenalty = clientOptions?.frequencyPenalty;
863
+ model.presencePenalty = clientOptions?.presencePenalty;
864
+ model.n = clientOptions?.n;
865
+ }
866
+ const convoToTitleInput = new RunnableLambda({ func: (promptValue) => ({
867
+ convo: promptValue.value,
868
+ inputText,
869
+ skipLanguage
870
+ }) }).withConfig({ runName: "ConvoTransform" });
871
+ const titleChain = titleMethod === "completion" ? await createCompletionTitleRunnable(model, titlePrompt) : await createTitleRunnable(model, titlePrompt);
872
+ /** Pipes `convoTemplate` -> `transformer` -> `titleChain` */
873
+ const fullChain = convoTemplate.withConfig({ runName: "ConvoTemplate" }).pipe(convoToTitleInput).pipe(titleChain).withConfig({ runName: "TitleChain" });
874
+ const invokeConfig = Object.assign({}, chainOptions, {
875
+ run_id: this.id,
876
+ runId: this.id,
877
+ runName: chainOptions?.runName ?? titleRunName
878
+ });
879
+ const invokeTitleChain = (runtimeConfig) => withLangfuseAttributes({
880
+ langfuse: titleLangfuseConfig,
881
+ userId: titleUserId,
882
+ sessionId: titleSessionId,
883
+ traceName: runtimeConfig.runName ?? titleRunName,
884
+ traceMetadata,
885
+ tags: ["librechat", "title"]
886
+ }, () => fullChain.invoke({
887
+ input: inputText,
888
+ output: response
889
+ }, runtimeConfig));
890
+ try {
891
+ try {
892
+ return await withLangfuseToolOutputTracingConfig(this.langfuse, () => invokeTitleChain(invokeConfig), titleContext?.langfuse);
893
+ } catch (_e) {
894
+ const langfuseHandler = findCallback(invokeConfig.callbacks, isLangfuseCallbackHandler);
895
+ const { callbacks: _cb, ...rest } = invokeConfig;
896
+ const safeConfig = Object.assign({}, rest, { callbacks: langfuseHandler ? [langfuseHandler] : [] });
897
+ return await withLangfuseToolOutputTracingConfig(this.langfuse, () => invokeTitleChain(safeConfig), titleContext?.langfuse);
898
+ }
899
+ } finally {
900
+ await disposeLangfuseHandler(titleLangfuseHandler);
901
+ }
902
+ }
903
+ };
1096
904
  function findLastMessageOfType(messages, type) {
1097
- for (let i = messages.length - 1; i >= 0; i--) {
1098
- if (messages[i].getType() === type) {
1099
- return messages[i];
1100
- }
1101
- }
1102
- return undefined;
905
+ for (let i = messages.length - 1; i >= 0; i--) if (messages[i].getType() === type) return messages[i];
1103
906
  }
1104
907
  function extractPromptText(message) {
1105
- const content = message.content;
1106
- if (typeof content === 'string') {
1107
- return content;
1108
- }
1109
- if (!Array.isArray(content)) {
1110
- return String(content);
1111
- }
1112
- const parts = [];
1113
- for (const block of content) {
1114
- if (typeof block === 'object' &&
1115
- 'type' in block &&
1116
- block.type === 'text' &&
1117
- 'text' in block &&
1118
- typeof block.text === 'string') {
1119
- parts.push(block.text);
1120
- }
1121
- }
1122
- return parts.join('\n');
908
+ const content = message.content;
909
+ if (typeof content === "string") return content;
910
+ if (!Array.isArray(content)) return String(content);
911
+ const parts = [];
912
+ for (const block of content) if (typeof block === "object" && "type" in block && block.type === "text" && "text" in block && typeof block.text === "string") parts.push(block.text);
913
+ return parts.join("\n");
1123
914
  }
1124
-
915
+ //#endregion
1125
916
  export { Run, defaultOmitOptions };
1126
- //# sourceMappingURL=run.mjs.map
917
+
918
+ //# sourceMappingURL=run.mjs.map