@librechat/agents 3.8.5 → 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 (77) 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 +127 -26
  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 +120 -26
  24. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  25. package/dist/cjs/tools/toolBatchReplay.cjs +1 -1
  26. package/dist/cjs/utils/index.cjs +1 -1
  27. package/dist/esm/agents/AgentContext.mjs +1 -1
  28. package/dist/esm/common/constants.mjs +2 -1
  29. package/dist/esm/common/constants.mjs.map +1 -1
  30. package/dist/esm/graphs/Graph.mjs +17 -8
  31. package/dist/esm/graphs/Graph.mjs.map +1 -1
  32. package/dist/esm/hitl/approvalReview.mjs +1 -1
  33. package/dist/esm/hooks/types.mjs.map +1 -1
  34. package/dist/esm/main.mjs +6 -6
  35. package/dist/esm/messages/index.mjs +1 -1
  36. package/dist/esm/messages/prune.mjs +1 -1
  37. package/dist/esm/run.mjs +7 -3
  38. package/dist/esm/run.mjs.map +1 -1
  39. package/dist/esm/stream.mjs +13 -3
  40. package/dist/esm/stream.mjs.map +1 -1
  41. package/dist/esm/summarization/node.mjs +3 -1
  42. package/dist/esm/summarization/node.mjs.map +1 -1
  43. package/dist/esm/tools/ToolNode.mjs +127 -26
  44. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  45. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +1 -0
  46. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
  47. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs +0 -1
  48. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs.map +1 -1
  49. package/dist/esm/tools/subagent/SubagentExecutor.mjs +120 -26
  50. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  51. package/dist/esm/tools/toolBatchReplay.mjs +1 -1
  52. package/dist/esm/utils/index.mjs +1 -1
  53. package/dist/types/common/constants.d.ts +2 -0
  54. package/dist/types/graphs/Graph.d.ts +3 -2
  55. package/dist/types/hooks/types.d.ts +3 -0
  56. package/dist/types/run.d.ts +1 -0
  57. package/dist/types/summarization/node.d.ts +1 -0
  58. package/dist/types/tools/ToolNode.d.ts +4 -2
  59. package/dist/types/tools/subagent/SubagentExecutor.d.ts +7 -4
  60. package/dist/types/types/graph.d.ts +28 -0
  61. package/dist/types/types/run.d.ts +2 -0
  62. package/dist/types/types/tools.d.ts +11 -1
  63. package/package.json +1 -1
  64. package/src/common/constants.ts +3 -0
  65. package/src/graphs/Graph.ts +32 -18
  66. package/src/hooks/types.ts +3 -0
  67. package/src/run.ts +30 -15
  68. package/src/stream.ts +27 -5
  69. package/src/summarization/node.ts +3 -0
  70. package/src/tools/ToolNode.ts +521 -152
  71. package/src/tools/local/LocalProgrammaticToolCalling.ts +1 -0
  72. package/src/tools/subagent/SubagentExecutionRegistry.ts +0 -1
  73. package/src/tools/subagent/SubagentExecutor.ts +214 -55
  74. package/src/types/graph.ts +37 -0
  75. package/src/types/run.ts +2 -0
  76. package/src/types/tools.ts +11 -1
  77. 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,25 +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
- clearStaleToolApprovalConfig,
132
- isChildToolReplayOwner,
133
- getToolReplayResumeStatus,
134
- } from '@/tools/toolBatchReplay';
135
135
 
136
136
  function stripToolApprovalReviewConfig(
137
137
  configurable: Record<string, unknown> | undefined
138
138
  ): Record<string, unknown> | undefined {
139
139
  if (
140
140
  configurable == null ||
141
- !(TOOL_APPROVAL_REVIEW_CONFIG_KEY in configurable) &&
142
- !(TOOL_BATCH_REPLAY_KEY in configurable)
141
+ (!(TOOL_APPROVAL_REVIEW_CONFIG_KEY in configurable) &&
142
+ !(TOOL_BATCH_REPLAY_KEY in configurable))
143
143
  ) {
144
144
  return configurable;
145
145
  }
@@ -474,8 +474,12 @@ function getAssistantBatchReplayKey(
474
474
  ): string {
475
475
  return JSON.stringify([
476
476
  threadId ?? null,
477
- batch.message.id == null || batch.message.id === '' ? null : batch.message.id,
478
- 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'),
479
483
  ]);
480
484
  }
481
485
 
@@ -712,6 +716,53 @@ function updateCodeSession(
712
716
  });
713
717
  }
714
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
+
715
766
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
716
767
  export class ToolNode<T = any> extends RunnableCallable<T, T> {
717
768
  private toolMap: Map<string, StructuredToolInterface | RunnableToolLike>;
@@ -787,6 +838,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
787
838
  * even where `agentId` (the subagent-scope marker) is undefined.
788
839
  */
789
840
  private executingAgentId?: string;
841
+ private readonly executionContext?: t.ToolNodeOptions['executionContext'];
790
842
  private executingAgentName?: string;
791
843
  private rootAgentId?: string;
792
844
  private rootAgentName?: string;
@@ -876,6 +928,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
876
928
  eagerEventToolSuppressions,
877
929
  agentId,
878
930
  executingAgentId,
931
+ executionContext,
879
932
  executingAgentName,
880
933
  rootAgentId,
881
934
  rootAgentName,
@@ -900,65 +953,124 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
900
953
  name: name ?? TOOL_NODE_RUN_NAME,
901
954
  tags,
902
955
  func: async (input, config) => {
903
- const messages = Array.isArray(input) ? input as BaseMessage[] :
904
- (input as { messages?: BaseMessage[] }).messages;
905
- const assistantBatch = messages == null ? undefined : findAssistantBatch(messages);
906
- const activeReplayKey = assistantBatch == null || getToolBatchReplayScope(config) == null ? undefined : getAssistantBatchReplayKey(
907
- assistantBatch, getToolBatchReplayScope(config)
908
- );
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
+ );
909
968
  const replayOwner = getToolBatchReplayOwner(
910
969
  config,
911
970
  this.executingAgentId ?? this.agentId ?? ''
912
971
  );
913
972
  let reviewEvidence = getToolApprovalReviewEvidence(config);
914
973
  const replayState = getToolBatchReplayState(config.configurable);
915
- const ownsParentBatch = replayState?.records.some(
916
- (record) => record.owner === replayOwner && record.batch === activeReplayKey
917
- ) === true;
974
+ const ownsParentBatch =
975
+ replayState?.records.some(
976
+ (record) =>
977
+ record.owner === replayOwner && record.batch === activeReplayKey
978
+ ) === true;
918
979
  let isChildReplay = false;
919
980
  if (ownsParentBatch) {
920
- const calls = this.isSendInput(input) ? [input.lg_tool_call] : assistantBatch?.message.tool_calls ?? [];
921
- const trustedParentCallIds = new Set(calls.filter((call) =>
922
- call.name === Constants.SUBAGENT &&
923
- (this.toolMap.get(call.name) as ReplayableSubagentTool | undefined)?.[SUBAGENT_REPLAY_CONTROLLER] != null
924
- ).map((call) => call.id ?? ''));
925
- isChildReplay = reviewEvidence?.owner != null
926
- ? isChildToolReplayOwner(config, reviewEvidence.owner, trustedParentCallIds)
927
- : 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
+ );
928
1011
  }
929
1012
  const hasApprovalAuthorization =
930
1013
  reviewEvidence?.owner != null || replayState?.approvalOwner != null;
931
- const replayResumeStatus = hasApprovalAuthorization || replayState != null
932
- ? getToolReplayResumeStatus(
933
- config,
934
- reviewEvidence?.interruptId ?? replayState?.interruptId,
935
- isChildReplay
936
- )
937
- : undefined;
1014
+ const replayResumeStatus =
1015
+ hasApprovalAuthorization || replayState != null
1016
+ ? getToolReplayResumeStatus(
1017
+ config,
1018
+ reviewEvidence?.interruptId ?? replayState?.interruptId,
1019
+ isChildReplay
1020
+ )
1021
+ : undefined;
938
1022
  if (replayResumeStatus === 'unverifiable' && hasApprovalAuthorization) {
939
- throw new Error('Cannot verify the active tool approval resume — failing closed');
1023
+ throw new Error(
1024
+ 'Cannot verify the active tool approval resume — failing closed'
1025
+ );
940
1026
  }
941
1027
  if (replayResumeStatus === 'stale') {
942
1028
  const configurable = { ...config.configurable };
943
- clearStaleToolApprovalConfig(configurable, replayOwner, activeReplayKey);
1029
+ clearStaleToolApprovalConfig(
1030
+ configurable,
1031
+ replayOwner,
1032
+ activeReplayKey
1033
+ );
944
1034
  config = {
945
1035
  ...config,
946
1036
  configurable,
947
1037
  };
948
1038
  reviewEvidence = undefined;
949
1039
  }
950
- if (reviewEvidence?.owner != null && reviewEvidence.owner !== replayOwner && !isChildReplay) {
951
- 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
+ );
952
1048
  }
953
1049
  const referenceReplay: { state?: ToolOutputReferenceState } = {};
954
- const restoredBatches = await restoreToolBatchReplayState(config, replayOwner);
955
- if (restoredBatches.length > 0 && !restoredBatches.some(({ batch }) => batch === activeReplayKey)) {
956
- 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
+ );
957
1061
  }
958
- if (activeReplayKey != null && getToolBatchReplayState(config.configurable) != null) {
1062
+ if (
1063
+ activeReplayKey != null &&
1064
+ getToolBatchReplayState(config.configurable) != null
1065
+ ) {
959
1066
  this.settledDirectResultsByBatch.delete(activeReplayKey);
960
1067
  }
961
- for (const { batch, results, turnState, referenceState } of restoredBatches) {
1068
+ for (const {
1069
+ batch,
1070
+ results,
1071
+ turnState,
1072
+ referenceState,
1073
+ } of restoredBatches) {
962
1074
  if (batch !== activeReplayKey) {
963
1075
  continue;
964
1076
  }
@@ -978,27 +1090,45 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
978
1090
  throw error;
979
1091
  }
980
1092
  const resumeState = createRunStepResumeState?.();
981
- const activeResults = activeReplayKey == null ? undefined : this.settledDirectResultsByBatch.get(activeReplayKey);
982
- const activeBatches = activeReplayKey == null
983
- ? new Map<string, Map<string, SettledDirectToolResult>>()
984
- : 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
+ ]);
985
1106
  const turnState = this.createSubagentResumeState();
986
- const activeCallIds = new Set(assistantBatch?.message.tool_calls?.map((call) => call.id));
987
- 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
+ );
988
1113
  throw new GraphInterrupt(
989
- await Promise.all(error.interrupts.map(async (pendingInterrupt) => {
990
- const value = await attachToolBatchReplayState(
991
- pendingInterrupt.value,
992
- replayOwner,
993
- activeBatches,
994
- turnState,
995
- referenceReplay.state
996
- );
997
- return {
998
- ...pendingInterrupt,
999
- value: resumeState == null ? value : attachRunStepResumeState(value, resumeState),
1000
- };
1001
- }))
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
+ )
1002
1132
  );
1003
1133
  }
1004
1134
  if (createRunStepResumeState == null) {
@@ -1040,6 +1170,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
1040
1170
  // Default to agentId so callers constructing ToolNode directly (who pass the
1041
1171
  // existing agentId option) still get attribution without knowing the new option.
1042
1172
  this.executingAgentId = executingAgentId ?? agentId;
1173
+ this.executionContext = executionContext;
1043
1174
  this.executingAgentName = executingAgentName;
1044
1175
  this.rootAgentId = rootAgentId;
1045
1176
  this.rootAgentName = rootAgentName;
@@ -1109,8 +1240,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
1109
1240
  ? options
1110
1241
  : {
1111
1242
  ...options,
1243
+ configurable: {
1244
+ ...options?.configurable,
1245
+ executionContext: this.executionContext,
1246
+ },
1112
1247
  metadata: {
1113
1248
  ...options?.metadata,
1249
+ executionContext: this.executionContext,
1114
1250
  agentId: this.executingAgentId,
1115
1251
  activeAgentId: this.executingAgentId,
1116
1252
  ...(this.executingAgentName == null
@@ -1668,7 +1804,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
1668
1804
  return next;
1669
1805
  })();
1670
1806
  this.recordToolUsageTurn(call.name, usageCount, call.id);
1671
- if (batchContext.replayBatchKey != null && call.id != null && call.id !== '') {
1807
+ if (
1808
+ batchContext.replayBatchKey != null &&
1809
+ call.id != null &&
1810
+ call.id !== ''
1811
+ ) {
1672
1812
  this.directPathTurns.set(call.id, usageCount);
1673
1813
  }
1674
1814
  let args = call.args;
@@ -1732,6 +1872,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
1732
1872
  runId: (config.configurable?.run_id as string | undefined) ?? '',
1733
1873
  threadId: config.configurable?.thread_id as string | undefined,
1734
1874
  agentId: this.agentId,
1875
+ executionContext: this.executionContext,
1735
1876
  executingAgentId: this.executingAgentId,
1736
1877
  },
1737
1878
  };
@@ -2167,8 +2308,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2167
2308
  approvalReplaySessionId,
2168
2309
  approvalReplayKey
2169
2310
  );
2170
- const approvalReviewEvidence = getToolApprovalReviewEvidence(config,
2171
- getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? ''));
2311
+ const approvalReviewEvidence = getToolApprovalReviewEvidence(
2312
+ config,
2313
+ getToolBatchReplayOwner(
2314
+ config,
2315
+ this.executingAgentId ?? this.agentId ?? ''
2316
+ )
2317
+ );
2172
2318
  const reviewedApproval = getReviewedToolApproval(
2173
2319
  approvalReviewEvidence?.payload,
2174
2320
  call.id
@@ -2294,6 +2440,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2294
2440
  runId,
2295
2441
  threadId,
2296
2442
  agentId: this.agentId,
2443
+ executionContext: this.executionContext,
2297
2444
  executingAgentId: this.executingAgentId,
2298
2445
  toolName: call.name,
2299
2446
  toolInput: resolvedArgs,
@@ -2305,11 +2452,17 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2305
2452
  matchQuery: call.name,
2306
2453
  onceReplayKey: approvalReplayKey,
2307
2454
  onceReplaySessionId: approvalReplaySessionId,
2308
- }).catch((): AggregatedHookResult | undefined => approvalReviewEvidence == null ? undefined : {
2309
- decision: 'deny',
2310
- reason: 'Approval policy could not be evaluated on resume',
2311
- additionalContexts: [], injectedMessages: [], errors: [],
2312
- });
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
+ );
2313
2466
 
2314
2467
  if (preResult != null) {
2315
2468
  // Forward any additionalContext strings hooks returned into
@@ -2651,6 +2804,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2651
2804
  runId,
2652
2805
  threadId,
2653
2806
  agentId: this.agentId,
2807
+ executionContext: this.executionContext,
2654
2808
  executingAgentId: this.executingAgentId,
2655
2809
  toolName: call.name,
2656
2810
  toolInput: effectiveCall.args as Record<string, unknown>,
@@ -2688,6 +2842,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2688
2842
  runId,
2689
2843
  threadId,
2690
2844
  agentId: this.agentId,
2845
+ executionContext: this.executionContext,
2691
2846
  executingAgentId: this.executingAgentId,
2692
2847
  toolName: call.name,
2693
2848
  toolInput: effectiveCall.args as Record<string, unknown>,
@@ -2804,6 +2959,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2804
2959
  runId,
2805
2960
  threadId,
2806
2961
  agentId: this.agentId,
2962
+ executionContext: this.executionContext,
2807
2963
  executingAgentId: this.executingAgentId,
2808
2964
  toolName: call.name,
2809
2965
  toolInput: resolvedArgs,
@@ -2935,18 +3091,22 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2935
3091
 
2936
3092
  private storeCodeSessionFromResults(
2937
3093
  results: t.ToolExecuteResult[],
2938
- requestMap: Map<string, t.ToolCallRequest>
3094
+ requestMap: Map<string, t.ToolCallRequest>,
3095
+ baselineByRequestId: ReadonlyMap<
3096
+ string,
3097
+ ReadonlyMap<string, string>
3098
+ >
2939
3099
  ): void {
2940
3100
  if (!this.sessions) {
2941
3101
  return;
2942
3102
  }
2943
3103
 
3104
+ this.retainCodeSessionInputsFromRequests(
3105
+ requestMap.values(),
3106
+ baselineByRequestId
3107
+ );
2944
3108
  for (let i = 0; i < results.length; i++) {
2945
3109
  const result = results[i];
2946
- if (result.status !== 'success' || result.artifact == null) {
2947
- continue;
2948
- }
2949
-
2950
3110
  const request = requestMap.get(result.toolCallId);
2951
3111
  if (
2952
3112
  request?.name == null ||
@@ -2957,6 +3117,10 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2957
3117
  continue;
2958
3118
  }
2959
3119
 
3120
+ if (result.status !== 'success' || result.artifact == null) {
3121
+ continue;
3122
+ }
3123
+
2960
3124
  const artifact = result.artifact as t.CodeExecutionArtifact | undefined;
2961
3125
  const execSessionId = artifact?.session_id;
2962
3126
  if (execSessionId == null || execSessionId === '') {
@@ -2972,6 +3136,52 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2972
3136
  }
2973
3137
  }
2974
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
+
2975
3185
  /**
2976
3186
  * Post-processes standard runTool outputs: dispatches ON_RUN_STEP_COMPLETED
2977
3187
  * and stores code session context. Mirrors the completion handling in
@@ -3178,10 +3388,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3178
3388
  unresolvedByCallId.set(call.id, pre.unresolved);
3179
3389
  }
3180
3390
  } else if (registry != null) {
3181
- const options = { substituteIntentKey: this.toolDeclaresBusinessIntent(call.name) };
3182
- const { resolved, unresolved } = preBatchSnapshot != null
3183
- ? preBatchSnapshot.resolve(originalArgs, options)
3184
- : 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);
3185
3398
  resolvedArgs = resolved as Record<string, unknown>;
3186
3399
  if (unresolved.length > 0 && call.id != null) {
3187
3400
  unresolvedByCallId.set(call.id, unresolved);
@@ -3229,8 +3442,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3229
3442
  config,
3230
3443
  runId
3231
3444
  );
3232
- const approvalReviewEvidence = getToolApprovalReviewEvidence(config,
3233
- getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? ''));
3445
+ const approvalReviewEvidence = getToolApprovalReviewEvidence(
3446
+ config,
3447
+ getToolBatchReplayOwner(
3448
+ config,
3449
+ this.executingAgentId ?? this.agentId ?? ''
3450
+ )
3451
+ );
3234
3452
  const hasPendingApproval = preToolCalls.some(
3235
3453
  (entry) =>
3236
3454
  entry.call.id != null &&
@@ -3295,6 +3513,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3295
3513
  runId,
3296
3514
  threadId,
3297
3515
  agentId: this.agentId,
3516
+ executionContext: this.executionContext,
3298
3517
  executingAgentId: this.executingAgentId,
3299
3518
  toolName: entry.call.name,
3300
3519
  toolInput: entry.args,
@@ -3397,6 +3616,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3397
3616
  runId,
3398
3617
  threadId,
3399
3618
  agentId: this.agentId,
3619
+ executionContext: this.executionContext,
3400
3620
  executingAgentId: this.executingAgentId,
3401
3621
  toolName: item.toolName,
3402
3622
  toolInput: item.args,
@@ -3504,9 +3724,15 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3504
3724
  if (hookResult.updatedInput != null) {
3505
3725
  applyInputOverride(entry, hookResult.updatedInput);
3506
3726
  } else {
3507
- const reviewed = getReviewedToolApproval(approvalReviewEvidence?.payload, entry.call.id);
3727
+ const reviewed = getReviewedToolApproval(
3728
+ approvalReviewEvidence?.payload,
3729
+ entry.call.id
3730
+ );
3508
3731
  if (reviewed != null) {
3509
- entry.args = this.coerceEventToolArgs(entry.call.name, reviewed.request.arguments);
3732
+ entry.args = this.coerceEventToolArgs(
3733
+ entry.call.name,
3734
+ reviewed.request.arguments
3735
+ );
3510
3736
  if (entry.call.id != null) {
3511
3737
  unresolvedByCallId.delete(entry.call.id);
3512
3738
  }
@@ -3877,6 +4103,23 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3877
4103
  }
3878
4104
 
3879
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
+ );
3880
4123
  const eagerExecutions: Array<{
3881
4124
  request: t.ToolCallRequest;
3882
4125
  execution: t.EagerEventToolExecution;
@@ -3887,6 +4130,14 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3887
4130
  const eagerExecution = this.takeMatchingEagerEventExecution(request);
3888
4131
  if (eagerExecution != null) {
3889
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;
3890
4141
  } else {
3891
4142
  dispatchRequests.push(request);
3892
4143
  }
@@ -3972,6 +4223,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3972
4223
  };
3973
4224
 
3974
4225
  const batchRequest: t.ToolExecuteBatchRequest = {
4226
+ executionContext: this.executionContext,
3975
4227
  toolCalls: dispatchRequests,
3976
4228
  userId: config.configurable?.user_id as string | undefined,
3977
4229
  // Dispatch attribution, NOT the hook subagent-scope marker:
@@ -4011,24 +4263,46 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4011
4263
 
4012
4264
  const eagerResultsPromise = Promise.all(
4013
4265
  eagerExecutions.map(async ({ request, execution }) => {
4014
- const results = await this.resolveEagerEventExecution(
4015
- request,
4016
- execution
4017
- );
4018
- return {
4019
- results,
4020
- completionDispatched:
4021
- execution.completionDispatched === true &&
4022
- execution.request.turn === request.turn,
4023
- toolCallId: request.id,
4024
- };
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
+ }
4025
4283
  })
4026
4284
  );
4027
4285
 
4028
- const [eagerResults, dispatchedResults] = await Promise.all([
4286
+ const [eagerOutcome, dispatchedOutcome] = await Promise.allSettled([
4029
4287
  eagerResultsPromise,
4030
4288
  dispatchPromise,
4031
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;
4032
4306
  // Settle in-flight early completion dispatches before the batch loop
4033
4307
  // below decides which completions still need emitting.
4034
4308
  await Promise.allSettled(earlyCompletionDispatches);
@@ -4046,7 +4320,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4046
4320
  ...dispatchedResults,
4047
4321
  ];
4048
4322
 
4049
- this.storeCodeSessionFromResults(results, requestMap);
4323
+ this.storeCodeSessionFromResults(
4324
+ results,
4325
+ requestMap,
4326
+ codeSessionBaselineByRequestId
4327
+ );
4050
4328
 
4051
4329
  for (const result of results) {
4052
4330
  if (result.injectedMessages && result.injectedMessages.length > 0) {
@@ -4115,6 +4393,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4115
4393
  runId,
4116
4394
  threadId,
4117
4395
  agentId: this.agentId,
4396
+ executionContext: this.executionContext,
4118
4397
  executingAgentId: this.executingAgentId,
4119
4398
  toolName,
4120
4399
  toolInput: request?.args ?? {},
@@ -4167,6 +4446,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4167
4446
  runId,
4168
4447
  threadId,
4169
4448
  agentId: this.agentId,
4449
+ executionContext: this.executionContext,
4170
4450
  executingAgentId: this.executingAgentId,
4171
4451
  toolName,
4172
4452
  toolInput: request?.args ?? {},
@@ -4326,28 +4606,40 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4326
4606
  // effects) every round.
4327
4607
  this.eagerEventToolSuppressions?.add(request.name);
4328
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
+ }
4329
4616
  // eslint-disable-next-line no-console
4330
4617
  console.warn(
4331
4618
  '[ToolNode] eager prestart args diverged from the final request for ' +
4332
4619
  `tool "${request.name}" (toolCallId=${request.id}); suppressing ` +
4333
4620
  'eager prestart for this tool for the rest of the run'
4334
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
+ });
4335
4633
  return {
4336
4634
  toolCallId: request.id,
4337
4635
  toolName: request.name,
4338
4636
  args: request.args,
4339
- request,
4340
- promise: Promise.resolve({
4341
- results: [
4342
- {
4343
- toolCallId: request.id,
4344
- status: 'error',
4345
- content: '',
4346
- errorMessage:
4347
- 'Tool call changed after eager execution started; refusing to re-run the tool to avoid duplicate side effects.',
4348
- },
4349
- ],
4350
- }),
4637
+ request: execution.request,
4638
+ codeSessionBaselineByName: execution.codeSessionBaselineByName,
4639
+ promise: execution.promise.then(
4640
+ createMismatchOutcome,
4641
+ createMismatchOutcome
4642
+ ),
4351
4643
  };
4352
4644
  }
4353
4645
 
@@ -4439,6 +4731,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4439
4731
  runId,
4440
4732
  threadId,
4441
4733
  agentId: this.agentId,
4734
+ executionContext: this.executionContext,
4442
4735
  executingAgentId: this.executingAgentId,
4443
4736
  entries: orderedBatchEntries,
4444
4737
  },
@@ -4647,13 +4940,16 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4647
4940
  position: number,
4648
4941
  result: SettledDirectToolResult
4649
4942
  ): void => {
4650
- if (
4651
- baseContext.replayBatchKey == null ||
4652
- settledBatchResults == null
4653
- ) {
4943
+ if (baseContext.replayBatchKey == null || settledBatchResults == null) {
4654
4944
  return;
4655
4945
  }
4656
- 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
+ );
4657
4953
  this.settledDirectResultsByBatch.set(
4658
4954
  baseContext.replayBatchKey,
4659
4955
  settledBatchResults
@@ -4668,14 +4964,26 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4668
4964
  if (turn != null) {
4669
4965
  this.recordToolUsageTurn(call.name, turn, call.id);
4670
4966
  }
4671
- 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;
4672
4970
  if (refMeta?._refKey != null && result.referenceContent != null) {
4673
- this.toolOutputRegistry?.set(baseContext.batchScopeId, refMeta._refKey, result.referenceContent);
4971
+ this.toolOutputRegistry?.set(
4972
+ baseContext.batchScopeId,
4973
+ refMeta._refKey,
4974
+ result.referenceContent
4975
+ );
4674
4976
  if (isBaseMessage(result.output)) {
4675
- result = { ...result, output: new ToolMessage({
4676
- ...(result.output as ToolMessage),
4677
- additional_kwargs: { ...result.output.additional_kwargs, _refScope: baseContext.batchScopeId },
4678
- }) };
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
+ };
4679
4987
  }
4680
4988
  }
4681
4989
  if (
@@ -4691,11 +4999,23 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4691
4999
  call: ToolCall,
4692
5000
  position: number
4693
5001
  ): Promise<SettledDirectToolResult> => {
4694
- 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
+ );
4695
5008
  if (cachedResult != null) {
4696
- if (cachedResult.proposal.name !== call.name ||
4697
- !recordArgsEqual(cachedResult.proposal.args, call.args as Record<string, unknown>)) {
4698
- 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
+ );
4699
5019
  }
4700
5020
  return restoreResult(call, cachedResult);
4701
5021
  }
@@ -4710,15 +5030,27 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4710
5030
  ? undefined
4711
5031
  : baseContext.resolvedArgsByCallId?.get(call.id);
4712
5032
  const result: SettledDirectToolResult = {
4713
- proposal: structuredClone({ name: call.name, args: call.args as Record<string, unknown> }),
4714
- 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)),
4715
5042
  output,
4716
5043
  additionalContexts,
4717
5044
  ...(resolvedArgs == null ? {} : { resolvedArgs }),
4718
5045
  };
4719
- 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;
4720
5049
  if (refMeta?._refKey != null) {
4721
- result.referenceContent = this.toolOutputRegistry?.get(refMeta._refScope, refMeta._refKey);
5050
+ result.referenceContent = this.toolOutputRegistry?.get(
5051
+ refMeta._refScope,
5052
+ refMeta._refKey
5053
+ );
4722
5054
  }
4723
5055
  cacheResult(call, position, result);
4724
5056
  return restoreResult(call, result);
@@ -4734,8 +5066,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4734
5066
  directCalls.map((call, i) => runOne(call, i))
4735
5067
  );
4736
5068
  this.throwIfBreakerTripped(config);
4737
- const failed = results.find((result) => result.status === 'rejected' && !isGraphInterrupt(result.reason)) ??
4738
- 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');
4739
5074
  if (failed?.status === 'rejected') {
4740
5075
  throw failed.reason;
4741
5076
  }
@@ -4877,8 +5212,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4877
5212
  return (Array.isArray(input) ? outputs : { messages: outputs }) as T;
4878
5213
  }
4879
5214
 
4880
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
4881
- 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> {
4882
5220
  /**
4883
5221
  * Breaker read once at batch entry: every downstream path (direct
4884
5222
  * `tool.invoke` runtimes and the ON_TOOL_EXECUTE dispatch) inherits this
@@ -4938,10 +5276,20 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4938
5276
  const incomingRunId = config.configurable?.run_id as string | undefined;
4939
5277
  const batchScopeId = incomingRunId ?? `\0anon-${this.anonBatchCounter++}`;
4940
5278
  const resumedReferenceState = referenceReplay.state;
4941
- const replayInputSnapshot = resumedReferenceState == null ? undefined :
4942
- this.toolOutputRegistry?.resumeBatch(batchScopeId, resumedReferenceState);
4943
- referenceReplay.state = resumedReferenceState ?? this.toolOutputRegistry?.snapshotState(batchScopeId);
4944
- 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;
4945
5293
  let outputs: (BaseMessage | Command)[];
4946
5294
  let replayBatchKey: string | undefined;
4947
5295
  /** Hoisted from the messages-state branch so the Command tail can carry
@@ -5045,10 +5393,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5045
5393
  throw new Error('ToolNode only accepts AIMessages as input.');
5046
5394
  }
5047
5395
  const aiMessage = assistantBatch.message;
5048
- replayBatchKey = getToolBatchReplayScope(config) == null ? undefined : getAssistantBatchReplayKey(
5049
- assistantBatch,
5050
- getToolBatchReplayScope(config)
5051
- );
5396
+ replayBatchKey =
5397
+ getToolBatchReplayScope(config) == null
5398
+ ? undefined
5399
+ : getAssistantBatchReplayKey(
5400
+ assistantBatch,
5401
+ getToolBatchReplayScope(config)
5402
+ );
5052
5403
 
5053
5404
  if (this.loadRuntimeTools) {
5054
5405
  const { tools, toolMap } = this.loadRuntimeTools(
@@ -5059,7 +5410,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5059
5410
  this.applyToolExecutionOverrides();
5060
5411
  }
5061
5412
 
5062
- 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
+ );
5063
5416
  const filteredCalls =
5064
5417
  aiMessage.tool_calls?.filter((call) => {
5065
5418
  /**
@@ -5069,7 +5422,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5069
5422
  * which are executed by the provider's API and don't require invocation
5070
5423
  */
5071
5424
  return (
5072
- (call.id == null || call.id === '' || !toolMessageIds.has(call.id)) &&
5425
+ (call.id == null ||
5426
+ call.id === '' ||
5427
+ !toolMessageIds.has(call.id)) &&
5073
5428
  !(
5074
5429
  call.id?.startsWith(Constants.ANTHROPIC_SERVER_TOOL_PREFIX) ??
5075
5430
  false
@@ -5234,7 +5589,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5234
5589
  * same-turn direct outputs.
5235
5590
  */
5236
5591
  const preBatchSnapshot =
5237
- replayInputSnapshot ?? this.toolOutputRegistry?.snapshot(batchScopeId);
5592
+ replayInputSnapshot ??
5593
+ this.toolOutputRegistry?.snapshot(batchScopeId);
5238
5594
  if (preBatchSnapshot != null) {
5239
5595
  for (const entry of eventEntries) {
5240
5596
  if (entry.call.id != null) {
@@ -5281,11 +5637,18 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5281
5637
  : [];
5282
5638
 
5283
5639
  const settledDirectResults =
5284
- replayBatchKey == null ? undefined : this.settledDirectResultsByBatch.get(replayBatchKey);
5640
+ replayBatchKey == null
5641
+ ? undefined
5642
+ : this.settledDirectResultsByBatch.get(replayBatchKey);
5285
5643
  const unhandledDirectCalls: ToolCall[] = [];
5286
5644
  const unhandledDirectOutputs: (BaseMessage | Command)[] = [];
5287
5645
  for (let i = 0; i < directCalls.length; i++) {
5288
- 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
+ );
5289
5652
  if (settled?.completionHandled === true) {
5290
5653
  continue;
5291
5654
  }
@@ -5302,7 +5665,12 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5302
5665
  errorOwnership
5303
5666
  );
5304
5667
  for (let i = 0; i < directCalls.length; i++) {
5305
- 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
+ );
5306
5674
  if (settled != null) {
5307
5675
  settled.completionHandled = true;
5308
5676
  }
@@ -5355,7 +5723,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5355
5723
  // leak a sibling's just-registered output into a sister
5356
5724
  // call's args mid-await (Codex P1 #18).
5357
5725
  const preBatchSnapshot =
5358
- replayInputSnapshot ?? this.toolOutputRegistry?.snapshot(batchScopeId);
5726
+ replayInputSnapshot ??
5727
+ this.toolOutputRegistry?.snapshot(batchScopeId);
5359
5728
  const directAdditionalContexts: string[] = [];
5360
5729
  const approvalScopeCallIds = new Set(
5361
5730
  filteredCalls.flatMap((call) => (call.id == null ? [] : [call.id]))