@librechat/agents 3.8.1 → 3.8.3

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 (96) hide show
  1. package/dist/cjs/graphs/Graph.cjs +2 -2
  2. package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
  3. package/dist/cjs/hitl/approvalReview.cjs +89 -0
  4. package/dist/cjs/hitl/approvalReview.cjs.map +1 -0
  5. package/dist/cjs/hooks/index.cjs +2 -0
  6. package/dist/cjs/hooks/index.cjs.map +1 -1
  7. package/dist/cjs/hooks/types.cjs.map +1 -1
  8. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +1 -1
  9. package/dist/cjs/main.cjs +5 -3
  10. package/dist/cjs/messages/core.cjs +1 -1
  11. package/dist/cjs/messages/format.cjs +1 -1
  12. package/dist/cjs/messages/prune.cjs +2 -2
  13. package/dist/cjs/run.cjs +17 -6
  14. package/dist/cjs/run.cjs.map +1 -1
  15. package/dist/cjs/session/AgentSession.cjs +1 -1
  16. package/dist/cjs/stream.cjs +2 -2
  17. package/dist/cjs/summarization/node.cjs +1 -1
  18. package/dist/cjs/tools/BashExecutor.cjs +2 -3
  19. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  20. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +3 -2
  21. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  22. package/dist/cjs/tools/CodeExecutor.cjs +63 -11
  23. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  24. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +8 -7
  25. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  26. package/dist/cjs/tools/ToolNode.cjs +271 -84
  27. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  28. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +24 -9
  29. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  30. package/dist/cjs/tools/subagent/SubagentReplay.cjs +2 -0
  31. package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -1
  32. package/dist/cjs/tools/toolBatchReplay.cjs +198 -0
  33. package/dist/cjs/tools/toolBatchReplay.cjs.map +1 -0
  34. package/dist/cjs/tools/toolOutputReferences.cjs +12 -0
  35. package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
  36. package/dist/cjs/types/hitl.cjs +4 -0
  37. package/dist/cjs/types/hitl.cjs.map +1 -1
  38. package/dist/esm/graphs/Graph.mjs +2 -2
  39. package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
  40. package/dist/esm/hitl/approvalReview.mjs +83 -0
  41. package/dist/esm/hitl/approvalReview.mjs.map +1 -0
  42. package/dist/esm/hooks/index.mjs +2 -1
  43. package/dist/esm/hooks/index.mjs.map +1 -1
  44. package/dist/esm/hooks/types.mjs.map +1 -1
  45. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +1 -1
  46. package/dist/esm/main.mjs +6 -6
  47. package/dist/esm/messages/core.mjs +1 -1
  48. package/dist/esm/messages/format.mjs +1 -1
  49. package/dist/esm/messages/prune.mjs +2 -2
  50. package/dist/esm/run.mjs +18 -7
  51. package/dist/esm/run.mjs.map +1 -1
  52. package/dist/esm/session/AgentSession.mjs +1 -1
  53. package/dist/esm/stream.mjs +2 -2
  54. package/dist/esm/summarization/node.mjs +1 -1
  55. package/dist/esm/tools/BashExecutor.mjs +2 -3
  56. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  57. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +3 -2
  58. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  59. package/dist/esm/tools/CodeExecutor.mjs +63 -12
  60. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  61. package/dist/esm/tools/ProgrammaticToolCalling.mjs +8 -7
  62. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  63. package/dist/esm/tools/ToolNode.mjs +271 -84
  64. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  65. package/dist/esm/tools/subagent/SubagentExecutor.mjs +24 -9
  66. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  67. package/dist/esm/tools/subagent/SubagentReplay.mjs +1 -1
  68. package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -1
  69. package/dist/esm/tools/toolBatchReplay.mjs +186 -0
  70. package/dist/esm/tools/toolBatchReplay.mjs.map +1 -0
  71. package/dist/esm/tools/toolOutputReferences.mjs +12 -0
  72. package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
  73. package/dist/esm/types/hitl.mjs +4 -1
  74. package/dist/esm/types/hitl.mjs.map +1 -1
  75. package/dist/types/hitl/approvalReview.d.ts +32 -0
  76. package/dist/types/hooks/index.d.ts +2 -0
  77. package/dist/types/hooks/types.d.ts +6 -0
  78. package/dist/types/tools/CodeExecutor.d.ts +2 -0
  79. package/dist/types/tools/ToolNode.d.ts +12 -6
  80. package/dist/types/tools/subagent/SubagentReplay.d.ts +2 -0
  81. package/dist/types/tools/toolBatchReplay.d.ts +63 -0
  82. package/dist/types/tools/toolOutputReferences.d.ts +2 -0
  83. package/package.json +1 -1
  84. package/src/hitl/approvalReview.ts +209 -0
  85. package/src/hooks/index.ts +3 -0
  86. package/src/hooks/types.ts +6 -0
  87. package/src/run.ts +34 -20
  88. package/src/tools/BashExecutor.ts +2 -2
  89. package/src/tools/BashProgrammaticToolCalling.ts +5 -3
  90. package/src/tools/CodeExecutor.ts +111 -17
  91. package/src/tools/ProgrammaticToolCalling.ts +10 -8
  92. package/src/tools/ToolNode.ts +536 -184
  93. package/src/tools/subagent/SubagentExecutor.ts +35 -6
  94. package/src/tools/subagent/SubagentReplay.ts +2 -2
  95. package/src/tools/toolBatchReplay.ts +423 -0
  96. package/src/tools/toolOutputReferences.ts +16 -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,59 @@ 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
+ let reviewEvidence = require_approvalReview.getToolApprovalReviewEvidence(config);
293
+ const replayState = require_toolBatchReplay.getToolBatchReplayState(config.configurable);
294
+ const ownsParentBatch = replayState?.records.some((record) => record.owner === replayOwner && record.batch === activeReplayKey) === true;
295
+ let isChildReplay = false;
296
+ if (ownsParentBatch) {
297
+ const calls = this.isSendInput(input) ? [input.lg_tool_call] : assistantBatch?.message.tool_calls ?? [];
298
+ 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 ?? ""));
299
+ isChildReplay = reviewEvidence?.owner != null ? require_toolBatchReplay.isChildToolReplayOwner(config, reviewEvidence.owner, trustedParentCallIds) : replayState.records.some((record) => require_toolBatchReplay.isChildToolReplayOwner(config, record.owner, trustedParentCallIds));
300
+ }
301
+ if ((reviewEvidence?.owner != null || replayState != null) && !require_toolBatchReplay.isToolReplayResume(config, reviewEvidence?.interruptId ?? replayState?.interruptId, isChildReplay)) {
302
+ config = {
303
+ ...config,
304
+ configurable: {
305
+ ...config.configurable,
306
+ [require_approvalReview.TOOL_APPROVAL_REVIEW_CONFIG_KEY]: void 0,
307
+ [require_toolBatchReplay.TOOL_BATCH_REPLAY_KEY]: void 0
308
+ }
309
+ };
310
+ reviewEvidence = void 0;
311
+ }
312
+ if (reviewEvidence?.owner != null && reviewEvidence.owner !== replayOwner && !isChildReplay) throw new Error("Tool approval execution owner changed before resume — failing closed");
313
+ const referenceReplay = {};
314
+ const restoredBatches = await require_toolBatchReplay.restoreToolBatchReplayState(config, replayOwner);
315
+ if (restoredBatches.length > 0 && !restoredBatches.some(({ batch }) => batch === activeReplayKey)) throw new Error("Tool batch proposal changed before approval replay — failing closed");
316
+ if (activeReplayKey != null && require_toolBatchReplay.getToolBatchReplayState(config.configurable) != null) this.settledDirectResultsByBatch.delete(activeReplayKey);
317
+ for (const { batch, results, turnState, referenceState } of restoredBatches) {
318
+ if (batch !== activeReplayKey) continue;
319
+ this.settledDirectResultsByBatch.set(batch, new Map(results));
320
+ if (turnState != null) this.restoreSubagentResumeState(turnState);
321
+ referenceReplay.state = referenceState;
322
+ }
282
323
  restoreRunStepResumeState?.(input.runStepState, config);
283
324
  let result;
284
325
  try {
285
- result = await this.run(input, config);
326
+ result = await this.run(input, config, referenceReplay);
286
327
  } 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)
328
+ if (!(0, _langchain_langgraph.isGraphInterrupt)(error)) throw error;
329
+ const resumeState = createRunStepResumeState?.();
330
+ const activeResults = activeReplayKey == null ? void 0 : this.settledDirectResultsByBatch.get(activeReplayKey);
331
+ const activeBatches = activeReplayKey == null ? /* @__PURE__ */ new Map() : new Map([[activeReplayKey, activeResults ?? /* @__PURE__ */ new Map()]]);
332
+ const turnState = this.createSubagentResumeState();
333
+ const activeCallIds = new Set(assistantBatch?.message.tool_calls?.map((call) => call.id));
334
+ turnState.directPathTurns = turnState.directPathTurns.filter(({ toolCallId }) => activeCallIds.has(toolCallId));
335
+ throw new _langchain_langgraph.GraphInterrupt(await Promise.all(error.interrupts.map(async (pendingInterrupt) => {
336
+ const value = await require_toolBatchReplay.attachToolBatchReplayState(pendingInterrupt.value, replayOwner, activeBatches, turnState, referenceReplay.state);
337
+ return {
338
+ ...pendingInterrupt,
339
+ value: resumeState == null ? value : require_runStepResume.attachRunStepResumeState(value, resumeState)
340
+ };
292
341
  })));
293
342
  }
294
343
  if (createRunStepResumeState == null) return result;
@@ -416,7 +465,7 @@ var ToolNode = class extends require_run.RunnableCallable {
416
465
  }
417
466
  clearDirectPathTurns() {
418
467
  this.directPathTurns.clear();
419
- this.settledInterruptingResults.clear();
468
+ this.settledDirectResultsByBatch.clear();
420
469
  }
421
470
  getCallerCapabilityProjection() {
422
471
  const discoveredToolNames = new Set(this.getDiscoveredToolNames?.() ?? []);
@@ -486,11 +535,16 @@ var ToolNode = class extends require_run.RunnableCallable {
486
535
  }
487
536
  invokeWithRuntime(tool, invokeParams, call, config, runInput) {
488
537
  const lgConfig = config;
489
- const runtime = {
538
+ const isTrustedSubagentHop = call.name === "subagent" && tool[require_SubagentReplay.SUBAGENT_REPLAY_CONTROLLER] != null;
539
+ const toolConfig = {
490
540
  ...config,
541
+ configurable: isTrustedSubagentHop ? config.configurable : stripToolApprovalReviewConfig(config.configurable)
542
+ };
543
+ const runtime = {
544
+ ...toolConfig,
491
545
  state: runInput,
492
546
  toolCallId: call.id ?? "",
493
- config,
547
+ config: toolConfig,
494
548
  context: lgConfig.context,
495
549
  store: lgConfig.store ?? null,
496
550
  writer: lgConfig.writer ?? config.configurable?.writer ?? null
@@ -576,6 +630,8 @@ var ToolNode = class extends require_run.RunnableCallable {
576
630
  if (call.id != null && call.id !== "") this.toolCallTurns.set(call.id, next);
577
631
  return next;
578
632
  })();
633
+ this.recordToolUsageTurn(call.name, usageCount, call.id);
634
+ if (batchContext.replayBatchKey != null && call.id != null && call.id !== "") this.directPathTurns.set(call.id, usageCount);
579
635
  let args = call.args;
580
636
  if (resolveFn != null) {
581
637
  const { resolved, unresolved } = resolveFn(runId, args);
@@ -724,6 +780,7 @@ var ToolNode = class extends require_run.RunnableCallable {
724
780
  throw new require_preparedSubagents.PreparedSubagentError("Tool hooks changed after a subagent started.");
725
781
  }
726
782
  const replayController = this.toolMap.get(call.name)?.[require_SubagentReplay.SUBAGENT_REPLAY_CONTROLLER];
783
+ const isTrustedSubagentReviewHop = call.name === "subagent" && replayController != null;
727
784
  const replayConfig = replayController == null ? config : withSubagentReplayBatch(config, batchContext.replayBatchKey);
728
785
  const settledOutput = await replayController?.getSettledOutput(call, replayConfig);
729
786
  if (settledOutput != null) {
@@ -754,9 +811,12 @@ var ToolNode = class extends require_run.RunnableCallable {
754
811
  const approvalReplayKey = call.id == null || call.id === "" ? void 0 : createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", call.id);
755
812
  const approvalReplaySessionId = getToolApprovalReplaySessionId(config, runId);
756
813
  const pendingApproval = approvalReplayKey == null ? void 0 : hookRegistry?.getPendingToolApproval(approvalReplaySessionId, approvalReplayKey);
814
+ const approvalReviewEvidence = require_approvalReview.getToolApprovalReviewEvidence(config, require_toolBatchReplay.getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? ""));
815
+ const reviewedApproval = require_approvalReview.getReviewedToolApproval(approvalReviewEvidence?.payload, call.id);
816
+ 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
817
  const hasPostHook = hookRegistry?.hasHookFor("PostToolUse", runId) === true;
758
818
  const hasFailureHook = hookRegistry?.hasHookFor("PostToolUseFailure", runId) === true;
759
- if (hookRegistry == null || !hasPreHook && pendingApproval == null && !hasPostHook && !hasFailureHook) {
819
+ if (!hasPreHook && pendingApproval == null && reviewedApproval == null && approvalReviewEvidence == null && !hasPostHook && !hasFailureHook) {
760
820
  const output = await this.runTool(call, replayConfig, batchContext);
761
821
  return output instanceof _langchain_core_messages.ToolMessage ? persistOutput(output) : output;
762
822
  }
@@ -774,18 +834,21 @@ var ToolNode = class extends require_run.RunnableCallable {
774
834
  }
775
835
  const turn = usageCount;
776
836
  const stepId = this.toolCallStepIds?.get(call.id ?? "") ?? "";
777
- let resolvedArgs = call.args;
778
837
  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
- }
838
+ const resolveDirectArgs = (args) => {
839
+ if (batchContext.preBatchSnapshot != null) return batchContext.preBatchSnapshot.resolve(args, hookResolveOptions).resolved;
840
+ if (this.toolOutputRegistry != null) return this.toolOutputRegistry.resolve(registryRunId, args, hookResolveOptions).resolved;
841
+ return args;
842
+ };
843
+ const originalResolvedArgs = resolveDirectArgs(call.args);
844
+ let resolvedArgs = hasPreHook || pendingApproval != null ? originalResolvedArgs : reviewedApproval?.request.arguments ?? originalResolvedArgs;
786
845
  let effectiveCall = call;
787
- if (hasPreHook || pendingApproval != null) {
788
- const preResult = await require_executeHooks.executeHooks({
846
+ if (hasPreHook || pendingApproval != null || reviewedApproval != null || approvalReviewEvidence != null) {
847
+ const preResult = hookRegistry == null ? {
848
+ additionalContexts: [],
849
+ injectedMessages: [],
850
+ errors: []
851
+ } : await require_executeHooks.executeHooks({
789
852
  registry: hookRegistry,
790
853
  input: {
791
854
  hook_event_name: "PreToolUse",
@@ -803,16 +866,31 @@ var ToolNode = class extends require_run.RunnableCallable {
803
866
  matchQuery: call.name,
804
867
  onceReplayKey: approvalReplayKey,
805
868
  onceReplaySessionId: approvalReplaySessionId
806
- }).catch(() => void 0);
869
+ }).catch(() => approvalReviewEvidence == null ? void 0 : {
870
+ decision: "deny",
871
+ reason: "Approval policy could not be evaluated on resume",
872
+ additionalContexts: [],
873
+ injectedMessages: [],
874
+ errors: []
875
+ });
807
876
  if (preResult != null) {
808
877
  if (preResult.additionalContexts.length > 0) {
809
878
  replayAdditionalContexts.push(...preResult.additionalContexts);
810
879
  batchContext.additionalContextsSink?.push(...preResult.additionalContexts);
811
880
  }
812
- if (preResult.updatedInput != null) effectiveCall = {
813
- ...call,
814
- args: preResult.updatedInput
815
- };
881
+ if (preResult.updatedInput != null) {
882
+ resolvedArgs = resolveDirectArgs(preResult.updatedInput);
883
+ effectiveCall = {
884
+ ...call,
885
+ args: resolvedArgs
886
+ };
887
+ } else if (reviewedApproval != null) {
888
+ resolvedArgs = reviewedApproval.request.arguments;
889
+ effectiveCall = {
890
+ ...call,
891
+ args: resolvedArgs
892
+ };
893
+ }
816
894
  if (preResult.decision === "deny") return persistOutput(this.blockDirectCall({
817
895
  call,
818
896
  resolvedArgs,
@@ -821,7 +899,15 @@ var ToolNode = class extends require_run.RunnableCallable {
821
899
  runId,
822
900
  threadId
823
901
  }), effectiveCall.args);
824
- if (preResult.decision === "ask") {
902
+ if (approvalEvidenceAppliesToBatch && reviewedApproval == null && !isTrustedSubagentReviewHop && preResult.decision !== "ask") return persistOutput(this.blockDirectCall({
903
+ call,
904
+ resolvedArgs,
905
+ reason: "Tool call was not included in the restored approval batch — failing closed",
906
+ hookRegistry,
907
+ runId,
908
+ threadId
909
+ }), resolvedArgs);
910
+ if (preResult.decision === "ask" || reviewedApproval != null) {
825
911
  if (this.humanInTheLoop?.enabled !== true) {
826
912
  const reason = this.resolveAskDecisionForDirectTool(preResult.reason, call.name);
827
913
  return persistOutput(this.blockDirectCall({
@@ -842,26 +928,41 @@ var ToolNode = class extends require_run.RunnableCallable {
842
928
  runId,
843
929
  threadId
844
930
  }), effectiveCall.args);
931
+ const allowedDecisions = preResult.decision === "ask" ? preResult.allowedDecisions : reviewedApproval?.reviewConfig.allowed_decisions;
932
+ const askReason = preResult.decision === "ask" ? preResult.reason : reviewedApproval?.request.description;
933
+ if (effectiveCall === call) effectiveCall = {
934
+ ...call,
935
+ args: resolvedArgs
936
+ };
845
937
  const payload = buildToolApprovalInterruptPayload([{
846
938
  entry: {
847
939
  call: effectiveCall,
848
940
  args: effectiveCall.args,
849
941
  stepId
850
942
  },
851
- reason: preResult.reason,
852
- allowedDecisions: preResult.allowedDecisions
943
+ reason: askReason,
944
+ allowedDecisions
853
945
  }], runId);
854
946
  const resumeValue = _langchain_core_singletons.AsyncLocalStorageProviderSingleton.runWithConfig(config, () => (0, _langchain_langgraph.interrupt)(payload));
855
- hookRegistry.clearPendingToolApproval(approvalReplaySessionId, approvalReplayKey);
947
+ hookRegistry?.clearPendingToolApproval(approvalReplaySessionId, approvalReplayKey);
948
+ const currentApproval = require_approvalReview.getReviewedToolApproval(payload, toolCallId);
949
+ if (reviewedApproval != null && (currentApproval == null || !require_approvalReview.toolApprovalProposalMatches(currentApproval, reviewedApproval))) return persistOutput(this.blockDirectCall({
950
+ call,
951
+ resolvedArgs,
952
+ reason: "Reviewed tool proposal changed before resume; retry approval",
953
+ hookRegistry,
954
+ runId,
955
+ threadId
956
+ }), effectiveCall.args);
856
957
  const decision = normalizeApprovalDecisions([toolCallId], resumeValue).get(toolCallId) ?? {
857
958
  type: "reject",
858
959
  reason: "No decision provided for tool approval"
859
960
  };
860
961
  const declaredType = decision.type;
861
- if (preResult.allowedDecisions != null && (typeof declaredType !== "string" || !preResult.allowedDecisions.includes(declaredType))) return persistOutput(this.blockDirectCall({
962
+ if (allowedDecisions != null && (typeof declaredType !== "string" || !allowedDecisions.includes(declaredType))) return persistOutput(this.blockDirectCall({
862
963
  call,
863
964
  resolvedArgs,
864
- reason: `Decision "${typeof declaredType === "string" ? declaredType : "<missing>"}" not in allowedDecisions [${preResult.allowedDecisions.join(", ")}] — failing closed`,
965
+ reason: `Decision "${typeof declaredType === "string" ? declaredType : "<missing>"}" not in allowedDecisions [${allowedDecisions.join(", ")}] — failing closed`,
865
966
  hookRegistry,
866
967
  runId,
867
968
  threadId
@@ -869,7 +970,7 @@ var ToolNode = class extends require_run.RunnableCallable {
869
970
  if (decision.type === "reject") return persistOutput(this.blockDirectCall({
870
971
  call,
871
972
  resolvedArgs,
872
- reason: decision.reason ?? preResult.reason ?? "Rejected by user",
973
+ reason: decision.reason ?? askReason ?? "Rejected by user",
873
974
  hookRegistry,
874
975
  runId,
875
976
  threadId
@@ -997,7 +1098,7 @@ var ToolNode = class extends require_run.RunnableCallable {
997
1098
  }
998
1099
  blockDirectCall(args) {
999
1100
  const { call, resolvedArgs, reason, hookRegistry, runId, threadId } = args;
1000
- if (hookRegistry.hasHookFor("PermissionDenied", runId) === true) require_executeHooks.executeHooks({
1101
+ if (hookRegistry?.hasHookFor("PermissionDenied", runId) === true) require_executeHooks.executeHooks({
1001
1102
  registry: hookRegistry,
1002
1103
  input: {
1003
1104
  hook_event_name: "PermissionDenied",
@@ -1126,7 +1227,8 @@ var ToolNode = class extends require_run.RunnableCallable {
1126
1227
  resolvedArgs = pre.resolved;
1127
1228
  if (pre.unresolved.length > 0 && call.id != null) unresolvedByCallId.set(call.id, pre.unresolved);
1128
1229
  } else if (registry != null) {
1129
- const { resolved, unresolved } = registry.resolve(registryRunId, originalArgs, { substituteIntentKey: this.toolDeclaresBusinessIntent(call.name) });
1230
+ const options = { substituteIntentKey: this.toolDeclaresBusinessIntent(call.name) };
1231
+ const { resolved, unresolved } = preBatchSnapshot != null ? preBatchSnapshot.resolve(originalArgs, options) : registry.resolve(registryRunId, originalArgs, options);
1130
1232
  resolvedArgs = resolved;
1131
1233
  if (unresolved.length > 0 && call.id != null) unresolvedByCallId.set(call.id, unresolved);
1132
1234
  }
@@ -1148,8 +1250,9 @@ var ToolNode = class extends require_run.RunnableCallable {
1148
1250
  });
1149
1251
  const hookRegistry = this.hookRegistry;
1150
1252
  const approvalReplaySessionId = getToolApprovalReplaySessionId(config, runId);
1253
+ const approvalReviewEvidence = require_approvalReview.getToolApprovalReviewEvidence(config, require_toolBatchReplay.getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? ""));
1151
1254
  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)) {
1255
+ if (hookRegistry?.hasHookFor("PreToolUse", runId) === true || hasPendingApproval || approvalReviewEvidence != null) {
1153
1256
  const preemptedEagerRecords = /* @__PURE__ */ new Map();
1154
1257
  if (this.eagerEventToolExecutions != null) for (const entry of preToolCalls) {
1155
1258
  const callId = entry.call.id;
@@ -1160,7 +1263,7 @@ var ToolNode = class extends require_run.RunnableCallable {
1160
1263
  this.eagerEventToolExecutions.delete(callId);
1161
1264
  }
1162
1265
  }
1163
- const preResults = await Promise.all(preToolCalls.map((entry) => {
1266
+ const preResults = hookRegistry == null ? preToolCalls.map(() => HOOK_FALLBACK) : await Promise.all(preToolCalls.map((entry) => {
1164
1267
  const toolUseId = entry.call.id;
1165
1268
  const approvalReplayKey = toolUseId == null ? void 0 : createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", toolUseId);
1166
1269
  return require_executeHooks.executeHooks({
@@ -1213,7 +1316,7 @@ var ToolNode = class extends require_run.RunnableCallable {
1213
1316
  const flushDeferredBlockedSideEffects = async () => {
1214
1317
  for (const item of deferredBlockedSideEffects) {
1215
1318
  await this.dispatchStepCompleted(item.callId, item.toolName, item.args, item.contentString, config);
1216
- if (hookRegistry.hasHookFor("PermissionDenied", runId)) require_executeHooks.executeHooks({
1319
+ if (hookRegistry?.hasHookFor("PermissionDenied", runId) === true) require_executeHooks.executeHooks({
1217
1320
  registry: hookRegistry,
1218
1321
  input: {
1219
1322
  hook_event_name: "PermissionDenied",
@@ -1257,6 +1360,13 @@ var ToolNode = class extends require_run.RunnableCallable {
1257
1360
  continue;
1258
1361
  }
1259
1362
  if (hookResult.updatedInput != null) applyInputOverride(entry, hookResult.updatedInput);
1363
+ else {
1364
+ const reviewed = require_approvalReview.getReviewedToolApproval(approvalReviewEvidence?.payload, entry.call.id);
1365
+ if (reviewed != null) {
1366
+ entry.args = this.coerceEventToolArgs(entry.call.name, reviewed.request.arguments);
1367
+ if (entry.call.id != null) unresolvedByCallId.delete(entry.call.id);
1368
+ }
1369
+ }
1260
1370
  askEntries.push({
1261
1371
  entry,
1262
1372
  reason: hookResult.reason,
@@ -1265,6 +1375,19 @@ var ToolNode = class extends require_run.RunnableCallable {
1265
1375
  continue;
1266
1376
  }
1267
1377
  if (hookResult.updatedInput != null) applyInputOverride(entry, hookResult.updatedInput);
1378
+ const reviewedApproval = require_approvalReview.getReviewedToolApproval(approvalReviewEvidence?.payload, entry.call.id);
1379
+ if (reviewedApproval != null) {
1380
+ if (hookResult.updatedInput == null) {
1381
+ entry.args = this.coerceEventToolArgs(entry.call.name, reviewedApproval.request.arguments);
1382
+ if (entry.call.id != null) unresolvedByCallId.delete(entry.call.id);
1383
+ }
1384
+ askEntries.push({
1385
+ entry,
1386
+ reason: reviewedApproval.request.description,
1387
+ allowedDecisions: reviewedApproval.reviewConfig.allowed_decisions
1388
+ });
1389
+ continue;
1390
+ }
1268
1391
  if (entry.call.id != null) {
1269
1392
  const preempted = preemptedEagerRecords.get(entry.call.id);
1270
1393
  if (preempted != null) this.eagerEventToolExecutions?.set(entry.call.id, preempted);
@@ -1274,9 +1397,12 @@ var ToolNode = class extends require_run.RunnableCallable {
1274
1397
  if (askEntries.length > 0) {
1275
1398
  const payload = buildToolApprovalInterruptPayload(askEntries, runId);
1276
1399
  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) {
1400
+ for (const { entry } of askEntries) hookRegistry?.clearPendingToolApproval(approvalReplaySessionId, createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", entry.call.id));
1401
+ const proposalMatches = approvalReviewEvidence == null || require_approvalReview.toolApprovalPayloadMatches(payload, approvalReviewEvidence.payload);
1402
+ if (!proposalMatches) for (const { entry } of askEntries) blockEntry(entry, "Reviewed tool proposal changed before resume; retry approval");
1403
+ const decisionEntries = proposalMatches ? askEntries : [];
1404
+ const decisionByCallId = normalizeApprovalDecisions(decisionEntries.map(({ entry }) => entry.call.id), resumeValue);
1405
+ for (const { entry, reason: askReason, allowedDecisions } of decisionEntries) {
1280
1406
  const decision = decisionByCallId.get(entry.call.id) ?? {
1281
1407
  type: "reject",
1282
1408
  reason: "No decision provided for tool approval"
@@ -1402,7 +1528,7 @@ var ToolNode = class extends require_run.RunnableCallable {
1402
1528
  userId: config.configurable?.user_id,
1403
1529
  agentId: this.executingAgentId,
1404
1530
  callerCapabilityProjection: this.getCallerCapabilityProjectionSnapshot(),
1405
- configurable: stripRunBreakerScope(config.configurable),
1531
+ configurable: stripHostExecutionConfig(config.configurable),
1406
1532
  metadata: config.metadata,
1407
1533
  signal: config.signal,
1408
1534
  resolve: (results) => {
@@ -1661,15 +1787,39 @@ var ToolNode = class extends require_run.RunnableCallable {
1661
1787
  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
1788
  }
1663
1789
  async runDirectBatchInterruptSafe(directCalls, batchIndices, config, baseContext) {
1664
- const settledBatchResults = baseContext.replayBatchKey == null ? void 0 : this.settledInterruptingResults.get(baseContext.replayBatchKey);
1790
+ const settledBatchResults = baseContext.replayBatchKey == null ? void 0 : this.settledDirectResultsByBatch.get(baseContext.replayBatchKey) ?? /* @__PURE__ */ new Map();
1791
+ const cacheResult = (call, position, result) => {
1792
+ if (baseContext.replayBatchKey == null || settledBatchResults == null) return;
1793
+ settledBatchResults.set(directReplayKey(call, baseContext.replayCallPositions?.get(call) ?? batchIndices[position]), result);
1794
+ this.settledDirectResultsByBatch.set(baseContext.replayBatchKey, settledBatchResults);
1795
+ };
1665
1796
  const restoreResult = (call, result) => {
1666
1797
  baseContext.additionalContextsSink?.push(...result.additionalContexts);
1798
+ const turn = result.turn;
1799
+ if (turn != null) this.recordToolUsageTurn(call.name, turn, call.id);
1800
+ const refMeta = (0, _langchain_core_messages.isBaseMessage)(result.output) ? result.output.additional_kwargs : void 0;
1801
+ if (refMeta?._refKey != null && result.referenceContent != null) {
1802
+ this.toolOutputRegistry?.set(baseContext.batchScopeId, refMeta._refKey, result.referenceContent);
1803
+ if ((0, _langchain_core_messages.isBaseMessage)(result.output)) result = {
1804
+ ...result,
1805
+ output: new _langchain_core_messages.ToolMessage({
1806
+ ...result.output,
1807
+ additional_kwargs: {
1808
+ ...result.output.additional_kwargs,
1809
+ _refScope: baseContext.batchScopeId
1810
+ }
1811
+ })
1812
+ };
1813
+ }
1667
1814
  if (call.id != null && result.resolvedArgs != null && baseContext.resolvedArgsByCallId != null) baseContext.resolvedArgsByCallId.set(call.id, result.resolvedArgs);
1668
1815
  return result;
1669
1816
  };
1670
1817
  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);
1818
+ const cachedResult = settledBatchResults?.get(directReplayKey(call, baseContext.replayCallPositions?.get(call) ?? batchIndices[position]));
1819
+ if (cachedResult != null) {
1820
+ 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");
1821
+ return restoreResult(call, cachedResult);
1822
+ }
1673
1823
  const additionalContexts = [];
1674
1824
  const output = await this.runDirectToolWithLifecycleHooks(call, config, {
1675
1825
  ...baseContext,
@@ -1677,14 +1827,32 @@ var ToolNode = class extends require_run.RunnableCallable {
1677
1827
  additionalContextsSink: additionalContexts
1678
1828
  });
1679
1829
  const resolvedArgs = call.id == null ? void 0 : baseContext.resolvedArgsByCallId?.get(call.id);
1680
- return restoreResult(call, {
1830
+ const result = {
1831
+ proposal: structuredClone({
1832
+ name: call.name,
1833
+ args: call.args
1834
+ }),
1835
+ turn: call.id == null ? void 0 : this.toolCallTurns.get(call.id) ?? this.directPathTurns.get(call.id),
1681
1836
  output,
1682
1837
  additionalContexts,
1683
1838
  ...resolvedArgs == null ? {} : { resolvedArgs }
1684
- });
1839
+ };
1840
+ const refMeta = (0, _langchain_core_messages.isBaseMessage)(output) ? output.additional_kwargs : void 0;
1841
+ if (refMeta?._refKey != null) result.referenceContent = this.toolOutputRegistry?.get(refMeta._refScope, refMeta._refKey);
1842
+ cacheResult(call, position, result);
1843
+ return restoreResult(call, result);
1685
1844
  };
1686
1845
  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);
1846
+ if (!(interrupting != null && directCalls.some((call) => interrupting.has(call.name)))) {
1847
+ const results = await Promise.allSettled(directCalls.map((call, i) => runOne(call, i)));
1848
+ this.throwIfBreakerTripped(config);
1849
+ const failed = results.find((result) => result.status === "rejected" && !(0, _langchain_langgraph.isGraphInterrupt)(result.reason)) ?? results.find((result) => result.status === "rejected");
1850
+ if (failed?.status === "rejected") throw failed.reason;
1851
+ return results.map((result) => {
1852
+ if (result.status === "rejected") throw result.reason;
1853
+ return result.value.output;
1854
+ });
1855
+ }
1688
1856
  const outputs = new Array(directCalls.length);
1689
1857
  const interruptingPositions = [];
1690
1858
  const regularPositions = [];
@@ -1705,12 +1873,6 @@ var ToolNode = class extends require_run.RunnableCallable {
1705
1873
  }
1706
1874
  const position = interruptingPositions[i];
1707
1875
  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
1876
  }
1715
1877
  if (hasInterruptingError) {
1716
1878
  if ((0, _langchain_langgraph.isGraphInterrupt)(interruptingError)) {
@@ -1752,7 +1914,7 @@ var ToolNode = class extends require_run.RunnableCallable {
1752
1914
  const outputs = [...toolMessages, ...injected];
1753
1915
  return Array.isArray(input) ? outputs : { messages: outputs };
1754
1916
  }
1755
- async run(input, config) {
1917
+ async run(input, config, referenceReplay = {}) {
1756
1918
  const breakerSignal = this.getBreakerSignal?.();
1757
1919
  const composedSignal = require_misc.composeAbortSignals(config.signal, breakerSignal);
1758
1920
  const batchRunScope = this.getRunScope?.();
@@ -1768,10 +1930,11 @@ var ToolNode = class extends require_run.RunnableCallable {
1768
1930
  this.toolCallTurns.clear();
1769
1931
  const resolvedArgsByCallId = /* @__PURE__ */ new Map();
1770
1932
  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;
1933
+ const batchScopeId = config.configurable?.run_id ?? `\0anon-${this.anonBatchCounter++}`;
1934
+ const resumedReferenceState = referenceReplay.state;
1935
+ const replayInputSnapshot = resumedReferenceState == null ? void 0 : this.toolOutputRegistry?.resumeBatch(batchScopeId, resumedReferenceState);
1936
+ referenceReplay.state = resumedReferenceState ?? this.toolOutputRegistry?.snapshotState(batchScopeId);
1937
+ const turn = resumedReferenceState?.turnCounter ?? this.toolOutputRegistry?.nextTurn(batchScopeId) ?? 0;
1775
1938
  let outputs;
1776
1939
  let replayBatchKey;
1777
1940
  let promotedAiMessage;
@@ -1781,17 +1944,19 @@ var ToolNode = class extends require_run.RunnableCallable {
1781
1944
  if (this.eventDrivenMode && !isLocalTool) return this.executeViaEvent([input.lg_tool_call], config, input, {
1782
1945
  batchIndices: [0],
1783
1946
  turn,
1784
- batchScopeId
1947
+ batchScopeId,
1948
+ preBatchSnapshot: replayInputSnapshot
1785
1949
  });
1786
1950
  const directAdditionalContexts = [];
1787
1951
  const { lg_tool_call: _sendToolCall, ...sendState } = input;
1788
1952
  const sendMessages = sendState.messages;
1789
1953
  const sendAssistantBatch = Array.isArray(sendMessages) ? findAssistantBatch(sendMessages) : void 0;
1790
- const sendReplayBatchKey = sendAssistantBatch == null ? void 0 : getAssistantBatchReplayKey(sendAssistantBatch, incomingRunId, incomingThreadId);
1954
+ const sendReplayBatchKey = sendAssistantBatch == null || require_toolBatchReplay.getToolBatchReplayScope(config) == null ? void 0 : getAssistantBatchReplayKey(sendAssistantBatch, require_toolBatchReplay.getToolBatchReplayScope(config));
1791
1955
  const sendOutput = await this.runDirectToolWithLifecycleHooks(input.lg_tool_call, config, {
1792
1956
  batchIndex: 0,
1793
1957
  turn,
1794
1958
  batchScopeId,
1959
+ preBatchSnapshot: replayInputSnapshot,
1795
1960
  resolvedArgsByCallId,
1796
1961
  errorOwnership,
1797
1962
  additionalContextsSink: directAdditionalContexts,
@@ -1815,14 +1980,15 @@ var ToolNode = class extends require_run.RunnableCallable {
1815
1980
  const assistantBatch = findAssistantBatch(messages);
1816
1981
  if (assistantBatch == null) throw new Error("ToolNode only accepts AIMessages as input.");
1817
1982
  const aiMessage = assistantBatch.message;
1818
- replayBatchKey = getAssistantBatchReplayKey(assistantBatch, incomingRunId, incomingThreadId);
1983
+ replayBatchKey = require_toolBatchReplay.getToolBatchReplayScope(config) == null ? void 0 : getAssistantBatchReplayKey(assistantBatch, require_toolBatchReplay.getToolBatchReplayScope(config));
1819
1984
  if (this.loadRuntimeTools) {
1820
1985
  const { tools, toolMap } = this.loadRuntimeTools(aiMessage.tool_calls ?? []);
1821
1986
  this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
1822
1987
  this.applyToolExecutionOverrides();
1823
1988
  }
1989
+ const replayCallPositions = new Map((aiMessage.tool_calls ?? []).map((call, i) => [call, i]));
1824
1990
  const filteredCalls = aiMessage.tool_calls?.filter((call) => {
1825
- return (call.id == null || !toolMessageIds.has(call.id)) && !(call.id?.startsWith("srvtoolu_") ?? false);
1991
+ return (call.id == null || call.id === "" || !toolMessageIds.has(call.id)) && !(call.id?.startsWith("srvtoolu_") ?? false);
1826
1992
  }) ?? [];
1827
1993
  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
1994
  invalidCallResults = attributableInvalidCalls.map((call) => new _langchain_core_messages.ToolMessage({
@@ -1864,14 +2030,16 @@ var ToolNode = class extends require_run.RunnableCallable {
1864
2030
  if (directEntries.length === 0 && invalidCallResults.length === 0) return this.executeViaEvent(filteredCalls, config, input, {
1865
2031
  batchIndices: eventEntries.map((entry) => entry.batchIndex),
1866
2032
  turn,
1867
- batchScopeId
2033
+ batchScopeId,
2034
+ preBatchSnapshot: replayInputSnapshot
1868
2035
  });
1869
2036
  const directCalls = directEntries.map((e) => e.call);
1870
2037
  const directIndices = directEntries.map((e) => e.batchIndex);
1871
2038
  const eventCalls = eventEntries.map((e) => e.call);
1872
2039
  const eventIndices = eventEntries.map((e) => e.batchIndex);
2040
+ const approvalScopeCallIds = new Set(filteredCalls.flatMap((call) => call.id == null ? [] : [call.id]));
1873
2041
  const preResolvedEventArgs = /* @__PURE__ */ new Map();
1874
- const preBatchSnapshot = this.toolOutputRegistry?.snapshot(batchScopeId);
2042
+ const preBatchSnapshot = replayInputSnapshot ?? this.toolOutputRegistry?.snapshot(batchScopeId);
1875
2043
  if (preBatchSnapshot != null) {
1876
2044
  for (const entry of eventEntries) if (entry.call.id != null) {
1877
2045
  const { resolved, unresolved } = preBatchSnapshot.resolve(entry.call.args, { substituteIntentKey: this.toolDeclaresBusinessIntent(entry.call.name) });
@@ -1890,9 +2058,25 @@ var ToolNode = class extends require_run.RunnableCallable {
1890
2058
  preBatchSnapshot,
1891
2059
  additionalContextsSink: directAdditionalContexts,
1892
2060
  replayBatchKey,
2061
+ replayCallPositions,
2062
+ approvalScopeCallIds,
1893
2063
  runInput: input
1894
2064
  }) : [];
1895
- if (directCalls.length > 0 && directOutputs.length > 0) await this.handleRunToolCompletions(directCalls, directOutputs, config, resolvedArgsByCallId, errorOwnership);
2065
+ const settledDirectResults = replayBatchKey == null ? void 0 : this.settledDirectResultsByBatch.get(replayBatchKey);
2066
+ const unhandledDirectCalls = [];
2067
+ const unhandledDirectOutputs = [];
2068
+ for (let i = 0; i < directCalls.length; i++) {
2069
+ if ((settledDirectResults?.get(directReplayKey(directCalls[i], replayCallPositions.get(directCalls[i]))))?.completionHandled === true) continue;
2070
+ unhandledDirectCalls.push(directCalls[i]);
2071
+ unhandledDirectOutputs.push(directOutputs[i]);
2072
+ }
2073
+ if (unhandledDirectCalls.length > 0) {
2074
+ await this.handleRunToolCompletions(unhandledDirectCalls, unhandledDirectOutputs, config, resolvedArgsByCallId, errorOwnership);
2075
+ for (let i = 0; i < directCalls.length; i++) {
2076
+ const settled = settledDirectResults?.get(directReplayKey(directCalls[i], replayCallPositions.get(directCalls[i])));
2077
+ if (settled != null) settled.completionHandled = true;
2078
+ }
2079
+ }
1896
2080
  const eventResult = eventCalls.length > 0 ? await this.dispatchToolEvents(eventCalls, config, {
1897
2081
  batchIndices: eventIndices,
1898
2082
  turn,
@@ -1919,8 +2103,9 @@ var ToolNode = class extends require_run.RunnableCallable {
1919
2103
  ...eventResult.injected
1920
2104
  ];
1921
2105
  } else {
1922
- const preBatchSnapshot = this.toolOutputRegistry?.snapshot(batchScopeId);
2106
+ const preBatchSnapshot = replayInputSnapshot ?? this.toolOutputRegistry?.snapshot(batchScopeId);
1923
2107
  const directAdditionalContexts = [];
2108
+ const approvalScopeCallIds = new Set(filteredCalls.flatMap((call) => call.id == null ? [] : [call.id]));
1924
2109
  const toolOutputs = await this.runDirectBatchInterruptSafe(filteredCalls, filteredCalls.map((_call, i) => i), config, {
1925
2110
  turn,
1926
2111
  batchScopeId,
@@ -1929,6 +2114,8 @@ var ToolNode = class extends require_run.RunnableCallable {
1929
2114
  preBatchSnapshot,
1930
2115
  additionalContextsSink: directAdditionalContexts,
1931
2116
  replayBatchKey,
2117
+ replayCallPositions,
2118
+ approvalScopeCallIds,
1932
2119
  runInput: input
1933
2120
  });
1934
2121
  await this.handleRunToolCompletions(filteredCalls, toolOutputs, config, resolvedArgsByCallId, errorOwnership);
@@ -1957,7 +2144,7 @@ var ToolNode = class extends require_run.RunnableCallable {
1957
2144
  }
1958
2145
  }
1959
2146
  if (!outputs.some(_langchain_langgraph.isCommand)) {
1960
- if (replayBatchKey != null) this.settledInterruptingResults.delete(replayBatchKey);
2147
+ if (replayBatchKey != null) this.settledDirectResultsByBatch.delete(replayBatchKey);
1961
2148
  return Array.isArray(input) ? outputs : { messages: outputs };
1962
2149
  }
1963
2150
  if (promotedAiMessage != null) outputs = outputs.map((output) => (0, _langchain_langgraph.isCommand)(output) ? patchCommandUpdateForPromotedInvalidCalls(output, promotedAiMessage, invalidCallResults) : output);
@@ -2007,7 +2194,7 @@ var ToolNode = class extends require_run.RunnableCallable {
2007
2194
  combinedOutputs.push(parallelCommand);
2008
2195
  } else if (handoffCommands.length === 1) combinedOutputs.push(handoffCommands[0]);
2009
2196
  if (parentCommand) combinedOutputs.push(parentCommand);
2010
- if (replayBatchKey != null) this.settledInterruptingResults.delete(replayBatchKey);
2197
+ if (replayBatchKey != null) this.settledDirectResultsByBatch.delete(replayBatchKey);
2011
2198
  return combinedOutputs;
2012
2199
  }
2013
2200
  isSendInput(input) {