@librechat/agents 3.8.4 → 3.8.6

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 (81) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +1 -1
  2. package/dist/cjs/common/constants.cjs +2 -0
  3. package/dist/cjs/common/constants.cjs.map +1 -1
  4. package/dist/cjs/graphs/Graph.cjs +18 -9
  5. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  6. package/dist/cjs/hitl/approvalReview.cjs +1 -1
  7. package/dist/cjs/hooks/types.cjs.map +1 -1
  8. package/dist/cjs/main.cjs +7 -6
  9. package/dist/cjs/messages/index.cjs +1 -1
  10. package/dist/cjs/messages/prune.cjs +1 -1
  11. package/dist/cjs/run.cjs +7 -3
  12. package/dist/cjs/run.cjs.map +1 -1
  13. package/dist/cjs/stream.cjs +13 -3
  14. package/dist/cjs/stream.cjs.map +1 -1
  15. package/dist/cjs/summarization/node.cjs +3 -1
  16. package/dist/cjs/summarization/node.cjs.map +1 -1
  17. package/dist/cjs/tools/ToolNode.cjs +134 -32
  18. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  19. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +1 -0
  20. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
  21. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs +0 -1
  22. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs.map +1 -1
  23. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +123 -29
  24. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  25. package/dist/cjs/tools/toolBatchReplay.cjs +65 -13
  26. package/dist/cjs/tools/toolBatchReplay.cjs.map +1 -1
  27. package/dist/cjs/utils/index.cjs +1 -1
  28. package/dist/esm/agents/AgentContext.mjs +1 -1
  29. package/dist/esm/common/constants.mjs +2 -1
  30. package/dist/esm/common/constants.mjs.map +1 -1
  31. package/dist/esm/graphs/Graph.mjs +17 -8
  32. package/dist/esm/graphs/Graph.mjs.map +1 -1
  33. package/dist/esm/hitl/approvalReview.mjs +1 -1
  34. package/dist/esm/hooks/types.mjs.map +1 -1
  35. package/dist/esm/main.mjs +6 -6
  36. package/dist/esm/messages/index.mjs +1 -1
  37. package/dist/esm/messages/prune.mjs +1 -1
  38. package/dist/esm/run.mjs +7 -3
  39. package/dist/esm/run.mjs.map +1 -1
  40. package/dist/esm/stream.mjs +13 -3
  41. package/dist/esm/stream.mjs.map +1 -1
  42. package/dist/esm/summarization/node.mjs +3 -1
  43. package/dist/esm/summarization/node.mjs.map +1 -1
  44. package/dist/esm/tools/ToolNode.mjs +135 -33
  45. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  46. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +1 -0
  47. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
  48. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs +0 -1
  49. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs.map +1 -1
  50. package/dist/esm/tools/subagent/SubagentExecutor.mjs +123 -29
  51. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  52. package/dist/esm/tools/toolBatchReplay.mjs +64 -13
  53. package/dist/esm/tools/toolBatchReplay.mjs.map +1 -1
  54. package/dist/esm/utils/index.mjs +1 -1
  55. package/dist/types/common/constants.d.ts +2 -0
  56. package/dist/types/graphs/Graph.d.ts +3 -2
  57. package/dist/types/hooks/types.d.ts +3 -0
  58. package/dist/types/run.d.ts +1 -0
  59. package/dist/types/summarization/node.d.ts +1 -0
  60. package/dist/types/tools/ToolNode.d.ts +4 -2
  61. package/dist/types/tools/subagent/SubagentExecutor.d.ts +7 -4
  62. package/dist/types/tools/toolBatchReplay.d.ts +6 -3
  63. package/dist/types/types/graph.d.ts +28 -0
  64. package/dist/types/types/run.d.ts +2 -0
  65. package/dist/types/types/tools.d.ts +11 -1
  66. package/package.json +1 -1
  67. package/src/common/constants.ts +3 -0
  68. package/src/graphs/Graph.ts +32 -18
  69. package/src/hooks/types.ts +3 -0
  70. package/src/run.ts +30 -15
  71. package/src/stream.ts +27 -5
  72. package/src/summarization/node.ts +3 -0
  73. package/src/tools/ToolNode.ts +528 -149
  74. package/src/tools/local/LocalProgrammaticToolCalling.ts +1 -0
  75. package/src/tools/subagent/SubagentExecutionRegistry.ts +0 -1
  76. package/src/tools/subagent/SubagentExecutor.ts +216 -48
  77. package/src/tools/toolBatchReplay.ts +121 -20
  78. package/src/types/graph.ts +37 -0
  79. package/src/types/run.ts +2 -0
  80. package/src/types/tools.ts +11 -1
  81. package/src/utils/llmConfig.ts +0 -1
@@ -49,10 +49,21 @@ import type {
49
49
  PostToolBatchEntry,
50
50
  ToolApprovalReplayKey,
51
51
  } from '@/hooks';
52
- import type { PreparedSubagents } from '@/tools/preparedSubagents';
53
52
  import type { SettledToolBatchResult } from '@/tools/toolBatchReplay';
53
+ import type { PreparedSubagents } from '@/tools/preparedSubagents';
54
54
  import type { RunBreakerScope } from '@/llm/streamLimits';
55
55
  import type * as t from '@/types';
56
+ import {
57
+ TOOL_BATCH_REPLAY_KEY,
58
+ getToolBatchReplayOwner,
59
+ getToolBatchReplayScope,
60
+ attachToolBatchReplayState,
61
+ restoreToolBatchReplayState,
62
+ getToolBatchReplayState,
63
+ clearStaleToolApprovalConfig,
64
+ isChildToolReplayOwner,
65
+ getToolReplayResumeStatus,
66
+ } from '@/tools/toolBatchReplay';
56
67
  import {
57
68
  type CallerCapabilityProjection,
58
69
  createCallerCapabilityProjectionSnapshot,
@@ -121,24 +132,14 @@ import { stripCodeSessionFileSummary } from '@/tools/CodeSessionFileSummary';
121
132
  import { Constants, GraphEvents, CODE_EXECUTION_TOOLS } from '@/common';
122
133
  import { PreparedSubagentError } from '@/tools/preparedSubagents';
123
134
  import { attachRunStepResumeState } from '@/tools/runStepResume';
124
- import {
125
- TOOL_BATCH_REPLAY_KEY,
126
- getToolBatchReplayOwner,
127
- getToolBatchReplayScope,
128
- attachToolBatchReplayState,
129
- restoreToolBatchReplayState,
130
- getToolBatchReplayState,
131
- isChildToolReplayOwner,
132
- isToolReplayResume,
133
- } from '@/tools/toolBatchReplay';
134
135
 
135
136
  function stripToolApprovalReviewConfig(
136
137
  configurable: Record<string, unknown> | undefined
137
138
  ): Record<string, unknown> | undefined {
138
139
  if (
139
140
  configurable == null ||
140
- !(TOOL_APPROVAL_REVIEW_CONFIG_KEY in configurable) &&
141
- !(TOOL_BATCH_REPLAY_KEY in configurable)
141
+ (!(TOOL_APPROVAL_REVIEW_CONFIG_KEY in configurable) &&
142
+ !(TOOL_BATCH_REPLAY_KEY in configurable))
142
143
  ) {
143
144
  return configurable;
144
145
  }
@@ -473,8 +474,12 @@ function getAssistantBatchReplayKey(
473
474
  ): string {
474
475
  return JSON.stringify([
475
476
  threadId ?? null,
476
- batch.message.id == null || batch.message.id === '' ? null : batch.message.id,
477
- createHash('sha256').update(stableStringify(batch.message.tool_calls ?? [])).digest('hex'),
477
+ batch.message.id == null || batch.message.id === ''
478
+ ? null
479
+ : batch.message.id,
480
+ createHash('sha256')
481
+ .update(stableStringify(batch.message.tool_calls ?? []))
482
+ .digest('hex'),
478
483
  ]);
479
484
  }
480
485
 
@@ -711,6 +716,53 @@ function updateCodeSession(
711
716
  });
712
717
  }
713
718
 
719
+ /** Retains host-provisioned inputs even when execution returns no usable artifact. */
720
+ function retainCodeSessionInputs(
721
+ sessions: t.ToolSessionMap,
722
+ sessionKey: string,
723
+ context: t.ToolCallRequest['codeSessionContext']
724
+ ): void {
725
+ if (
726
+ context?.session_id == null ||
727
+ context.session_id === '' ||
728
+ context.files == null
729
+ )
730
+ return;
731
+ const existing = sessions.get(sessionKey) as t.CodeSessionContext | undefined;
732
+ const existingFiles = existing?.files ?? [];
733
+ const files = [...existingFiles];
734
+ const indexByIdentity = new Map<string, number>();
735
+ const indexByName = new Map<string, number>();
736
+ for (let i = 0; i < files.length; i++) {
737
+ indexByIdentity.set(fileIdentityKey(files[i]), i);
738
+ indexByName.set(files[i].name, i);
739
+ }
740
+ let changed = existing?.session_id !== context.session_id;
741
+ for (const file of context.files) {
742
+ if (!file.id || !file.name || !file.storage_session_id) continue;
743
+ const identity = fileIdentityKey(file);
744
+ if (indexByIdentity.has(identity)) continue;
745
+ const replacementIndex = indexByName.get(file.name);
746
+ if (replacementIndex !== undefined) {
747
+ indexByIdentity.delete(fileIdentityKey(files[replacementIndex]));
748
+ files[replacementIndex] = { ...file };
749
+ indexByIdentity.set(identity, replacementIndex);
750
+ changed = true;
751
+ continue;
752
+ }
753
+ indexByIdentity.set(identity, files.length);
754
+ indexByName.set(file.name, files.length);
755
+ files.push({ ...file });
756
+ changed = true;
757
+ }
758
+ if (!changed) return;
759
+ sessions.set(sessionKey, {
760
+ session_id: context.session_id,
761
+ files,
762
+ lastUpdated: Date.now(),
763
+ });
764
+ }
765
+
714
766
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
715
767
  export class ToolNode<T = any> extends RunnableCallable<T, T> {
716
768
  private toolMap: Map<string, StructuredToolInterface | RunnableToolLike>;
@@ -786,6 +838,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
786
838
  * even where `agentId` (the subagent-scope marker) is undefined.
787
839
  */
788
840
  private executingAgentId?: string;
841
+ private readonly executionContext?: t.ToolNodeOptions['executionContext'];
789
842
  private executingAgentName?: string;
790
843
  private rootAgentId?: string;
791
844
  private rootAgentName?: string;
@@ -875,6 +928,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
875
928
  eagerEventToolSuppressions,
876
929
  agentId,
877
930
  executingAgentId,
931
+ executionContext,
878
932
  executingAgentName,
879
933
  rootAgentId,
880
934
  rootAgentName,
@@ -899,56 +953,124 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
899
953
  name: name ?? TOOL_NODE_RUN_NAME,
900
954
  tags,
901
955
  func: async (input, config) => {
902
- const messages = Array.isArray(input) ? input as BaseMessage[] :
903
- (input as { messages?: BaseMessage[] }).messages;
904
- const assistantBatch = messages == null ? undefined : findAssistantBatch(messages);
905
- const activeReplayKey = assistantBatch == null || getToolBatchReplayScope(config) == null ? undefined : getAssistantBatchReplayKey(
906
- assistantBatch, getToolBatchReplayScope(config)
907
- );
956
+ const messages = Array.isArray(input)
957
+ ? (input as BaseMessage[])
958
+ : (input as { messages?: BaseMessage[] }).messages;
959
+ const assistantBatch =
960
+ messages == null ? undefined : findAssistantBatch(messages);
961
+ const activeReplayKey =
962
+ assistantBatch == null || getToolBatchReplayScope(config) == null
963
+ ? undefined
964
+ : getAssistantBatchReplayKey(
965
+ assistantBatch,
966
+ getToolBatchReplayScope(config)
967
+ );
908
968
  const replayOwner = getToolBatchReplayOwner(
909
969
  config,
910
970
  this.executingAgentId ?? this.agentId ?? ''
911
971
  );
912
972
  let reviewEvidence = getToolApprovalReviewEvidence(config);
913
973
  const replayState = getToolBatchReplayState(config.configurable);
914
- const ownsParentBatch = replayState?.records.some(
915
- (record) => record.owner === replayOwner && record.batch === activeReplayKey
916
- ) === true;
974
+ const ownsParentBatch =
975
+ replayState?.records.some(
976
+ (record) =>
977
+ record.owner === replayOwner && record.batch === activeReplayKey
978
+ ) === true;
917
979
  let isChildReplay = false;
918
980
  if (ownsParentBatch) {
919
- const calls = this.isSendInput(input) ? [input.lg_tool_call] : assistantBatch?.message.tool_calls ?? [];
920
- const trustedParentCallIds = new Set(calls.filter((call) =>
921
- call.name === Constants.SUBAGENT &&
922
- (this.toolMap.get(call.name) as ReplayableSubagentTool | undefined)?.[SUBAGENT_REPLAY_CONTROLLER] != null
923
- ).map((call) => call.id ?? ''));
924
- isChildReplay = reviewEvidence?.owner != null
925
- ? isChildToolReplayOwner(config, reviewEvidence.owner, trustedParentCallIds)
926
- : replayState.records.some((record) => isChildToolReplayOwner(config, record.owner, trustedParentCallIds));
981
+ const calls = this.isSendInput(input)
982
+ ? [input.lg_tool_call]
983
+ : (assistantBatch?.message.tool_calls ?? []);
984
+ const trustedParentCallIds = new Set(
985
+ calls
986
+ .filter(
987
+ (call) =>
988
+ call.name === Constants.SUBAGENT &&
989
+ (
990
+ this.toolMap.get(call.name) as
991
+ | ReplayableSubagentTool
992
+ | undefined
993
+ )?.[SUBAGENT_REPLAY_CONTROLLER] != null
994
+ )
995
+ .map((call) => call.id ?? '')
996
+ );
997
+ isChildReplay =
998
+ reviewEvidence?.owner != null
999
+ ? isChildToolReplayOwner(
1000
+ config,
1001
+ reviewEvidence.owner,
1002
+ trustedParentCallIds
1003
+ )
1004
+ : replayState.records.some((record) =>
1005
+ isChildToolReplayOwner(
1006
+ config,
1007
+ record.owner,
1008
+ trustedParentCallIds
1009
+ )
1010
+ );
1011
+ }
1012
+ const hasApprovalAuthorization =
1013
+ reviewEvidence?.owner != null || replayState?.approvalOwner != null;
1014
+ const replayResumeStatus =
1015
+ hasApprovalAuthorization || replayState != null
1016
+ ? getToolReplayResumeStatus(
1017
+ config,
1018
+ reviewEvidence?.interruptId ?? replayState?.interruptId,
1019
+ isChildReplay
1020
+ )
1021
+ : undefined;
1022
+ if (replayResumeStatus === 'unverifiable' && hasApprovalAuthorization) {
1023
+ throw new Error(
1024
+ 'Cannot verify the active tool approval resume — failing closed'
1025
+ );
927
1026
  }
928
- if ((reviewEvidence?.owner != null || replayState != null) &&
929
- !isToolReplayResume(config, reviewEvidence?.interruptId ?? replayState?.interruptId, isChildReplay)) {
1027
+ if (replayResumeStatus === 'stale') {
1028
+ const configurable = { ...config.configurable };
1029
+ clearStaleToolApprovalConfig(
1030
+ configurable,
1031
+ replayOwner,
1032
+ activeReplayKey
1033
+ );
930
1034
  config = {
931
1035
  ...config,
932
- configurable: {
933
- ...config.configurable,
934
- [TOOL_APPROVAL_REVIEW_CONFIG_KEY]: undefined,
935
- [TOOL_BATCH_REPLAY_KEY]: undefined,
936
- },
1036
+ configurable,
937
1037
  };
938
1038
  reviewEvidence = undefined;
939
1039
  }
940
- if (reviewEvidence?.owner != null && reviewEvidence.owner !== replayOwner && !isChildReplay) {
941
- throw new Error('Tool approval execution owner changed before resume — failing closed');
1040
+ if (
1041
+ reviewEvidence?.owner != null &&
1042
+ reviewEvidence.owner !== replayOwner &&
1043
+ !isChildReplay
1044
+ ) {
1045
+ throw new Error(
1046
+ 'Tool approval execution owner changed before resume — failing closed'
1047
+ );
942
1048
  }
943
1049
  const referenceReplay: { state?: ToolOutputReferenceState } = {};
944
- const restoredBatches = await restoreToolBatchReplayState(config, replayOwner);
945
- if (restoredBatches.length > 0 && !restoredBatches.some(({ batch }) => batch === activeReplayKey)) {
946
- throw new Error('Tool batch proposal changed before approval replay — failing closed');
1050
+ const restoredBatches = await restoreToolBatchReplayState(
1051
+ config,
1052
+ replayOwner
1053
+ );
1054
+ if (
1055
+ restoredBatches.length > 0 &&
1056
+ !restoredBatches.some(({ batch }) => batch === activeReplayKey)
1057
+ ) {
1058
+ throw new Error(
1059
+ 'Tool batch proposal changed before approval replay — failing closed'
1060
+ );
947
1061
  }
948
- if (activeReplayKey != null && getToolBatchReplayState(config.configurable) != null) {
1062
+ if (
1063
+ activeReplayKey != null &&
1064
+ getToolBatchReplayState(config.configurable) != null
1065
+ ) {
949
1066
  this.settledDirectResultsByBatch.delete(activeReplayKey);
950
1067
  }
951
- for (const { batch, results, turnState, referenceState } of restoredBatches) {
1068
+ for (const {
1069
+ batch,
1070
+ results,
1071
+ turnState,
1072
+ referenceState,
1073
+ } of restoredBatches) {
952
1074
  if (batch !== activeReplayKey) {
953
1075
  continue;
954
1076
  }
@@ -968,27 +1090,45 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
968
1090
  throw error;
969
1091
  }
970
1092
  const resumeState = createRunStepResumeState?.();
971
- const activeResults = activeReplayKey == null ? undefined : this.settledDirectResultsByBatch.get(activeReplayKey);
972
- const activeBatches = activeReplayKey == null
973
- ? new Map<string, Map<string, SettledDirectToolResult>>()
974
- : new Map([[activeReplayKey, activeResults ?? new Map<string, SettledDirectToolResult>()]]);
1093
+ const activeResults =
1094
+ activeReplayKey == null
1095
+ ? undefined
1096
+ : this.settledDirectResultsByBatch.get(activeReplayKey);
1097
+ const activeBatches =
1098
+ activeReplayKey == null
1099
+ ? new Map<string, Map<string, SettledDirectToolResult>>()
1100
+ : new Map([
1101
+ [
1102
+ activeReplayKey,
1103
+ activeResults ?? new Map<string, SettledDirectToolResult>(),
1104
+ ],
1105
+ ]);
975
1106
  const turnState = this.createSubagentResumeState();
976
- const activeCallIds = new Set(assistantBatch?.message.tool_calls?.map((call) => call.id));
977
- turnState.directPathTurns = turnState.directPathTurns.filter(({ toolCallId }) => activeCallIds.has(toolCallId));
1107
+ const activeCallIds = new Set(
1108
+ assistantBatch?.message.tool_calls?.map((call) => call.id)
1109
+ );
1110
+ turnState.directPathTurns = turnState.directPathTurns.filter(
1111
+ ({ toolCallId }) => activeCallIds.has(toolCallId)
1112
+ );
978
1113
  throw new GraphInterrupt(
979
- await Promise.all(error.interrupts.map(async (pendingInterrupt) => {
980
- const value = await attachToolBatchReplayState(
981
- pendingInterrupt.value,
982
- replayOwner,
983
- activeBatches,
984
- turnState,
985
- referenceReplay.state
986
- );
987
- return {
988
- ...pendingInterrupt,
989
- value: resumeState == null ? value : attachRunStepResumeState(value, resumeState),
990
- };
991
- }))
1114
+ await Promise.all(
1115
+ error.interrupts.map(async (pendingInterrupt) => {
1116
+ const value = await attachToolBatchReplayState(
1117
+ pendingInterrupt.value,
1118
+ replayOwner,
1119
+ activeBatches,
1120
+ turnState,
1121
+ referenceReplay.state
1122
+ );
1123
+ return {
1124
+ ...pendingInterrupt,
1125
+ value:
1126
+ resumeState == null
1127
+ ? value
1128
+ : attachRunStepResumeState(value, resumeState),
1129
+ };
1130
+ })
1131
+ )
992
1132
  );
993
1133
  }
994
1134
  if (createRunStepResumeState == null) {
@@ -1030,6 +1170,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
1030
1170
  // Default to agentId so callers constructing ToolNode directly (who pass the
1031
1171
  // existing agentId option) still get attribution without knowing the new option.
1032
1172
  this.executingAgentId = executingAgentId ?? agentId;
1173
+ this.executionContext = executionContext;
1033
1174
  this.executingAgentName = executingAgentName;
1034
1175
  this.rootAgentId = rootAgentId;
1035
1176
  this.rootAgentName = rootAgentName;
@@ -1099,8 +1240,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
1099
1240
  ? options
1100
1241
  : {
1101
1242
  ...options,
1243
+ configurable: {
1244
+ ...options?.configurable,
1245
+ executionContext: this.executionContext,
1246
+ },
1102
1247
  metadata: {
1103
1248
  ...options?.metadata,
1249
+ executionContext: this.executionContext,
1104
1250
  agentId: this.executingAgentId,
1105
1251
  activeAgentId: this.executingAgentId,
1106
1252
  ...(this.executingAgentName == null
@@ -1658,7 +1804,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
1658
1804
  return next;
1659
1805
  })();
1660
1806
  this.recordToolUsageTurn(call.name, usageCount, call.id);
1661
- if (batchContext.replayBatchKey != null && call.id != null && call.id !== '') {
1807
+ if (
1808
+ batchContext.replayBatchKey != null &&
1809
+ call.id != null &&
1810
+ call.id !== ''
1811
+ ) {
1662
1812
  this.directPathTurns.set(call.id, usageCount);
1663
1813
  }
1664
1814
  let args = call.args;
@@ -1722,6 +1872,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
1722
1872
  runId: (config.configurable?.run_id as string | undefined) ?? '',
1723
1873
  threadId: config.configurable?.thread_id as string | undefined,
1724
1874
  agentId: this.agentId,
1875
+ executionContext: this.executionContext,
1725
1876
  executingAgentId: this.executingAgentId,
1726
1877
  },
1727
1878
  };
@@ -2157,8 +2308,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2157
2308
  approvalReplaySessionId,
2158
2309
  approvalReplayKey
2159
2310
  );
2160
- const approvalReviewEvidence = getToolApprovalReviewEvidence(config,
2161
- getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? ''));
2311
+ const approvalReviewEvidence = getToolApprovalReviewEvidence(
2312
+ config,
2313
+ getToolBatchReplayOwner(
2314
+ config,
2315
+ this.executingAgentId ?? this.agentId ?? ''
2316
+ )
2317
+ );
2162
2318
  const reviewedApproval = getReviewedToolApproval(
2163
2319
  approvalReviewEvidence?.payload,
2164
2320
  call.id
@@ -2284,6 +2440,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2284
2440
  runId,
2285
2441
  threadId,
2286
2442
  agentId: this.agentId,
2443
+ executionContext: this.executionContext,
2287
2444
  executingAgentId: this.executingAgentId,
2288
2445
  toolName: call.name,
2289
2446
  toolInput: resolvedArgs,
@@ -2295,11 +2452,17 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2295
2452
  matchQuery: call.name,
2296
2453
  onceReplayKey: approvalReplayKey,
2297
2454
  onceReplaySessionId: approvalReplaySessionId,
2298
- }).catch((): AggregatedHookResult | undefined => approvalReviewEvidence == null ? undefined : {
2299
- decision: 'deny',
2300
- reason: 'Approval policy could not be evaluated on resume',
2301
- additionalContexts: [], injectedMessages: [], errors: [],
2302
- });
2455
+ }).catch((): AggregatedHookResult | undefined =>
2456
+ approvalReviewEvidence == null
2457
+ ? undefined
2458
+ : {
2459
+ decision: 'deny',
2460
+ reason: 'Approval policy could not be evaluated on resume',
2461
+ additionalContexts: [],
2462
+ injectedMessages: [],
2463
+ errors: [],
2464
+ }
2465
+ );
2303
2466
 
2304
2467
  if (preResult != null) {
2305
2468
  // Forward any additionalContext strings hooks returned into
@@ -2641,6 +2804,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2641
2804
  runId,
2642
2805
  threadId,
2643
2806
  agentId: this.agentId,
2807
+ executionContext: this.executionContext,
2644
2808
  executingAgentId: this.executingAgentId,
2645
2809
  toolName: call.name,
2646
2810
  toolInput: effectiveCall.args as Record<string, unknown>,
@@ -2678,6 +2842,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2678
2842
  runId,
2679
2843
  threadId,
2680
2844
  agentId: this.agentId,
2845
+ executionContext: this.executionContext,
2681
2846
  executingAgentId: this.executingAgentId,
2682
2847
  toolName: call.name,
2683
2848
  toolInput: effectiveCall.args as Record<string, unknown>,
@@ -2794,6 +2959,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2794
2959
  runId,
2795
2960
  threadId,
2796
2961
  agentId: this.agentId,
2962
+ executionContext: this.executionContext,
2797
2963
  executingAgentId: this.executingAgentId,
2798
2964
  toolName: call.name,
2799
2965
  toolInput: resolvedArgs,
@@ -2925,18 +3091,22 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2925
3091
 
2926
3092
  private storeCodeSessionFromResults(
2927
3093
  results: t.ToolExecuteResult[],
2928
- requestMap: Map<string, t.ToolCallRequest>
3094
+ requestMap: Map<string, t.ToolCallRequest>,
3095
+ baselineByRequestId: ReadonlyMap<
3096
+ string,
3097
+ ReadonlyMap<string, string>
3098
+ >
2929
3099
  ): void {
2930
3100
  if (!this.sessions) {
2931
3101
  return;
2932
3102
  }
2933
3103
 
3104
+ this.retainCodeSessionInputsFromRequests(
3105
+ requestMap.values(),
3106
+ baselineByRequestId
3107
+ );
2934
3108
  for (let i = 0; i < results.length; i++) {
2935
3109
  const result = results[i];
2936
- if (result.status !== 'success' || result.artifact == null) {
2937
- continue;
2938
- }
2939
-
2940
3110
  const request = requestMap.get(result.toolCallId);
2941
3111
  if (
2942
3112
  request?.name == null ||
@@ -2947,6 +3117,10 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2947
3117
  continue;
2948
3118
  }
2949
3119
 
3120
+ if (result.status !== 'success' || result.artifact == null) {
3121
+ continue;
3122
+ }
3123
+
2950
3124
  const artifact = result.artifact as t.CodeExecutionArtifact | undefined;
2951
3125
  const execSessionId = artifact?.session_id;
2952
3126
  if (execSessionId == null || execSessionId === '') {
@@ -2962,6 +3136,52 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2962
3136
  }
2963
3137
  }
2964
3138
 
3139
+ private retainCodeSessionInputsFromRequests(
3140
+ requests: Iterable<t.ToolCallRequest>,
3141
+ baselineByRequestId: ReadonlyMap<
3142
+ string,
3143
+ ReadonlyMap<string, string>
3144
+ >
3145
+ ): void {
3146
+ if (!this.sessions) {
3147
+ return;
3148
+ }
3149
+ const refreshedByName = new Map<string, t.CodeEnvFile>();
3150
+ let refreshedSessionId: string | undefined;
3151
+ let fileSessionId: string | undefined;
3152
+ for (const request of requests) {
3153
+ if (
3154
+ request.name === '' ||
3155
+ (!this.participatesInCodeSession(request.name) &&
3156
+ request.name !== Constants.SKILL_TOOL &&
3157
+ request.name !== Constants.READ_FILE &&
3158
+ request.retainCodeSessionInputs !== true)
3159
+ ) {
3160
+ continue;
3161
+ }
3162
+ const context = request.codeSessionContext;
3163
+ if (context?.session_id == null || context.session_id === '') continue;
3164
+ if (context.session_id !== request.codeSessionBaselineId) {
3165
+ refreshedSessionId = context.session_id;
3166
+ }
3167
+ const baselineIdentityByName = baselineByRequestId.get(request.id);
3168
+ for (const file of context.files ?? []) {
3169
+ if (!file.id || !file.name || !file.storage_session_id) continue;
3170
+ if (baselineIdentityByName?.get(file.name) === fileIdentityKey(file)) {
3171
+ continue;
3172
+ }
3173
+ fileSessionId = context.session_id;
3174
+ refreshedByName.set(file.name, file);
3175
+ }
3176
+ }
3177
+ const sessionId = refreshedSessionId ?? fileSessionId;
3178
+ if (sessionId == null) return;
3179
+ retainCodeSessionInputs(this.sessions, this.codeSessionKey, {
3180
+ session_id: sessionId,
3181
+ files: [...refreshedByName.values()],
3182
+ });
3183
+ }
3184
+
2965
3185
  /**
2966
3186
  * Post-processes standard runTool outputs: dispatches ON_RUN_STEP_COMPLETED
2967
3187
  * and stores code session context. Mirrors the completion handling in
@@ -3168,10 +3388,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3168
3388
  unresolvedByCallId.set(call.id, pre.unresolved);
3169
3389
  }
3170
3390
  } else if (registry != null) {
3171
- const options = { substituteIntentKey: this.toolDeclaresBusinessIntent(call.name) };
3172
- const { resolved, unresolved } = preBatchSnapshot != null
3173
- ? preBatchSnapshot.resolve(originalArgs, options)
3174
- : registry.resolve(registryRunId, originalArgs, options);
3391
+ const options = {
3392
+ substituteIntentKey: this.toolDeclaresBusinessIntent(call.name),
3393
+ };
3394
+ const { resolved, unresolved } =
3395
+ preBatchSnapshot != null
3396
+ ? preBatchSnapshot.resolve(originalArgs, options)
3397
+ : registry.resolve(registryRunId, originalArgs, options);
3175
3398
  resolvedArgs = resolved as Record<string, unknown>;
3176
3399
  if (unresolved.length > 0 && call.id != null) {
3177
3400
  unresolvedByCallId.set(call.id, unresolved);
@@ -3219,8 +3442,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3219
3442
  config,
3220
3443
  runId
3221
3444
  );
3222
- const approvalReviewEvidence = getToolApprovalReviewEvidence(config,
3223
- getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? ''));
3445
+ const approvalReviewEvidence = getToolApprovalReviewEvidence(
3446
+ config,
3447
+ getToolBatchReplayOwner(
3448
+ config,
3449
+ this.executingAgentId ?? this.agentId ?? ''
3450
+ )
3451
+ );
3224
3452
  const hasPendingApproval = preToolCalls.some(
3225
3453
  (entry) =>
3226
3454
  entry.call.id != null &&
@@ -3285,6 +3513,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3285
3513
  runId,
3286
3514
  threadId,
3287
3515
  agentId: this.agentId,
3516
+ executionContext: this.executionContext,
3288
3517
  executingAgentId: this.executingAgentId,
3289
3518
  toolName: entry.call.name,
3290
3519
  toolInput: entry.args,
@@ -3387,6 +3616,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3387
3616
  runId,
3388
3617
  threadId,
3389
3618
  agentId: this.agentId,
3619
+ executionContext: this.executionContext,
3390
3620
  executingAgentId: this.executingAgentId,
3391
3621
  toolName: item.toolName,
3392
3622
  toolInput: item.args,
@@ -3494,9 +3724,15 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3494
3724
  if (hookResult.updatedInput != null) {
3495
3725
  applyInputOverride(entry, hookResult.updatedInput);
3496
3726
  } else {
3497
- const reviewed = getReviewedToolApproval(approvalReviewEvidence?.payload, entry.call.id);
3727
+ const reviewed = getReviewedToolApproval(
3728
+ approvalReviewEvidence?.payload,
3729
+ entry.call.id
3730
+ );
3498
3731
  if (reviewed != null) {
3499
- entry.args = this.coerceEventToolArgs(entry.call.name, reviewed.request.arguments);
3732
+ entry.args = this.coerceEventToolArgs(
3733
+ entry.call.name,
3734
+ reviewed.request.arguments
3735
+ );
3500
3736
  if (entry.call.id != null) {
3501
3737
  unresolvedByCallId.delete(entry.call.id);
3502
3738
  }
@@ -3867,6 +4103,23 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3867
4103
  }
3868
4104
 
3869
4105
  const requestMap = new Map(plan.allRequests.map((r) => [r.id, r]));
4106
+ for (const request of plan.allRequests) {
4107
+ request.codeSessionBaselineId = request.codeSessionContext?.session_id;
4108
+ }
4109
+ const codeSessionBaselineByRequestId = new Map<
4110
+ string,
4111
+ ReadonlyMap<string, string>
4112
+ >(
4113
+ plan.allRequests.map((request) => [
4114
+ request.id,
4115
+ new Map(
4116
+ (request.codeSessionContext?.files ?? []).map((file) => [
4117
+ file.name,
4118
+ fileIdentityKey(file),
4119
+ ])
4120
+ ),
4121
+ ])
4122
+ );
3870
4123
  const eagerExecutions: Array<{
3871
4124
  request: t.ToolCallRequest;
3872
4125
  execution: t.EagerEventToolExecution;
@@ -3877,6 +4130,14 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3877
4130
  const eagerExecution = this.takeMatchingEagerEventExecution(request);
3878
4131
  if (eagerExecution != null) {
3879
4132
  eagerExecutions.push({ request, execution: eagerExecution });
4133
+ if (eagerExecution.codeSessionBaselineByName != null) {
4134
+ codeSessionBaselineByRequestId.set(
4135
+ request.id,
4136
+ eagerExecution.codeSessionBaselineByName
4137
+ );
4138
+ }
4139
+ request.codeSessionBaselineId =
4140
+ eagerExecution.request.codeSessionBaselineId;
3880
4141
  } else {
3881
4142
  dispatchRequests.push(request);
3882
4143
  }
@@ -3962,6 +4223,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3962
4223
  };
3963
4224
 
3964
4225
  const batchRequest: t.ToolExecuteBatchRequest = {
4226
+ executionContext: this.executionContext,
3965
4227
  toolCalls: dispatchRequests,
3966
4228
  userId: config.configurable?.user_id as string | undefined,
3967
4229
  // Dispatch attribution, NOT the hook subagent-scope marker:
@@ -4001,24 +4263,46 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4001
4263
 
4002
4264
  const eagerResultsPromise = Promise.all(
4003
4265
  eagerExecutions.map(async ({ request, execution }) => {
4004
- const results = await this.resolveEagerEventExecution(
4005
- request,
4006
- execution
4007
- );
4008
- return {
4009
- results,
4010
- completionDispatched:
4011
- execution.completionDispatched === true &&
4012
- execution.request.turn === request.turn,
4013
- toolCallId: request.id,
4014
- };
4266
+ try {
4267
+ const results = await this.resolveEagerEventExecution(
4268
+ request,
4269
+ execution
4270
+ );
4271
+ return {
4272
+ results,
4273
+ completionDispatched:
4274
+ execution.completionDispatched === true &&
4275
+ execution.request.turn === request.turn,
4276
+ toolCallId: request.id,
4277
+ };
4278
+ } finally {
4279
+ if (execution.request.codeSessionContext != null) {
4280
+ request.codeSessionContext = execution.request.codeSessionContext;
4281
+ }
4282
+ }
4015
4283
  })
4016
4284
  );
4017
4285
 
4018
- const [eagerResults, dispatchedResults] = await Promise.all([
4286
+ const [eagerOutcome, dispatchedOutcome] = await Promise.allSettled([
4019
4287
  eagerResultsPromise,
4020
4288
  dispatchPromise,
4021
4289
  ]);
4290
+ if (eagerOutcome.status === 'rejected') {
4291
+ this.retainCodeSessionInputsFromRequests(
4292
+ requestMap.values(),
4293
+ codeSessionBaselineByRequestId
4294
+ );
4295
+ throw eagerOutcome.reason;
4296
+ }
4297
+ if (dispatchedOutcome.status === 'rejected') {
4298
+ this.retainCodeSessionInputsFromRequests(
4299
+ requestMap.values(),
4300
+ codeSessionBaselineByRequestId
4301
+ );
4302
+ throw dispatchedOutcome.reason;
4303
+ }
4304
+ const eagerResults = eagerOutcome.value;
4305
+ const dispatchedResults = dispatchedOutcome.value;
4022
4306
  // Settle in-flight early completion dispatches before the batch loop
4023
4307
  // below decides which completions still need emitting.
4024
4308
  await Promise.allSettled(earlyCompletionDispatches);
@@ -4036,7 +4320,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4036
4320
  ...dispatchedResults,
4037
4321
  ];
4038
4322
 
4039
- this.storeCodeSessionFromResults(results, requestMap);
4323
+ this.storeCodeSessionFromResults(
4324
+ results,
4325
+ requestMap,
4326
+ codeSessionBaselineByRequestId
4327
+ );
4040
4328
 
4041
4329
  for (const result of results) {
4042
4330
  if (result.injectedMessages && result.injectedMessages.length > 0) {
@@ -4105,6 +4393,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4105
4393
  runId,
4106
4394
  threadId,
4107
4395
  agentId: this.agentId,
4396
+ executionContext: this.executionContext,
4108
4397
  executingAgentId: this.executingAgentId,
4109
4398
  toolName,
4110
4399
  toolInput: request?.args ?? {},
@@ -4157,6 +4446,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4157
4446
  runId,
4158
4447
  threadId,
4159
4448
  agentId: this.agentId,
4449
+ executionContext: this.executionContext,
4160
4450
  executingAgentId: this.executingAgentId,
4161
4451
  toolName,
4162
4452
  toolInput: request?.args ?? {},
@@ -4316,28 +4606,40 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4316
4606
  // effects) every round.
4317
4607
  this.eagerEventToolSuppressions?.add(request.name);
4318
4608
  this.eagerEventToolSuppressions?.add(execution.toolName);
4609
+ if (
4610
+ this.participatesInCodeSession(execution.toolName) ||
4611
+ execution.toolName === Constants.SKILL_TOOL ||
4612
+ execution.toolName === Constants.READ_FILE
4613
+ ) {
4614
+ request.retainCodeSessionInputs = true;
4615
+ }
4319
4616
  // eslint-disable-next-line no-console
4320
4617
  console.warn(
4321
4618
  '[ToolNode] eager prestart args diverged from the final request for ' +
4322
4619
  `tool "${request.name}" (toolCallId=${request.id}); suppressing ` +
4323
4620
  'eager prestart for this tool for the rest of the run'
4324
4621
  );
4622
+ const createMismatchOutcome = (): t.EagerEventToolExecutionOutcome => ({
4623
+ results: [
4624
+ {
4625
+ toolCallId: request.id,
4626
+ status: 'error',
4627
+ content: '',
4628
+ errorMessage:
4629
+ 'Tool call changed after eager execution started; refusing to re-run the tool to avoid duplicate side effects.',
4630
+ },
4631
+ ],
4632
+ });
4325
4633
  return {
4326
4634
  toolCallId: request.id,
4327
4635
  toolName: request.name,
4328
4636
  args: request.args,
4329
- request,
4330
- promise: Promise.resolve({
4331
- results: [
4332
- {
4333
- toolCallId: request.id,
4334
- status: 'error',
4335
- content: '',
4336
- errorMessage:
4337
- 'Tool call changed after eager execution started; refusing to re-run the tool to avoid duplicate side effects.',
4338
- },
4339
- ],
4340
- }),
4637
+ request: execution.request,
4638
+ codeSessionBaselineByName: execution.codeSessionBaselineByName,
4639
+ promise: execution.promise.then(
4640
+ createMismatchOutcome,
4641
+ createMismatchOutcome
4642
+ ),
4341
4643
  };
4342
4644
  }
4343
4645
 
@@ -4429,6 +4731,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4429
4731
  runId,
4430
4732
  threadId,
4431
4733
  agentId: this.agentId,
4734
+ executionContext: this.executionContext,
4432
4735
  executingAgentId: this.executingAgentId,
4433
4736
  entries: orderedBatchEntries,
4434
4737
  },
@@ -4637,13 +4940,16 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4637
4940
  position: number,
4638
4941
  result: SettledDirectToolResult
4639
4942
  ): void => {
4640
- if (
4641
- baseContext.replayBatchKey == null ||
4642
- settledBatchResults == null
4643
- ) {
4943
+ if (baseContext.replayBatchKey == null || settledBatchResults == null) {
4644
4944
  return;
4645
4945
  }
4646
- settledBatchResults.set(directReplayKey(call, baseContext.replayCallPositions?.get(call) ?? batchIndices[position]), result);
4946
+ settledBatchResults.set(
4947
+ directReplayKey(
4948
+ call,
4949
+ baseContext.replayCallPositions?.get(call) ?? batchIndices[position]
4950
+ ),
4951
+ result
4952
+ );
4647
4953
  this.settledDirectResultsByBatch.set(
4648
4954
  baseContext.replayBatchKey,
4649
4955
  settledBatchResults
@@ -4658,14 +4964,26 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4658
4964
  if (turn != null) {
4659
4965
  this.recordToolUsageTurn(call.name, turn, call.id);
4660
4966
  }
4661
- const refMeta = isBaseMessage(result.output) ? result.output.additional_kwargs as t.ToolMessageRefMetadata : undefined;
4967
+ const refMeta = isBaseMessage(result.output)
4968
+ ? (result.output.additional_kwargs as t.ToolMessageRefMetadata)
4969
+ : undefined;
4662
4970
  if (refMeta?._refKey != null && result.referenceContent != null) {
4663
- this.toolOutputRegistry?.set(baseContext.batchScopeId, refMeta._refKey, result.referenceContent);
4971
+ this.toolOutputRegistry?.set(
4972
+ baseContext.batchScopeId,
4973
+ refMeta._refKey,
4974
+ result.referenceContent
4975
+ );
4664
4976
  if (isBaseMessage(result.output)) {
4665
- result = { ...result, output: new ToolMessage({
4666
- ...(result.output as ToolMessage),
4667
- additional_kwargs: { ...result.output.additional_kwargs, _refScope: baseContext.batchScopeId },
4668
- }) };
4977
+ result = {
4978
+ ...result,
4979
+ output: new ToolMessage({
4980
+ ...(result.output as ToolMessage),
4981
+ additional_kwargs: {
4982
+ ...result.output.additional_kwargs,
4983
+ _refScope: baseContext.batchScopeId,
4984
+ },
4985
+ }),
4986
+ };
4669
4987
  }
4670
4988
  }
4671
4989
  if (
@@ -4681,11 +4999,23 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4681
4999
  call: ToolCall,
4682
5000
  position: number
4683
5001
  ): Promise<SettledDirectToolResult> => {
4684
- const cachedResult = settledBatchResults?.get(directReplayKey(call, baseContext.replayCallPositions?.get(call) ?? batchIndices[position]));
5002
+ const cachedResult = settledBatchResults?.get(
5003
+ directReplayKey(
5004
+ call,
5005
+ baseContext.replayCallPositions?.get(call) ?? batchIndices[position]
5006
+ )
5007
+ );
4685
5008
  if (cachedResult != null) {
4686
- if (cachedResult.proposal.name !== call.name ||
4687
- !recordArgsEqual(cachedResult.proposal.args, call.args as Record<string, unknown>)) {
4688
- throw new Error('Cannot change an already completed tool call during approval replay');
5009
+ if (
5010
+ cachedResult.proposal.name !== call.name ||
5011
+ !recordArgsEqual(
5012
+ cachedResult.proposal.args,
5013
+ call.args as Record<string, unknown>
5014
+ )
5015
+ ) {
5016
+ throw new Error(
5017
+ 'Cannot change an already completed tool call during approval replay'
5018
+ );
4689
5019
  }
4690
5020
  return restoreResult(call, cachedResult);
4691
5021
  }
@@ -4700,15 +5030,27 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4700
5030
  ? undefined
4701
5031
  : baseContext.resolvedArgsByCallId?.get(call.id);
4702
5032
  const result: SettledDirectToolResult = {
4703
- proposal: structuredClone({ name: call.name, args: call.args as Record<string, unknown> }),
4704
- turn: call.id == null ? undefined : this.toolCallTurns.get(call.id) ?? this.directPathTurns.get(call.id),
5033
+ proposal: structuredClone({
5034
+ name: call.name,
5035
+ args: call.args as Record<string, unknown>,
5036
+ }),
5037
+ turn:
5038
+ call.id == null
5039
+ ? undefined
5040
+ : (this.toolCallTurns.get(call.id) ??
5041
+ this.directPathTurns.get(call.id)),
4705
5042
  output,
4706
5043
  additionalContexts,
4707
5044
  ...(resolvedArgs == null ? {} : { resolvedArgs }),
4708
5045
  };
4709
- const refMeta = isBaseMessage(output) ? output.additional_kwargs as t.ToolMessageRefMetadata : undefined;
5046
+ const refMeta = isBaseMessage(output)
5047
+ ? (output.additional_kwargs as t.ToolMessageRefMetadata)
5048
+ : undefined;
4710
5049
  if (refMeta?._refKey != null) {
4711
- result.referenceContent = this.toolOutputRegistry?.get(refMeta._refScope, refMeta._refKey);
5050
+ result.referenceContent = this.toolOutputRegistry?.get(
5051
+ refMeta._refScope,
5052
+ refMeta._refKey
5053
+ );
4712
5054
  }
4713
5055
  cacheResult(call, position, result);
4714
5056
  return restoreResult(call, result);
@@ -4724,8 +5066,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4724
5066
  directCalls.map((call, i) => runOne(call, i))
4725
5067
  );
4726
5068
  this.throwIfBreakerTripped(config);
4727
- const failed = results.find((result) => result.status === 'rejected' && !isGraphInterrupt(result.reason)) ??
4728
- results.find((result) => result.status === 'rejected');
5069
+ const failed =
5070
+ results.find(
5071
+ (result) =>
5072
+ result.status === 'rejected' && !isGraphInterrupt(result.reason)
5073
+ ) ?? results.find((result) => result.status === 'rejected');
4729
5074
  if (failed?.status === 'rejected') {
4730
5075
  throw failed.reason;
4731
5076
  }
@@ -4867,8 +5212,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4867
5212
  return (Array.isArray(input) ? outputs : { messages: outputs }) as T;
4868
5213
  }
4869
5214
 
4870
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
4871
- protected async run(input: any, config: RunnableConfig, referenceReplay: { state?: ToolOutputReferenceState } = {}): Promise<T> {
5215
+ protected async run(
5216
+ input: T,
5217
+ config: RunnableConfig,
5218
+ referenceReplay: { state?: ToolOutputReferenceState } = {}
5219
+ ): Promise<T> {
4872
5220
  /**
4873
5221
  * Breaker read once at batch entry: every downstream path (direct
4874
5222
  * `tool.invoke` runtimes and the ON_TOOL_EXECUTE dispatch) inherits this
@@ -4928,10 +5276,20 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4928
5276
  const incomingRunId = config.configurable?.run_id as string | undefined;
4929
5277
  const batchScopeId = incomingRunId ?? `\0anon-${this.anonBatchCounter++}`;
4930
5278
  const resumedReferenceState = referenceReplay.state;
4931
- const replayInputSnapshot = resumedReferenceState == null ? undefined :
4932
- this.toolOutputRegistry?.resumeBatch(batchScopeId, resumedReferenceState);
4933
- referenceReplay.state = resumedReferenceState ?? this.toolOutputRegistry?.snapshotState(batchScopeId);
4934
- const turn = resumedReferenceState?.turnCounter ?? this.toolOutputRegistry?.nextTurn(batchScopeId) ?? 0;
5279
+ const replayInputSnapshot =
5280
+ resumedReferenceState == null
5281
+ ? undefined
5282
+ : this.toolOutputRegistry?.resumeBatch(
5283
+ batchScopeId,
5284
+ resumedReferenceState
5285
+ );
5286
+ referenceReplay.state =
5287
+ resumedReferenceState ??
5288
+ this.toolOutputRegistry?.snapshotState(batchScopeId);
5289
+ const turn =
5290
+ resumedReferenceState?.turnCounter ??
5291
+ this.toolOutputRegistry?.nextTurn(batchScopeId) ??
5292
+ 0;
4935
5293
  let outputs: (BaseMessage | Command)[];
4936
5294
  let replayBatchKey: string | undefined;
4937
5295
  /** Hoisted from the messages-state branch so the Command tail can carry
@@ -5035,10 +5393,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5035
5393
  throw new Error('ToolNode only accepts AIMessages as input.');
5036
5394
  }
5037
5395
  const aiMessage = assistantBatch.message;
5038
- replayBatchKey = getToolBatchReplayScope(config) == null ? undefined : getAssistantBatchReplayKey(
5039
- assistantBatch,
5040
- getToolBatchReplayScope(config)
5041
- );
5396
+ replayBatchKey =
5397
+ getToolBatchReplayScope(config) == null
5398
+ ? undefined
5399
+ : getAssistantBatchReplayKey(
5400
+ assistantBatch,
5401
+ getToolBatchReplayScope(config)
5402
+ );
5042
5403
 
5043
5404
  if (this.loadRuntimeTools) {
5044
5405
  const { tools, toolMap } = this.loadRuntimeTools(
@@ -5049,7 +5410,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5049
5410
  this.applyToolExecutionOverrides();
5050
5411
  }
5051
5412
 
5052
- const replayCallPositions = new Map((aiMessage.tool_calls ?? []).map((call, i) => [call, i]));
5413
+ const replayCallPositions = new Map(
5414
+ (aiMessage.tool_calls ?? []).map((call, i) => [call, i])
5415
+ );
5053
5416
  const filteredCalls =
5054
5417
  aiMessage.tool_calls?.filter((call) => {
5055
5418
  /**
@@ -5059,7 +5422,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5059
5422
  * which are executed by the provider's API and don't require invocation
5060
5423
  */
5061
5424
  return (
5062
- (call.id == null || call.id === '' || !toolMessageIds.has(call.id)) &&
5425
+ (call.id == null ||
5426
+ call.id === '' ||
5427
+ !toolMessageIds.has(call.id)) &&
5063
5428
  !(
5064
5429
  call.id?.startsWith(Constants.ANTHROPIC_SERVER_TOOL_PREFIX) ??
5065
5430
  false
@@ -5224,7 +5589,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5224
5589
  * same-turn direct outputs.
5225
5590
  */
5226
5591
  const preBatchSnapshot =
5227
- replayInputSnapshot ?? this.toolOutputRegistry?.snapshot(batchScopeId);
5592
+ replayInputSnapshot ??
5593
+ this.toolOutputRegistry?.snapshot(batchScopeId);
5228
5594
  if (preBatchSnapshot != null) {
5229
5595
  for (const entry of eventEntries) {
5230
5596
  if (entry.call.id != null) {
@@ -5271,11 +5637,18 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5271
5637
  : [];
5272
5638
 
5273
5639
  const settledDirectResults =
5274
- replayBatchKey == null ? undefined : this.settledDirectResultsByBatch.get(replayBatchKey);
5640
+ replayBatchKey == null
5641
+ ? undefined
5642
+ : this.settledDirectResultsByBatch.get(replayBatchKey);
5275
5643
  const unhandledDirectCalls: ToolCall[] = [];
5276
5644
  const unhandledDirectOutputs: (BaseMessage | Command)[] = [];
5277
5645
  for (let i = 0; i < directCalls.length; i++) {
5278
- const settled = settledDirectResults?.get(directReplayKey(directCalls[i], replayCallPositions.get(directCalls[i])!));
5646
+ const settled = settledDirectResults?.get(
5647
+ directReplayKey(
5648
+ directCalls[i],
5649
+ replayCallPositions.get(directCalls[i])!
5650
+ )
5651
+ );
5279
5652
  if (settled?.completionHandled === true) {
5280
5653
  continue;
5281
5654
  }
@@ -5292,7 +5665,12 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5292
5665
  errorOwnership
5293
5666
  );
5294
5667
  for (let i = 0; i < directCalls.length; i++) {
5295
- const settled = settledDirectResults?.get(directReplayKey(directCalls[i], replayCallPositions.get(directCalls[i])!));
5668
+ const settled = settledDirectResults?.get(
5669
+ directReplayKey(
5670
+ directCalls[i],
5671
+ replayCallPositions.get(directCalls[i])!
5672
+ )
5673
+ );
5296
5674
  if (settled != null) {
5297
5675
  settled.completionHandled = true;
5298
5676
  }
@@ -5345,7 +5723,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5345
5723
  // leak a sibling's just-registered output into a sister
5346
5724
  // call's args mid-await (Codex P1 #18).
5347
5725
  const preBatchSnapshot =
5348
- replayInputSnapshot ?? this.toolOutputRegistry?.snapshot(batchScopeId);
5726
+ replayInputSnapshot ??
5727
+ this.toolOutputRegistry?.snapshot(batchScopeId);
5349
5728
  const directAdditionalContexts: string[] = [];
5350
5729
  const approvalScopeCallIds = new Set(
5351
5730
  filteredCalls.flatMap((call) => (call.id == null ? [] : [call.id]))