@librechat/agents 3.8.0 → 3.8.2

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 (248) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +13 -3
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +176 -55
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/graphs/MultiAgentGraph.cjs +16 -6
  6. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  7. package/dist/cjs/hitl/approvalReview.cjs +89 -0
  8. package/dist/cjs/hitl/approvalReview.cjs.map +1 -0
  9. package/dist/cjs/langfuse.cjs +42 -0
  10. package/dist/cjs/langfuse.cjs.map +1 -1
  11. package/dist/cjs/langfuseToolOutputTracing.cjs +42 -0
  12. package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
  13. package/dist/cjs/langfuseTraceShaping.cjs +7 -1
  14. package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
  15. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +6 -10
  16. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  17. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +2 -7
  18. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  19. package/dist/cjs/llm/contextPressureMeter.cjs +51 -10
  20. package/dist/cjs/llm/contextPressureMeter.cjs.map +1 -1
  21. package/dist/cjs/llm/init.cjs +1 -1
  22. package/dist/cjs/llm/invoke.cjs +2 -2
  23. package/dist/cjs/llm/openai/index.cjs +4 -3
  24. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  25. package/dist/cjs/llm/openai/utils/index.cjs +5 -4
  26. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  27. package/dist/cjs/llm/preempt.cjs +3 -2
  28. package/dist/cjs/llm/preempt.cjs.map +1 -1
  29. package/dist/cjs/llm/prepareProviderRequest.cjs +9 -7
  30. package/dist/cjs/llm/prepareProviderRequest.cjs.map +1 -1
  31. package/dist/cjs/llm/providers.cjs +1 -1
  32. package/dist/cjs/main.cjs +14 -8
  33. package/dist/cjs/messages/alternation.cjs +1 -5
  34. package/dist/cjs/messages/alternation.cjs.map +1 -1
  35. package/dist/cjs/messages/budget.cjs +206 -7
  36. package/dist/cjs/messages/budget.cjs.map +1 -1
  37. package/dist/cjs/messages/cache.cjs +3 -9
  38. package/dist/cjs/messages/cache.cjs.map +1 -1
  39. package/dist/cjs/messages/core.cjs +26 -38
  40. package/dist/cjs/messages/core.cjs.map +1 -1
  41. package/dist/cjs/messages/format.cjs +132 -43
  42. package/dist/cjs/messages/format.cjs.map +1 -1
  43. package/dist/cjs/messages/index.cjs +2 -0
  44. package/dist/cjs/messages/prune.cjs +14 -9
  45. package/dist/cjs/messages/prune.cjs.map +1 -1
  46. package/dist/cjs/messages/reasoningTypes.cjs +21 -0
  47. package/dist/cjs/messages/reasoningTypes.cjs.map +1 -0
  48. package/dist/cjs/messages/recency.cjs +15 -94
  49. package/dist/cjs/messages/recency.cjs.map +1 -1
  50. package/dist/cjs/messages/toolHistoryProjection.cjs +243 -0
  51. package/dist/cjs/messages/toolHistoryProjection.cjs.map +1 -0
  52. package/dist/cjs/messages/toolResultTypes.cjs +145 -4
  53. package/dist/cjs/messages/toolResultTypes.cjs.map +1 -1
  54. package/dist/cjs/run.cjs +22 -10
  55. package/dist/cjs/run.cjs.map +1 -1
  56. package/dist/cjs/session/AgentSession.cjs +7 -5
  57. package/dist/cjs/session/AgentSession.cjs.map +1 -1
  58. package/dist/cjs/session/JsonlSessionStore.cjs +3 -0
  59. package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -1
  60. package/dist/cjs/session/index.cjs +1 -1
  61. package/dist/cjs/session/sessionProjection.cjs +75 -0
  62. package/dist/cjs/session/sessionProjection.cjs.map +1 -0
  63. package/dist/cjs/stream.cjs +20 -19
  64. package/dist/cjs/stream.cjs.map +1 -1
  65. package/dist/cjs/summarization/index.cjs.map +1 -1
  66. package/dist/cjs/summarization/node.cjs +19 -9
  67. package/dist/cjs/summarization/node.cjs.map +1 -1
  68. package/dist/cjs/summarization/shared.cjs +9 -0
  69. package/dist/cjs/summarization/shared.cjs.map +1 -1
  70. package/dist/cjs/tools/ToolNode.cjs +256 -84
  71. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  72. package/dist/cjs/tools/local/CompileCheckTool.cjs +1 -1
  73. package/dist/cjs/tools/local/LocalCodingTools.cjs +1 -1
  74. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +24 -9
  75. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  76. package/dist/cjs/tools/subagent/SubagentReplay.cjs +2 -0
  77. package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -1
  78. package/dist/cjs/tools/toolBatchReplay.cjs +187 -0
  79. package/dist/cjs/tools/toolBatchReplay.cjs.map +1 -0
  80. package/dist/cjs/tools/toolOutputReferences.cjs +12 -0
  81. package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
  82. package/dist/cjs/types/hitl.cjs +4 -0
  83. package/dist/cjs/types/hitl.cjs.map +1 -1
  84. package/dist/cjs/utils/events.cjs +13 -0
  85. package/dist/cjs/utils/events.cjs.map +1 -1
  86. package/dist/cjs/utils/tokens.cjs +105 -0
  87. package/dist/cjs/utils/tokens.cjs.map +1 -1
  88. package/dist/esm/agents/AgentContext.mjs +13 -3
  89. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  90. package/dist/esm/graphs/Graph.mjs +178 -57
  91. package/dist/esm/graphs/Graph.mjs.map +1 -1
  92. package/dist/esm/graphs/MultiAgentGraph.mjs +16 -6
  93. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  94. package/dist/esm/hitl/approvalReview.mjs +83 -0
  95. package/dist/esm/hitl/approvalReview.mjs.map +1 -0
  96. package/dist/esm/langfuse.mjs +43 -2
  97. package/dist/esm/langfuse.mjs.map +1 -1
  98. package/dist/esm/langfuseToolOutputTracing.mjs +41 -1
  99. package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
  100. package/dist/esm/langfuseTraceShaping.mjs +7 -1
  101. package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
  102. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +6 -10
  103. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  104. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +2 -7
  105. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  106. package/dist/esm/llm/contextPressureMeter.mjs +52 -11
  107. package/dist/esm/llm/contextPressureMeter.mjs.map +1 -1
  108. package/dist/esm/llm/init.mjs +1 -1
  109. package/dist/esm/llm/invoke.mjs +2 -2
  110. package/dist/esm/llm/openai/index.mjs +2 -1
  111. package/dist/esm/llm/openai/index.mjs.map +1 -1
  112. package/dist/esm/llm/openai/utils/index.mjs +5 -4
  113. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  114. package/dist/esm/llm/preempt.mjs +2 -1
  115. package/dist/esm/llm/preempt.mjs.map +1 -1
  116. package/dist/esm/llm/prepareProviderRequest.mjs +9 -7
  117. package/dist/esm/llm/prepareProviderRequest.mjs.map +1 -1
  118. package/dist/esm/llm/providers.mjs +1 -1
  119. package/dist/esm/main.mjs +13 -10
  120. package/dist/esm/messages/alternation.mjs +2 -6
  121. package/dist/esm/messages/alternation.mjs.map +1 -1
  122. package/dist/esm/messages/budget.mjs +206 -8
  123. package/dist/esm/messages/budget.mjs.map +1 -1
  124. package/dist/esm/messages/cache.mjs +3 -9
  125. package/dist/esm/messages/cache.mjs.map +1 -1
  126. package/dist/esm/messages/core.mjs +24 -34
  127. package/dist/esm/messages/core.mjs.map +1 -1
  128. package/dist/esm/messages/format.mjs +133 -44
  129. package/dist/esm/messages/format.mjs.map +1 -1
  130. package/dist/esm/messages/index.mjs +2 -0
  131. package/dist/esm/messages/prune.mjs +14 -9
  132. package/dist/esm/messages/prune.mjs.map +1 -1
  133. package/dist/esm/messages/reasoningTypes.mjs +20 -0
  134. package/dist/esm/messages/reasoningTypes.mjs.map +1 -0
  135. package/dist/esm/messages/recency.mjs +16 -95
  136. package/dist/esm/messages/recency.mjs.map +1 -1
  137. package/dist/esm/messages/toolHistoryProjection.mjs +237 -0
  138. package/dist/esm/messages/toolHistoryProjection.mjs.map +1 -0
  139. package/dist/esm/messages/toolResultTypes.mjs +141 -4
  140. package/dist/esm/messages/toolResultTypes.mjs.map +1 -1
  141. package/dist/esm/run.mjs +23 -11
  142. package/dist/esm/run.mjs.map +1 -1
  143. package/dist/esm/session/AgentSession.mjs +7 -5
  144. package/dist/esm/session/AgentSession.mjs.map +1 -1
  145. package/dist/esm/session/JsonlSessionStore.mjs +3 -0
  146. package/dist/esm/session/JsonlSessionStore.mjs.map +1 -1
  147. package/dist/esm/session/index.mjs +1 -1
  148. package/dist/esm/session/sessionProjection.mjs +72 -0
  149. package/dist/esm/session/sessionProjection.mjs.map +1 -0
  150. package/dist/esm/stream.mjs +17 -16
  151. package/dist/esm/stream.mjs.map +1 -1
  152. package/dist/esm/summarization/index.mjs.map +1 -1
  153. package/dist/esm/summarization/node.mjs +20 -10
  154. package/dist/esm/summarization/node.mjs.map +1 -1
  155. package/dist/esm/summarization/shared.mjs +9 -1
  156. package/dist/esm/summarization/shared.mjs.map +1 -1
  157. package/dist/esm/tools/ToolNode.mjs +256 -84
  158. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  159. package/dist/esm/tools/local/CompileCheckTool.mjs +1 -1
  160. package/dist/esm/tools/local/LocalCodingTools.mjs +1 -1
  161. package/dist/esm/tools/subagent/SubagentExecutor.mjs +24 -9
  162. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  163. package/dist/esm/tools/subagent/SubagentReplay.mjs +1 -1
  164. package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -1
  165. package/dist/esm/tools/toolBatchReplay.mjs +176 -0
  166. package/dist/esm/tools/toolBatchReplay.mjs.map +1 -0
  167. package/dist/esm/tools/toolOutputReferences.mjs +12 -0
  168. package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
  169. package/dist/esm/types/hitl.mjs +4 -1
  170. package/dist/esm/types/hitl.mjs.map +1 -1
  171. package/dist/esm/utils/events.mjs +13 -0
  172. package/dist/esm/utils/events.mjs.map +1 -1
  173. package/dist/esm/utils/tokens.mjs +105 -1
  174. package/dist/esm/utils/tokens.mjs.map +1 -1
  175. package/dist/types/agents/AgentContext.d.ts +13 -1
  176. package/dist/types/graphs/Graph.d.ts +27 -0
  177. package/dist/types/hitl/approvalReview.d.ts +32 -0
  178. package/dist/types/hooks/types.d.ts +4 -2
  179. package/dist/types/index.d.ts +1 -0
  180. package/dist/types/langfuse.d.ts +5 -0
  181. package/dist/types/langfuseToolOutputTracing.d.ts +2 -0
  182. package/dist/types/llm/contextPressureMeter.d.ts +2 -1
  183. package/dist/types/llm/prepareProviderRequest.d.ts +7 -1
  184. package/dist/types/messages/budget.d.ts +15 -10
  185. package/dist/types/messages/core.d.ts +8 -17
  186. package/dist/types/messages/format.d.ts +3 -2
  187. package/dist/types/messages/index.d.ts +1 -1
  188. package/dist/types/messages/prune.d.ts +1 -1
  189. package/dist/types/messages/reasoningTypes.d.ts +7 -0
  190. package/dist/types/messages/recency.d.ts +3 -0
  191. package/dist/types/messages/toolHistoryProjection.d.ts +65 -0
  192. package/dist/types/messages/toolResultTypes.d.ts +31 -1
  193. package/dist/types/session/sessionProjection.d.ts +10 -0
  194. package/dist/types/summarization/index.d.ts +2 -1
  195. package/dist/types/summarization/node.d.ts +1 -0
  196. package/dist/types/summarization/shared.d.ts +12 -0
  197. package/dist/types/tools/ToolNode.d.ts +12 -6
  198. package/dist/types/tools/subagent/SubagentReplay.d.ts +2 -0
  199. package/dist/types/tools/toolBatchReplay.d.ts +54 -0
  200. package/dist/types/tools/toolOutputReferences.d.ts +2 -0
  201. package/dist/types/types/graph.d.ts +20 -0
  202. package/dist/types/types/run.d.ts +4 -0
  203. package/dist/types/types/summarize.d.ts +4 -1
  204. package/dist/types/utils/tokens.d.ts +2 -1
  205. package/package.json +1 -1
  206. package/src/agents/AgentContext.ts +34 -3
  207. package/src/graphs/Graph.ts +465 -147
  208. package/src/graphs/MultiAgentGraph.ts +28 -6
  209. package/src/hitl/approvalReview.ts +209 -0
  210. package/src/hooks/types.ts +4 -1
  211. package/src/index.ts +1 -0
  212. package/src/langfuse.ts +67 -9
  213. package/src/langfuseToolOutputTracing.ts +91 -0
  214. package/src/langfuseTraceShaping.ts +17 -4
  215. package/src/llm/anthropic/utils/message_inputs.ts +5 -10
  216. package/src/llm/bedrock/utils/message_inputs.ts +2 -8
  217. package/src/llm/contextPressureMeter.ts +80 -12
  218. package/src/llm/openai/utils/index.ts +5 -4
  219. package/src/llm/prepareProviderRequest.ts +21 -16
  220. package/src/messages/alternation.ts +2 -15
  221. package/src/messages/budget.ts +439 -23
  222. package/src/messages/cache.ts +8 -9
  223. package/src/messages/core.ts +65 -95
  224. package/src/messages/format.ts +262 -78
  225. package/src/messages/index.ts +1 -1
  226. package/src/messages/prune.ts +31 -12
  227. package/src/messages/reasoningTypes.ts +23 -0
  228. package/src/messages/recency.ts +35 -179
  229. package/src/messages/toolHistoryProjection.ts +460 -0
  230. package/src/messages/toolResultTypes.ts +331 -100
  231. package/src/run.ts +44 -22
  232. package/src/session/AgentSession.ts +33 -16
  233. package/src/session/JsonlSessionStore.ts +6 -0
  234. package/src/session/sessionProjection.ts +126 -0
  235. package/src/stream.ts +14 -19
  236. package/src/summarization/index.ts +2 -0
  237. package/src/summarization/node.ts +62 -13
  238. package/src/summarization/shared.ts +23 -0
  239. package/src/tools/ToolNode.ts +518 -184
  240. package/src/tools/subagent/SubagentExecutor.ts +35 -6
  241. package/src/tools/subagent/SubagentReplay.ts +2 -2
  242. package/src/tools/toolBatchReplay.ts +395 -0
  243. package/src/tools/toolOutputReferences.ts +16 -0
  244. package/src/types/graph.ts +20 -0
  245. package/src/types/run.ts +4 -0
  246. package/src/types/summarize.ts +4 -1
  247. package/src/utils/events.ts +19 -0
  248. package/src/utils/tokens.ts +183 -0
@@ -1,19 +1,21 @@
1
- const require_truncation = require("../utils/truncation.cjs");
2
1
  const require_enum = require("../common/enum.cjs");
3
2
  require("../common/index.cjs");
3
+ const require_truncation = require("../utils/truncation.cjs");
4
4
  const require_runStepResume = require("./runStepResume.cjs");
5
5
  const require_SubagentReplay = require("./subagent/SubagentReplay.cjs");
6
6
  const require_misc = require("../utils/misc.cjs");
7
7
  const require_langfuseRuntimeScope = require("../langfuseRuntimeScope.cjs");
8
8
  const require_toolContent = require("../utils/toolContent.cjs");
9
9
  const require_provenance = require("../messages/provenance.cjs");
10
+ const require_eagerEventExecution = require("./eagerEventExecution.cjs");
11
+ const require_approvalReview = require("../hitl/approvalReview.cjs");
10
12
  const require_types = require("../hooks/types.cjs");
13
+ const require_toolBatchReplay = require("./toolBatchReplay.cjs");
11
14
  const require_executeHooks = require("../hooks/executeHooks.cjs");
12
15
  require("../hooks/index.cjs");
13
16
  const require_injected = require("../messages/injected.cjs");
14
17
  const require_events = require("../utils/events.cjs");
15
18
  const require_streamLimits = require("../llm/streamLimits.cjs");
16
- const require_eagerEventExecution = require("./eagerEventExecution.cjs");
17
19
  const require_intentArg = require("./intentArg.cjs");
18
20
  const require_toolOutputReferences = require("./toolOutputReferences.cjs");
19
21
  const require_preparedSubagents = require("./preparedSubagents.cjs");
@@ -28,10 +30,17 @@ let _langchain_core_runnables = require("@langchain/core/runnables");
28
30
  let _langchain_core_messages = require("@langchain/core/messages");
29
31
  let _langchain_langgraph = require("@langchain/langgraph");
30
32
  let _langchain_core_singletons = require("@langchain/core/singletons");
33
+ let crypto = require("crypto");
31
34
  //#region src/tools/ToolNode.ts
32
- function stripRunBreakerScope(configurable) {
33
- if (configurable == null || !("lc_run_breaker_scope" in configurable)) return configurable;
34
- const { [require_streamLimits.RUN_BREAKER_SCOPE_CONFIG_KEY]: _scope, ...rest } = configurable;
35
+ function stripToolApprovalReviewConfig(configurable) {
36
+ if (configurable == null || !("__librechat_tool_approval_review" in configurable) && !("__librechat_tool_batch_replay" in configurable)) return configurable;
37
+ const { [require_approvalReview.TOOL_APPROVAL_REVIEW_CONFIG_KEY]: _review, [require_toolBatchReplay.TOOL_BATCH_REPLAY_KEY]: _replay, ...rest } = configurable;
38
+ return rest;
39
+ }
40
+ function stripHostExecutionConfig(configurable) {
41
+ const withoutReview = stripToolApprovalReviewConfig(configurable);
42
+ if (withoutReview == null || !("lc_run_breaker_scope" in withoutReview)) return withoutReview;
43
+ const { [require_streamLimits.RUN_BREAKER_SCOPE_CONFIG_KEY]: _scope, ...rest } = withoutReview;
35
44
  return rest;
36
45
  }
37
46
  function createToolApprovalReplayKey(config, agentId, toolUseId) {
@@ -60,6 +69,9 @@ function withSubagentReplayBatch(config, replayBatchKey) {
60
69
  }
61
70
  };
62
71
  }
72
+ function directReplayKey(call, batchIndex) {
73
+ return call.id == null || call.id === "" ? JSON.stringify(["position", batchIndex]) : JSON.stringify(["id", call.id]);
74
+ }
63
75
  function createToolErrorOwnership() {
64
76
  return {
65
77
  handlerOwned: /* @__PURE__ */ new Set(),
@@ -113,20 +125,14 @@ function describeOfferedShape(value) {
113
125
  function findAssistantBatch(messages) {
114
126
  for (let i = messages.length - 1; i >= 0; i--) {
115
127
  const message = messages[i];
116
- if ((0, _langchain_core_messages.isAIMessage)(message)) return {
117
- message,
118
- index: i,
119
- messageCount: messages.length
120
- };
128
+ if ((0, _langchain_core_messages.isAIMessage)(message)) return { message };
121
129
  }
122
130
  }
123
- function getAssistantBatchReplayKey(batch, runId, threadId) {
131
+ function getAssistantBatchReplayKey(batch, threadId) {
124
132
  return JSON.stringify([
125
- runId ?? null,
126
133
  threadId ?? null,
127
- batch.message.id ?? null,
128
- batch.index,
129
- batch.messageCount
134
+ batch.message.id == null || batch.message.id === "" ? null : batch.message.id,
135
+ (0, crypto.createHash)("sha256").update(require_eagerEventExecution.stableStringify(batch.message.tool_calls ?? [])).digest("hex")
130
136
  ]);
131
137
  }
132
138
  function buildToolApprovalInterruptPayload(askEntries, hookSessionId) {
@@ -244,7 +250,7 @@ var ToolNode = class extends require_run.RunnableCallable {
244
250
  toolUsageCount;
245
251
  toolCallTurns = /* @__PURE__ */ new Map();
246
252
  directPathTurns = /* @__PURE__ */ new Map();
247
- settledInterruptingResults = /* @__PURE__ */ new Map();
253
+ settledDirectResultsByBatch = /* @__PURE__ */ new Map();
248
254
  toolRegistry;
249
255
  toolDefinitions;
250
256
  localImplementationNames = /* @__PURE__ */ new Set();
@@ -279,16 +285,44 @@ var ToolNode = class extends require_run.RunnableCallable {
279
285
  name: name ?? TOOL_NODE_RUN_NAME,
280
286
  tags,
281
287
  func: async (input, config) => {
288
+ const messages = Array.isArray(input) ? input : input.messages;
289
+ const assistantBatch = messages == null ? void 0 : findAssistantBatch(messages);
290
+ const activeReplayKey = assistantBatch == null || require_toolBatchReplay.getToolBatchReplayScope(config) == null ? void 0 : getAssistantBatchReplayKey(assistantBatch, require_toolBatchReplay.getToolBatchReplayScope(config));
291
+ const replayOwner = require_toolBatchReplay.getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? "");
292
+ const reviewEvidence = require_approvalReview.getToolApprovalReviewEvidence(config);
293
+ if (reviewEvidence?.owner != null && reviewEvidence.owner !== replayOwner) {
294
+ const calls = this.isSendInput(input) ? [input.lg_tool_call] : assistantBatch?.message.tool_calls ?? [];
295
+ const trustedParentCallIds = new Set(calls.filter((call) => call.name === "subagent" && this.toolMap.get(call.name)?.[require_SubagentReplay.SUBAGENT_REPLAY_CONTROLLER] != null).map((call) => call.id ?? ""));
296
+ if (!(require_toolBatchReplay.getToolBatchReplayState(config.configurable)?.records.some((record) => record.owner === replayOwner && record.batch === activeReplayKey) === true) || !require_toolBatchReplay.isChildToolApprovalOwner(config, reviewEvidence.owner, trustedParentCallIds)) throw new Error("Tool approval execution owner changed before resume — failing closed");
297
+ }
298
+ const referenceReplay = {};
299
+ const restoredBatches = await require_toolBatchReplay.restoreToolBatchReplayState(config, replayOwner);
300
+ if (restoredBatches.length > 0 && !restoredBatches.some(({ batch }) => batch === activeReplayKey)) throw new Error("Tool batch proposal changed before approval replay — failing closed");
301
+ if (activeReplayKey != null && require_toolBatchReplay.getToolBatchReplayState(config.configurable) != null) this.settledDirectResultsByBatch.delete(activeReplayKey);
302
+ for (const { batch, results, turnState, referenceState } of restoredBatches) {
303
+ if (batch !== activeReplayKey) continue;
304
+ this.settledDirectResultsByBatch.set(batch, new Map(results));
305
+ if (turnState != null) this.restoreSubagentResumeState(turnState);
306
+ referenceReplay.state = referenceState;
307
+ }
282
308
  restoreRunStepResumeState?.(input.runStepState, config);
283
309
  let result;
284
310
  try {
285
- result = await this.run(input, config);
311
+ result = await this.run(input, config, referenceReplay);
286
312
  } catch (error) {
287
- if (!(0, _langchain_langgraph.isGraphInterrupt)(error) || createRunStepResumeState == null) throw error;
288
- const resumeState = createRunStepResumeState();
289
- throw new _langchain_langgraph.GraphInterrupt(error.interrupts.map((pendingInterrupt) => ({
290
- ...pendingInterrupt,
291
- value: require_runStepResume.attachRunStepResumeState(pendingInterrupt.value, resumeState)
313
+ if (!(0, _langchain_langgraph.isGraphInterrupt)(error)) throw error;
314
+ const resumeState = createRunStepResumeState?.();
315
+ const activeResults = activeReplayKey == null ? void 0 : this.settledDirectResultsByBatch.get(activeReplayKey);
316
+ const activeBatches = activeReplayKey == null ? /* @__PURE__ */ new Map() : new Map([[activeReplayKey, activeResults ?? /* @__PURE__ */ new Map()]]);
317
+ const turnState = this.createSubagentResumeState();
318
+ const activeCallIds = new Set(assistantBatch?.message.tool_calls?.map((call) => call.id));
319
+ turnState.directPathTurns = turnState.directPathTurns.filter(({ toolCallId }) => activeCallIds.has(toolCallId));
320
+ throw new _langchain_langgraph.GraphInterrupt(await Promise.all(error.interrupts.map(async (pendingInterrupt) => {
321
+ const value = await require_toolBatchReplay.attachToolBatchReplayState(pendingInterrupt.value, replayOwner, activeBatches, turnState, referenceReplay.state);
322
+ return {
323
+ ...pendingInterrupt,
324
+ value: resumeState == null ? value : require_runStepResume.attachRunStepResumeState(value, resumeState)
325
+ };
292
326
  })));
293
327
  }
294
328
  if (createRunStepResumeState == null) return result;
@@ -416,7 +450,7 @@ var ToolNode = class extends require_run.RunnableCallable {
416
450
  }
417
451
  clearDirectPathTurns() {
418
452
  this.directPathTurns.clear();
419
- this.settledInterruptingResults.clear();
453
+ this.settledDirectResultsByBatch.clear();
420
454
  }
421
455
  getCallerCapabilityProjection() {
422
456
  const discoveredToolNames = new Set(this.getDiscoveredToolNames?.() ?? []);
@@ -486,11 +520,16 @@ var ToolNode = class extends require_run.RunnableCallable {
486
520
  }
487
521
  invokeWithRuntime(tool, invokeParams, call, config, runInput) {
488
522
  const lgConfig = config;
489
- const runtime = {
523
+ const isTrustedSubagentHop = call.name === "subagent" && tool[require_SubagentReplay.SUBAGENT_REPLAY_CONTROLLER] != null;
524
+ const toolConfig = {
490
525
  ...config,
526
+ configurable: isTrustedSubagentHop ? config.configurable : stripToolApprovalReviewConfig(config.configurable)
527
+ };
528
+ const runtime = {
529
+ ...toolConfig,
491
530
  state: runInput,
492
531
  toolCallId: call.id ?? "",
493
- config,
532
+ config: toolConfig,
494
533
  context: lgConfig.context,
495
534
  store: lgConfig.store ?? null,
496
535
  writer: lgConfig.writer ?? config.configurable?.writer ?? null
@@ -576,6 +615,8 @@ var ToolNode = class extends require_run.RunnableCallable {
576
615
  if (call.id != null && call.id !== "") this.toolCallTurns.set(call.id, next);
577
616
  return next;
578
617
  })();
618
+ this.recordToolUsageTurn(call.name, usageCount, call.id);
619
+ if (batchContext.replayBatchKey != null && call.id != null && call.id !== "") this.directPathTurns.set(call.id, usageCount);
579
620
  let args = call.args;
580
621
  if (resolveFn != null) {
581
622
  const { resolved, unresolved } = resolveFn(runId, args);
@@ -724,6 +765,7 @@ var ToolNode = class extends require_run.RunnableCallable {
724
765
  throw new require_preparedSubagents.PreparedSubagentError("Tool hooks changed after a subagent started.");
725
766
  }
726
767
  const replayController = this.toolMap.get(call.name)?.[require_SubagentReplay.SUBAGENT_REPLAY_CONTROLLER];
768
+ const isTrustedSubagentReviewHop = call.name === "subagent" && replayController != null;
727
769
  const replayConfig = replayController == null ? config : withSubagentReplayBatch(config, batchContext.replayBatchKey);
728
770
  const settledOutput = await replayController?.getSettledOutput(call, replayConfig);
729
771
  if (settledOutput != null) {
@@ -754,9 +796,12 @@ var ToolNode = class extends require_run.RunnableCallable {
754
796
  const approvalReplayKey = call.id == null || call.id === "" ? void 0 : createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", call.id);
755
797
  const approvalReplaySessionId = getToolApprovalReplaySessionId(config, runId);
756
798
  const pendingApproval = approvalReplayKey == null ? void 0 : hookRegistry?.getPendingToolApproval(approvalReplaySessionId, approvalReplayKey);
799
+ const approvalReviewEvidence = require_approvalReview.getToolApprovalReviewEvidence(config, require_toolBatchReplay.getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? ""));
800
+ const reviewedApproval = require_approvalReview.getReviewedToolApproval(approvalReviewEvidence?.payload, call.id);
801
+ const approvalEvidenceAppliesToBatch = approvalReviewEvidence?.payload.action_requests.some((request) => batchContext.approvalScopeCallIds == null ? request.tool_call_id === call.id : batchContext.approvalScopeCallIds.has(request.tool_call_id)) === true;
757
802
  const hasPostHook = hookRegistry?.hasHookFor("PostToolUse", runId) === true;
758
803
  const hasFailureHook = hookRegistry?.hasHookFor("PostToolUseFailure", runId) === true;
759
- if (hookRegistry == null || !hasPreHook && pendingApproval == null && !hasPostHook && !hasFailureHook) {
804
+ if (!hasPreHook && pendingApproval == null && reviewedApproval == null && approvalReviewEvidence == null && !hasPostHook && !hasFailureHook) {
760
805
  const output = await this.runTool(call, replayConfig, batchContext);
761
806
  return output instanceof _langchain_core_messages.ToolMessage ? persistOutput(output) : output;
762
807
  }
@@ -774,18 +819,21 @@ var ToolNode = class extends require_run.RunnableCallable {
774
819
  }
775
820
  const turn = usageCount;
776
821
  const stepId = this.toolCallStepIds?.get(call.id ?? "") ?? "";
777
- let resolvedArgs = call.args;
778
822
  const hookResolveOptions = { substituteIntentKey: this.toolDeclaresBusinessIntent(call.name) };
779
- if (batchContext.preBatchSnapshot != null) {
780
- const { resolved } = batchContext.preBatchSnapshot.resolve(call.args, hookResolveOptions);
781
- resolvedArgs = resolved;
782
- } else if (this.toolOutputRegistry != null) {
783
- const { resolved } = this.toolOutputRegistry.resolve(registryRunId, call.args, hookResolveOptions);
784
- resolvedArgs = resolved;
785
- }
823
+ const resolveDirectArgs = (args) => {
824
+ if (batchContext.preBatchSnapshot != null) return batchContext.preBatchSnapshot.resolve(args, hookResolveOptions).resolved;
825
+ if (this.toolOutputRegistry != null) return this.toolOutputRegistry.resolve(registryRunId, args, hookResolveOptions).resolved;
826
+ return args;
827
+ };
828
+ const originalResolvedArgs = resolveDirectArgs(call.args);
829
+ let resolvedArgs = hasPreHook || pendingApproval != null ? originalResolvedArgs : reviewedApproval?.request.arguments ?? originalResolvedArgs;
786
830
  let effectiveCall = call;
787
- if (hasPreHook || pendingApproval != null) {
788
- const preResult = await require_executeHooks.executeHooks({
831
+ if (hasPreHook || pendingApproval != null || reviewedApproval != null || approvalReviewEvidence != null) {
832
+ const preResult = hookRegistry == null ? {
833
+ additionalContexts: [],
834
+ injectedMessages: [],
835
+ errors: []
836
+ } : await require_executeHooks.executeHooks({
789
837
  registry: hookRegistry,
790
838
  input: {
791
839
  hook_event_name: "PreToolUse",
@@ -803,16 +851,31 @@ var ToolNode = class extends require_run.RunnableCallable {
803
851
  matchQuery: call.name,
804
852
  onceReplayKey: approvalReplayKey,
805
853
  onceReplaySessionId: approvalReplaySessionId
806
- }).catch(() => void 0);
854
+ }).catch(() => approvalReviewEvidence == null ? void 0 : {
855
+ decision: "deny",
856
+ reason: "Approval policy could not be evaluated on resume",
857
+ additionalContexts: [],
858
+ injectedMessages: [],
859
+ errors: []
860
+ });
807
861
  if (preResult != null) {
808
862
  if (preResult.additionalContexts.length > 0) {
809
863
  replayAdditionalContexts.push(...preResult.additionalContexts);
810
864
  batchContext.additionalContextsSink?.push(...preResult.additionalContexts);
811
865
  }
812
- if (preResult.updatedInput != null) effectiveCall = {
813
- ...call,
814
- args: preResult.updatedInput
815
- };
866
+ if (preResult.updatedInput != null) {
867
+ resolvedArgs = resolveDirectArgs(preResult.updatedInput);
868
+ effectiveCall = {
869
+ ...call,
870
+ args: resolvedArgs
871
+ };
872
+ } else if (reviewedApproval != null) {
873
+ resolvedArgs = reviewedApproval.request.arguments;
874
+ effectiveCall = {
875
+ ...call,
876
+ args: resolvedArgs
877
+ };
878
+ }
816
879
  if (preResult.decision === "deny") return persistOutput(this.blockDirectCall({
817
880
  call,
818
881
  resolvedArgs,
@@ -821,7 +884,15 @@ var ToolNode = class extends require_run.RunnableCallable {
821
884
  runId,
822
885
  threadId
823
886
  }), effectiveCall.args);
824
- if (preResult.decision === "ask") {
887
+ if (approvalEvidenceAppliesToBatch && reviewedApproval == null && !isTrustedSubagentReviewHop && preResult.decision !== "ask") return persistOutput(this.blockDirectCall({
888
+ call,
889
+ resolvedArgs,
890
+ reason: "Tool call was not included in the restored approval batch — failing closed",
891
+ hookRegistry,
892
+ runId,
893
+ threadId
894
+ }), resolvedArgs);
895
+ if (preResult.decision === "ask" || reviewedApproval != null) {
825
896
  if (this.humanInTheLoop?.enabled !== true) {
826
897
  const reason = this.resolveAskDecisionForDirectTool(preResult.reason, call.name);
827
898
  return persistOutput(this.blockDirectCall({
@@ -842,26 +913,41 @@ var ToolNode = class extends require_run.RunnableCallable {
842
913
  runId,
843
914
  threadId
844
915
  }), effectiveCall.args);
916
+ const allowedDecisions = preResult.decision === "ask" ? preResult.allowedDecisions : reviewedApproval?.reviewConfig.allowed_decisions;
917
+ const askReason = preResult.decision === "ask" ? preResult.reason : reviewedApproval?.request.description;
918
+ if (effectiveCall === call) effectiveCall = {
919
+ ...call,
920
+ args: resolvedArgs
921
+ };
845
922
  const payload = buildToolApprovalInterruptPayload([{
846
923
  entry: {
847
924
  call: effectiveCall,
848
925
  args: effectiveCall.args,
849
926
  stepId
850
927
  },
851
- reason: preResult.reason,
852
- allowedDecisions: preResult.allowedDecisions
928
+ reason: askReason,
929
+ allowedDecisions
853
930
  }], runId);
854
931
  const resumeValue = _langchain_core_singletons.AsyncLocalStorageProviderSingleton.runWithConfig(config, () => (0, _langchain_langgraph.interrupt)(payload));
855
- hookRegistry.clearPendingToolApproval(approvalReplaySessionId, approvalReplayKey);
932
+ hookRegistry?.clearPendingToolApproval(approvalReplaySessionId, approvalReplayKey);
933
+ const currentApproval = require_approvalReview.getReviewedToolApproval(payload, toolCallId);
934
+ if (reviewedApproval != null && (currentApproval == null || !require_approvalReview.toolApprovalProposalMatches(currentApproval, reviewedApproval))) return persistOutput(this.blockDirectCall({
935
+ call,
936
+ resolvedArgs,
937
+ reason: "Reviewed tool proposal changed before resume; retry approval",
938
+ hookRegistry,
939
+ runId,
940
+ threadId
941
+ }), effectiveCall.args);
856
942
  const decision = normalizeApprovalDecisions([toolCallId], resumeValue).get(toolCallId) ?? {
857
943
  type: "reject",
858
944
  reason: "No decision provided for tool approval"
859
945
  };
860
946
  const declaredType = decision.type;
861
- if (preResult.allowedDecisions != null && (typeof declaredType !== "string" || !preResult.allowedDecisions.includes(declaredType))) return persistOutput(this.blockDirectCall({
947
+ if (allowedDecisions != null && (typeof declaredType !== "string" || !allowedDecisions.includes(declaredType))) return persistOutput(this.blockDirectCall({
862
948
  call,
863
949
  resolvedArgs,
864
- reason: `Decision "${typeof declaredType === "string" ? declaredType : "<missing>"}" not in allowedDecisions [${preResult.allowedDecisions.join(", ")}] — failing closed`,
950
+ reason: `Decision "${typeof declaredType === "string" ? declaredType : "<missing>"}" not in allowedDecisions [${allowedDecisions.join(", ")}] — failing closed`,
865
951
  hookRegistry,
866
952
  runId,
867
953
  threadId
@@ -869,7 +955,7 @@ var ToolNode = class extends require_run.RunnableCallable {
869
955
  if (decision.type === "reject") return persistOutput(this.blockDirectCall({
870
956
  call,
871
957
  resolvedArgs,
872
- reason: decision.reason ?? preResult.reason ?? "Rejected by user",
958
+ reason: decision.reason ?? askReason ?? "Rejected by user",
873
959
  hookRegistry,
874
960
  runId,
875
961
  threadId
@@ -997,7 +1083,7 @@ var ToolNode = class extends require_run.RunnableCallable {
997
1083
  }
998
1084
  blockDirectCall(args) {
999
1085
  const { call, resolvedArgs, reason, hookRegistry, runId, threadId } = args;
1000
- if (hookRegistry.hasHookFor("PermissionDenied", runId) === true) require_executeHooks.executeHooks({
1086
+ if (hookRegistry?.hasHookFor("PermissionDenied", runId) === true) require_executeHooks.executeHooks({
1001
1087
  registry: hookRegistry,
1002
1088
  input: {
1003
1089
  hook_event_name: "PermissionDenied",
@@ -1126,7 +1212,8 @@ var ToolNode = class extends require_run.RunnableCallable {
1126
1212
  resolvedArgs = pre.resolved;
1127
1213
  if (pre.unresolved.length > 0 && call.id != null) unresolvedByCallId.set(call.id, pre.unresolved);
1128
1214
  } else if (registry != null) {
1129
- const { resolved, unresolved } = registry.resolve(registryRunId, originalArgs, { substituteIntentKey: this.toolDeclaresBusinessIntent(call.name) });
1215
+ const options = { substituteIntentKey: this.toolDeclaresBusinessIntent(call.name) };
1216
+ const { resolved, unresolved } = preBatchSnapshot != null ? preBatchSnapshot.resolve(originalArgs, options) : registry.resolve(registryRunId, originalArgs, options);
1130
1217
  resolvedArgs = resolved;
1131
1218
  if (unresolved.length > 0 && call.id != null) unresolvedByCallId.set(call.id, unresolved);
1132
1219
  }
@@ -1148,8 +1235,9 @@ var ToolNode = class extends require_run.RunnableCallable {
1148
1235
  });
1149
1236
  const hookRegistry = this.hookRegistry;
1150
1237
  const approvalReplaySessionId = getToolApprovalReplaySessionId(config, runId);
1238
+ const approvalReviewEvidence = require_approvalReview.getToolApprovalReviewEvidence(config, require_toolBatchReplay.getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? ""));
1151
1239
  const hasPendingApproval = preToolCalls.some((entry) => entry.call.id != null && hookRegistry?.getPendingToolApproval(approvalReplaySessionId, createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", entry.call.id)) != null);
1152
- if (hookRegistry != null && (hookRegistry.hasHookFor("PreToolUse", runId) || hasPendingApproval)) {
1240
+ if (hookRegistry?.hasHookFor("PreToolUse", runId) === true || hasPendingApproval || approvalReviewEvidence != null) {
1153
1241
  const preemptedEagerRecords = /* @__PURE__ */ new Map();
1154
1242
  if (this.eagerEventToolExecutions != null) for (const entry of preToolCalls) {
1155
1243
  const callId = entry.call.id;
@@ -1160,7 +1248,7 @@ var ToolNode = class extends require_run.RunnableCallable {
1160
1248
  this.eagerEventToolExecutions.delete(callId);
1161
1249
  }
1162
1250
  }
1163
- const preResults = await Promise.all(preToolCalls.map((entry) => {
1251
+ const preResults = hookRegistry == null ? preToolCalls.map(() => HOOK_FALLBACK) : await Promise.all(preToolCalls.map((entry) => {
1164
1252
  const toolUseId = entry.call.id;
1165
1253
  const approvalReplayKey = toolUseId == null ? void 0 : createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", toolUseId);
1166
1254
  return require_executeHooks.executeHooks({
@@ -1213,7 +1301,7 @@ var ToolNode = class extends require_run.RunnableCallable {
1213
1301
  const flushDeferredBlockedSideEffects = async () => {
1214
1302
  for (const item of deferredBlockedSideEffects) {
1215
1303
  await this.dispatchStepCompleted(item.callId, item.toolName, item.args, item.contentString, config);
1216
- if (hookRegistry.hasHookFor("PermissionDenied", runId)) require_executeHooks.executeHooks({
1304
+ if (hookRegistry?.hasHookFor("PermissionDenied", runId) === true) require_executeHooks.executeHooks({
1217
1305
  registry: hookRegistry,
1218
1306
  input: {
1219
1307
  hook_event_name: "PermissionDenied",
@@ -1257,6 +1345,13 @@ var ToolNode = class extends require_run.RunnableCallable {
1257
1345
  continue;
1258
1346
  }
1259
1347
  if (hookResult.updatedInput != null) applyInputOverride(entry, hookResult.updatedInput);
1348
+ else {
1349
+ const reviewed = require_approvalReview.getReviewedToolApproval(approvalReviewEvidence?.payload, entry.call.id);
1350
+ if (reviewed != null) {
1351
+ entry.args = this.coerceEventToolArgs(entry.call.name, reviewed.request.arguments);
1352
+ if (entry.call.id != null) unresolvedByCallId.delete(entry.call.id);
1353
+ }
1354
+ }
1260
1355
  askEntries.push({
1261
1356
  entry,
1262
1357
  reason: hookResult.reason,
@@ -1265,6 +1360,19 @@ var ToolNode = class extends require_run.RunnableCallable {
1265
1360
  continue;
1266
1361
  }
1267
1362
  if (hookResult.updatedInput != null) applyInputOverride(entry, hookResult.updatedInput);
1363
+ const reviewedApproval = require_approvalReview.getReviewedToolApproval(approvalReviewEvidence?.payload, entry.call.id);
1364
+ if (reviewedApproval != null) {
1365
+ if (hookResult.updatedInput == null) {
1366
+ entry.args = this.coerceEventToolArgs(entry.call.name, reviewedApproval.request.arguments);
1367
+ if (entry.call.id != null) unresolvedByCallId.delete(entry.call.id);
1368
+ }
1369
+ askEntries.push({
1370
+ entry,
1371
+ reason: reviewedApproval.request.description,
1372
+ allowedDecisions: reviewedApproval.reviewConfig.allowed_decisions
1373
+ });
1374
+ continue;
1375
+ }
1268
1376
  if (entry.call.id != null) {
1269
1377
  const preempted = preemptedEagerRecords.get(entry.call.id);
1270
1378
  if (preempted != null) this.eagerEventToolExecutions?.set(entry.call.id, preempted);
@@ -1274,9 +1382,12 @@ var ToolNode = class extends require_run.RunnableCallable {
1274
1382
  if (askEntries.length > 0) {
1275
1383
  const payload = buildToolApprovalInterruptPayload(askEntries, runId);
1276
1384
  const resumeValue = _langchain_core_singletons.AsyncLocalStorageProviderSingleton.runWithConfig(config, () => (0, _langchain_langgraph.interrupt)(payload));
1277
- for (const { entry } of askEntries) hookRegistry.clearPendingToolApproval(approvalReplaySessionId, createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", entry.call.id));
1278
- const decisionByCallId = normalizeApprovalDecisions(askEntries.map(({ entry }) => entry.call.id), resumeValue);
1279
- for (const { entry, reason: askReason, allowedDecisions } of askEntries) {
1385
+ for (const { entry } of askEntries) hookRegistry?.clearPendingToolApproval(approvalReplaySessionId, createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", entry.call.id));
1386
+ const proposalMatches = approvalReviewEvidence == null || require_approvalReview.toolApprovalPayloadMatches(payload, approvalReviewEvidence.payload);
1387
+ if (!proposalMatches) for (const { entry } of askEntries) blockEntry(entry, "Reviewed tool proposal changed before resume; retry approval");
1388
+ const decisionEntries = proposalMatches ? askEntries : [];
1389
+ const decisionByCallId = normalizeApprovalDecisions(decisionEntries.map(({ entry }) => entry.call.id), resumeValue);
1390
+ for (const { entry, reason: askReason, allowedDecisions } of decisionEntries) {
1280
1391
  const decision = decisionByCallId.get(entry.call.id) ?? {
1281
1392
  type: "reject",
1282
1393
  reason: "No decision provided for tool approval"
@@ -1402,7 +1513,7 @@ var ToolNode = class extends require_run.RunnableCallable {
1402
1513
  userId: config.configurable?.user_id,
1403
1514
  agentId: this.executingAgentId,
1404
1515
  callerCapabilityProjection: this.getCallerCapabilityProjectionSnapshot(),
1405
- configurable: stripRunBreakerScope(config.configurable),
1516
+ configurable: stripHostExecutionConfig(config.configurable),
1406
1517
  metadata: config.metadata,
1407
1518
  signal: config.signal,
1408
1519
  resolve: (results) => {
@@ -1661,15 +1772,39 @@ var ToolNode = class extends require_run.RunnableCallable {
1661
1772
  return this.dispatchStepCompleted(result.toolCallId, request.name, request.args, output, config, request.turn, require_intentArg.resolveToolOutcome(request.args, require_intentArg.outcomeFieldsFromResult(result), { isError: result.status === "error" }));
1662
1773
  }
1663
1774
  async runDirectBatchInterruptSafe(directCalls, batchIndices, config, baseContext) {
1664
- const settledBatchResults = baseContext.replayBatchKey == null ? void 0 : this.settledInterruptingResults.get(baseContext.replayBatchKey);
1775
+ const settledBatchResults = baseContext.replayBatchKey == null ? void 0 : this.settledDirectResultsByBatch.get(baseContext.replayBatchKey) ?? /* @__PURE__ */ new Map();
1776
+ const cacheResult = (call, position, result) => {
1777
+ if (baseContext.replayBatchKey == null || settledBatchResults == null) return;
1778
+ settledBatchResults.set(directReplayKey(call, baseContext.replayCallPositions?.get(call) ?? batchIndices[position]), result);
1779
+ this.settledDirectResultsByBatch.set(baseContext.replayBatchKey, settledBatchResults);
1780
+ };
1665
1781
  const restoreResult = (call, result) => {
1666
1782
  baseContext.additionalContextsSink?.push(...result.additionalContexts);
1783
+ const turn = result.turn;
1784
+ if (turn != null) this.recordToolUsageTurn(call.name, turn, call.id);
1785
+ const refMeta = (0, _langchain_core_messages.isBaseMessage)(result.output) ? result.output.additional_kwargs : void 0;
1786
+ if (refMeta?._refKey != null && result.referenceContent != null) {
1787
+ this.toolOutputRegistry?.set(baseContext.batchScopeId, refMeta._refKey, result.referenceContent);
1788
+ if ((0, _langchain_core_messages.isBaseMessage)(result.output)) result = {
1789
+ ...result,
1790
+ output: new _langchain_core_messages.ToolMessage({
1791
+ ...result.output,
1792
+ additional_kwargs: {
1793
+ ...result.output.additional_kwargs,
1794
+ _refScope: baseContext.batchScopeId
1795
+ }
1796
+ })
1797
+ };
1798
+ }
1667
1799
  if (call.id != null && result.resolvedArgs != null && baseContext.resolvedArgsByCallId != null) baseContext.resolvedArgsByCallId.set(call.id, result.resolvedArgs);
1668
1800
  return result;
1669
1801
  };
1670
1802
  const runOne = async (call, position) => {
1671
- const cachedResult = typeof call.id === "string" ? settledBatchResults?.get(call.id) : void 0;
1672
- if (cachedResult != null) return restoreResult(call, cachedResult);
1803
+ const cachedResult = settledBatchResults?.get(directReplayKey(call, baseContext.replayCallPositions?.get(call) ?? batchIndices[position]));
1804
+ if (cachedResult != null) {
1805
+ if (cachedResult.proposal.name !== call.name || !require_eagerEventExecution.recordArgsEqual(cachedResult.proposal.args, call.args)) throw new Error("Cannot change an already completed tool call during approval replay");
1806
+ return restoreResult(call, cachedResult);
1807
+ }
1673
1808
  const additionalContexts = [];
1674
1809
  const output = await this.runDirectToolWithLifecycleHooks(call, config, {
1675
1810
  ...baseContext,
@@ -1677,14 +1812,32 @@ var ToolNode = class extends require_run.RunnableCallable {
1677
1812
  additionalContextsSink: additionalContexts
1678
1813
  });
1679
1814
  const resolvedArgs = call.id == null ? void 0 : baseContext.resolvedArgsByCallId?.get(call.id);
1680
- return restoreResult(call, {
1815
+ const result = {
1816
+ proposal: structuredClone({
1817
+ name: call.name,
1818
+ args: call.args
1819
+ }),
1820
+ turn: call.id == null ? void 0 : this.toolCallTurns.get(call.id) ?? this.directPathTurns.get(call.id),
1681
1821
  output,
1682
1822
  additionalContexts,
1683
1823
  ...resolvedArgs == null ? {} : { resolvedArgs }
1684
- });
1824
+ };
1825
+ const refMeta = (0, _langchain_core_messages.isBaseMessage)(output) ? output.additional_kwargs : void 0;
1826
+ if (refMeta?._refKey != null) result.referenceContent = this.toolOutputRegistry?.get(refMeta._refScope, refMeta._refKey);
1827
+ cacheResult(call, position, result);
1828
+ return restoreResult(call, result);
1685
1829
  };
1686
1830
  const interrupting = this.interruptingToolNames;
1687
- if (!(interrupting != null && directCalls.some((call) => interrupting.has(call.name)))) return (await Promise.all(directCalls.map((call, i) => runOne(call, i)))).map((result) => result.output);
1831
+ if (!(interrupting != null && directCalls.some((call) => interrupting.has(call.name)))) {
1832
+ const results = await Promise.allSettled(directCalls.map((call, i) => runOne(call, i)));
1833
+ this.throwIfBreakerTripped(config);
1834
+ const failed = results.find((result) => result.status === "rejected" && !(0, _langchain_langgraph.isGraphInterrupt)(result.reason)) ?? results.find((result) => result.status === "rejected");
1835
+ if (failed?.status === "rejected") throw failed.reason;
1836
+ return results.map((result) => {
1837
+ if (result.status === "rejected") throw result.reason;
1838
+ return result.value.output;
1839
+ });
1840
+ }
1688
1841
  const outputs = new Array(directCalls.length);
1689
1842
  const interruptingPositions = [];
1690
1843
  const regularPositions = [];
@@ -1705,12 +1858,6 @@ var ToolNode = class extends require_run.RunnableCallable {
1705
1858
  }
1706
1859
  const position = interruptingPositions[i];
1707
1860
  outputs[position] = result.value.output;
1708
- const callId = directCalls[position].id;
1709
- if (baseContext.replayBatchKey != null && typeof callId === "string" && callId !== "") {
1710
- const batchResults = this.settledInterruptingResults.get(baseContext.replayBatchKey) ?? /* @__PURE__ */ new Map();
1711
- batchResults.set(callId, result.value);
1712
- this.settledInterruptingResults.set(baseContext.replayBatchKey, batchResults);
1713
- }
1714
1861
  }
1715
1862
  if (hasInterruptingError) {
1716
1863
  if ((0, _langchain_langgraph.isGraphInterrupt)(interruptingError)) {
@@ -1752,7 +1899,7 @@ var ToolNode = class extends require_run.RunnableCallable {
1752
1899
  const outputs = [...toolMessages, ...injected];
1753
1900
  return Array.isArray(input) ? outputs : { messages: outputs };
1754
1901
  }
1755
- async run(input, config) {
1902
+ async run(input, config, referenceReplay = {}) {
1756
1903
  const breakerSignal = this.getBreakerSignal?.();
1757
1904
  const composedSignal = require_misc.composeAbortSignals(config.signal, breakerSignal);
1758
1905
  const batchRunScope = this.getRunScope?.();
@@ -1768,10 +1915,11 @@ var ToolNode = class extends require_run.RunnableCallable {
1768
1915
  this.toolCallTurns.clear();
1769
1916
  const resolvedArgsByCallId = /* @__PURE__ */ new Map();
1770
1917
  const errorOwnership = createToolErrorOwnership();
1771
- const incomingRunId = config.configurable?.run_id;
1772
- const incomingThreadId = config.configurable?.thread_id;
1773
- const batchScopeId = incomingRunId ?? `\0anon-${this.anonBatchCounter++}`;
1774
- const turn = this.toolOutputRegistry?.nextTurn(batchScopeId) ?? 0;
1918
+ const batchScopeId = config.configurable?.run_id ?? `\0anon-${this.anonBatchCounter++}`;
1919
+ const resumedReferenceState = referenceReplay.state;
1920
+ const replayInputSnapshot = resumedReferenceState == null ? void 0 : this.toolOutputRegistry?.resumeBatch(batchScopeId, resumedReferenceState);
1921
+ referenceReplay.state = resumedReferenceState ?? this.toolOutputRegistry?.snapshotState(batchScopeId);
1922
+ const turn = resumedReferenceState?.turnCounter ?? this.toolOutputRegistry?.nextTurn(batchScopeId) ?? 0;
1775
1923
  let outputs;
1776
1924
  let replayBatchKey;
1777
1925
  let promotedAiMessage;
@@ -1781,17 +1929,19 @@ var ToolNode = class extends require_run.RunnableCallable {
1781
1929
  if (this.eventDrivenMode && !isLocalTool) return this.executeViaEvent([input.lg_tool_call], config, input, {
1782
1930
  batchIndices: [0],
1783
1931
  turn,
1784
- batchScopeId
1932
+ batchScopeId,
1933
+ preBatchSnapshot: replayInputSnapshot
1785
1934
  });
1786
1935
  const directAdditionalContexts = [];
1787
1936
  const { lg_tool_call: _sendToolCall, ...sendState } = input;
1788
1937
  const sendMessages = sendState.messages;
1789
1938
  const sendAssistantBatch = Array.isArray(sendMessages) ? findAssistantBatch(sendMessages) : void 0;
1790
- const sendReplayBatchKey = sendAssistantBatch == null ? void 0 : getAssistantBatchReplayKey(sendAssistantBatch, incomingRunId, incomingThreadId);
1939
+ const sendReplayBatchKey = sendAssistantBatch == null || require_toolBatchReplay.getToolBatchReplayScope(config) == null ? void 0 : getAssistantBatchReplayKey(sendAssistantBatch, require_toolBatchReplay.getToolBatchReplayScope(config));
1791
1940
  const sendOutput = await this.runDirectToolWithLifecycleHooks(input.lg_tool_call, config, {
1792
1941
  batchIndex: 0,
1793
1942
  turn,
1794
1943
  batchScopeId,
1944
+ preBatchSnapshot: replayInputSnapshot,
1795
1945
  resolvedArgsByCallId,
1796
1946
  errorOwnership,
1797
1947
  additionalContextsSink: directAdditionalContexts,
@@ -1815,14 +1965,15 @@ var ToolNode = class extends require_run.RunnableCallable {
1815
1965
  const assistantBatch = findAssistantBatch(messages);
1816
1966
  if (assistantBatch == null) throw new Error("ToolNode only accepts AIMessages as input.");
1817
1967
  const aiMessage = assistantBatch.message;
1818
- replayBatchKey = getAssistantBatchReplayKey(assistantBatch, incomingRunId, incomingThreadId);
1968
+ replayBatchKey = require_toolBatchReplay.getToolBatchReplayScope(config) == null ? void 0 : getAssistantBatchReplayKey(assistantBatch, require_toolBatchReplay.getToolBatchReplayScope(config));
1819
1969
  if (this.loadRuntimeTools) {
1820
1970
  const { tools, toolMap } = this.loadRuntimeTools(aiMessage.tool_calls ?? []);
1821
1971
  this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
1822
1972
  this.applyToolExecutionOverrides();
1823
1973
  }
1974
+ const replayCallPositions = new Map((aiMessage.tool_calls ?? []).map((call, i) => [call, i]));
1824
1975
  const filteredCalls = aiMessage.tool_calls?.filter((call) => {
1825
- return (call.id == null || !toolMessageIds.has(call.id)) && !(call.id?.startsWith("srvtoolu_") ?? false);
1976
+ return (call.id == null || call.id === "" || !toolMessageIds.has(call.id)) && !(call.id?.startsWith("srvtoolu_") ?? false);
1826
1977
  }) ?? [];
1827
1978
  const attributableInvalidCalls = !(!Array.isArray(input) && typeof aiMessage.id === "string" && aiMessage.id.length > 0) ? [] : (aiMessage.invalid_tool_calls ?? []).filter((call) => call.id != null && call.id !== "" && !toolMessageIds.has(call.id) && !call.id.startsWith("srvtoolu_"));
1828
1979
  invalidCallResults = attributableInvalidCalls.map((call) => new _langchain_core_messages.ToolMessage({
@@ -1864,14 +2015,16 @@ var ToolNode = class extends require_run.RunnableCallable {
1864
2015
  if (directEntries.length === 0 && invalidCallResults.length === 0) return this.executeViaEvent(filteredCalls, config, input, {
1865
2016
  batchIndices: eventEntries.map((entry) => entry.batchIndex),
1866
2017
  turn,
1867
- batchScopeId
2018
+ batchScopeId,
2019
+ preBatchSnapshot: replayInputSnapshot
1868
2020
  });
1869
2021
  const directCalls = directEntries.map((e) => e.call);
1870
2022
  const directIndices = directEntries.map((e) => e.batchIndex);
1871
2023
  const eventCalls = eventEntries.map((e) => e.call);
1872
2024
  const eventIndices = eventEntries.map((e) => e.batchIndex);
2025
+ const approvalScopeCallIds = new Set(filteredCalls.flatMap((call) => call.id == null ? [] : [call.id]));
1873
2026
  const preResolvedEventArgs = /* @__PURE__ */ new Map();
1874
- const preBatchSnapshot = this.toolOutputRegistry?.snapshot(batchScopeId);
2027
+ const preBatchSnapshot = replayInputSnapshot ?? this.toolOutputRegistry?.snapshot(batchScopeId);
1875
2028
  if (preBatchSnapshot != null) {
1876
2029
  for (const entry of eventEntries) if (entry.call.id != null) {
1877
2030
  const { resolved, unresolved } = preBatchSnapshot.resolve(entry.call.args, { substituteIntentKey: this.toolDeclaresBusinessIntent(entry.call.name) });
@@ -1890,9 +2043,25 @@ var ToolNode = class extends require_run.RunnableCallable {
1890
2043
  preBatchSnapshot,
1891
2044
  additionalContextsSink: directAdditionalContexts,
1892
2045
  replayBatchKey,
2046
+ replayCallPositions,
2047
+ approvalScopeCallIds,
1893
2048
  runInput: input
1894
2049
  }) : [];
1895
- if (directCalls.length > 0 && directOutputs.length > 0) await this.handleRunToolCompletions(directCalls, directOutputs, config, resolvedArgsByCallId, errorOwnership);
2050
+ const settledDirectResults = replayBatchKey == null ? void 0 : this.settledDirectResultsByBatch.get(replayBatchKey);
2051
+ const unhandledDirectCalls = [];
2052
+ const unhandledDirectOutputs = [];
2053
+ for (let i = 0; i < directCalls.length; i++) {
2054
+ if ((settledDirectResults?.get(directReplayKey(directCalls[i], replayCallPositions.get(directCalls[i]))))?.completionHandled === true) continue;
2055
+ unhandledDirectCalls.push(directCalls[i]);
2056
+ unhandledDirectOutputs.push(directOutputs[i]);
2057
+ }
2058
+ if (unhandledDirectCalls.length > 0) {
2059
+ await this.handleRunToolCompletions(unhandledDirectCalls, unhandledDirectOutputs, config, resolvedArgsByCallId, errorOwnership);
2060
+ for (let i = 0; i < directCalls.length; i++) {
2061
+ const settled = settledDirectResults?.get(directReplayKey(directCalls[i], replayCallPositions.get(directCalls[i])));
2062
+ if (settled != null) settled.completionHandled = true;
2063
+ }
2064
+ }
1896
2065
  const eventResult = eventCalls.length > 0 ? await this.dispatchToolEvents(eventCalls, config, {
1897
2066
  batchIndices: eventIndices,
1898
2067
  turn,
@@ -1919,8 +2088,9 @@ var ToolNode = class extends require_run.RunnableCallable {
1919
2088
  ...eventResult.injected
1920
2089
  ];
1921
2090
  } else {
1922
- const preBatchSnapshot = this.toolOutputRegistry?.snapshot(batchScopeId);
2091
+ const preBatchSnapshot = replayInputSnapshot ?? this.toolOutputRegistry?.snapshot(batchScopeId);
1923
2092
  const directAdditionalContexts = [];
2093
+ const approvalScopeCallIds = new Set(filteredCalls.flatMap((call) => call.id == null ? [] : [call.id]));
1924
2094
  const toolOutputs = await this.runDirectBatchInterruptSafe(filteredCalls, filteredCalls.map((_call, i) => i), config, {
1925
2095
  turn,
1926
2096
  batchScopeId,
@@ -1929,6 +2099,8 @@ var ToolNode = class extends require_run.RunnableCallable {
1929
2099
  preBatchSnapshot,
1930
2100
  additionalContextsSink: directAdditionalContexts,
1931
2101
  replayBatchKey,
2102
+ replayCallPositions,
2103
+ approvalScopeCallIds,
1932
2104
  runInput: input
1933
2105
  });
1934
2106
  await this.handleRunToolCompletions(filteredCalls, toolOutputs, config, resolvedArgsByCallId, errorOwnership);
@@ -1957,7 +2129,7 @@ var ToolNode = class extends require_run.RunnableCallable {
1957
2129
  }
1958
2130
  }
1959
2131
  if (!outputs.some(_langchain_langgraph.isCommand)) {
1960
- if (replayBatchKey != null) this.settledInterruptingResults.delete(replayBatchKey);
2132
+ if (replayBatchKey != null) this.settledDirectResultsByBatch.delete(replayBatchKey);
1961
2133
  return Array.isArray(input) ? outputs : { messages: outputs };
1962
2134
  }
1963
2135
  if (promotedAiMessage != null) outputs = outputs.map((output) => (0, _langchain_langgraph.isCommand)(output) ? patchCommandUpdateForPromotedInvalidCalls(output, promotedAiMessage, invalidCallResults) : output);
@@ -2007,7 +2179,7 @@ var ToolNode = class extends require_run.RunnableCallable {
2007
2179
  combinedOutputs.push(parallelCommand);
2008
2180
  } else if (handoffCommands.length === 1) combinedOutputs.push(handoffCommands[0]);
2009
2181
  if (parentCommand) combinedOutputs.push(parentCommand);
2010
- if (replayBatchKey != null) this.settledInterruptingResults.delete(replayBatchKey);
2182
+ if (replayBatchKey != null) this.settledDirectResultsByBatch.delete(replayBatchKey);
2011
2183
  return combinedOutputs;
2012
2184
  }
2013
2185
  isSendInput(input) {