@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.
- package/dist/cjs/agents/AgentContext.cjs +1 -1
- package/dist/cjs/common/constants.cjs +2 -0
- package/dist/cjs/common/constants.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +18 -9
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/hitl/approvalReview.cjs +1 -1
- package/dist/cjs/hooks/types.cjs.map +1 -1
- package/dist/cjs/main.cjs +7 -6
- package/dist/cjs/messages/index.cjs +1 -1
- package/dist/cjs/messages/prune.cjs +1 -1
- package/dist/cjs/run.cjs +7 -3
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +13 -3
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +3 -1
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +134 -32
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +1 -0
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs +0 -1
- package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +123 -29
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/toolBatchReplay.cjs +65 -13
- package/dist/cjs/tools/toolBatchReplay.cjs.map +1 -1
- package/dist/cjs/utils/index.cjs +1 -1
- package/dist/esm/agents/AgentContext.mjs +1 -1
- package/dist/esm/common/constants.mjs +2 -1
- package/dist/esm/common/constants.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +17 -8
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/hitl/approvalReview.mjs +1 -1
- package/dist/esm/hooks/types.mjs.map +1 -1
- package/dist/esm/main.mjs +6 -6
- package/dist/esm/messages/index.mjs +1 -1
- package/dist/esm/messages/prune.mjs +1 -1
- package/dist/esm/run.mjs +7 -3
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +13 -3
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +3 -1
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +135 -33
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +1 -0
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs +0 -1
- package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +123 -29
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/toolBatchReplay.mjs +64 -13
- package/dist/esm/tools/toolBatchReplay.mjs.map +1 -1
- package/dist/esm/utils/index.mjs +1 -1
- package/dist/types/common/constants.d.ts +2 -0
- package/dist/types/graphs/Graph.d.ts +3 -2
- package/dist/types/hooks/types.d.ts +3 -0
- package/dist/types/run.d.ts +1 -0
- package/dist/types/summarization/node.d.ts +1 -0
- package/dist/types/tools/ToolNode.d.ts +4 -2
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +7 -4
- package/dist/types/tools/toolBatchReplay.d.ts +6 -3
- package/dist/types/types/graph.d.ts +28 -0
- package/dist/types/types/run.d.ts +2 -0
- package/dist/types/types/tools.d.ts +11 -1
- package/package.json +1 -1
- package/src/common/constants.ts +3 -0
- package/src/graphs/Graph.ts +32 -18
- package/src/hooks/types.ts +3 -0
- package/src/run.ts +30 -15
- package/src/stream.ts +27 -5
- package/src/summarization/node.ts +3 -0
- package/src/tools/ToolNode.ts +528 -149
- package/src/tools/local/LocalProgrammaticToolCalling.ts +1 -0
- package/src/tools/subagent/SubagentExecutionRegistry.ts +0 -1
- package/src/tools/subagent/SubagentExecutor.ts +216 -48
- package/src/tools/toolBatchReplay.ts +121 -20
- package/src/types/graph.ts +37 -0
- package/src/types/run.ts +2 -0
- package/src/types/tools.ts +11 -1
- package/src/utils/llmConfig.ts +0 -1
package/src/tools/ToolNode.ts
CHANGED
|
@@ -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
|
-
|
|
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 === ''
|
|
477
|
-
|
|
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)
|
|
903
|
-
(input as
|
|
904
|
-
|
|
905
|
-
const
|
|
906
|
-
|
|
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 =
|
|
915
|
-
(
|
|
916
|
-
|
|
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)
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
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 (
|
|
929
|
-
|
|
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 (
|
|
941
|
-
|
|
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(
|
|
945
|
-
|
|
946
|
-
|
|
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 (
|
|
1062
|
+
if (
|
|
1063
|
+
activeReplayKey != null &&
|
|
1064
|
+
getToolBatchReplayState(config.configurable) != null
|
|
1065
|
+
) {
|
|
949
1066
|
this.settledDirectResultsByBatch.delete(activeReplayKey);
|
|
950
1067
|
}
|
|
951
|
-
for (const {
|
|
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 =
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
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(
|
|
977
|
-
|
|
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(
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
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 (
|
|
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(
|
|
2161
|
-
|
|
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 =>
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
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 = {
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
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(
|
|
3223
|
-
|
|
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(
|
|
3727
|
+
const reviewed = getReviewedToolApproval(
|
|
3728
|
+
approvalReviewEvidence?.payload,
|
|
3729
|
+
entry.call.id
|
|
3730
|
+
);
|
|
3498
3731
|
if (reviewed != null) {
|
|
3499
|
-
entry.args = this.coerceEventToolArgs(
|
|
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
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
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 [
|
|
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(
|
|
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
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
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(
|
|
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)
|
|
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(
|
|
4971
|
+
this.toolOutputRegistry?.set(
|
|
4972
|
+
baseContext.batchScopeId,
|
|
4973
|
+
refMeta._refKey,
|
|
4974
|
+
result.referenceContent
|
|
4975
|
+
);
|
|
4664
4976
|
if (isBaseMessage(result.output)) {
|
|
4665
|
-
result = {
|
|
4666
|
-
...
|
|
4667
|
-
|
|
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(
|
|
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 (
|
|
4687
|
-
|
|
4688
|
-
|
|
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({
|
|
4704
|
-
|
|
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)
|
|
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(
|
|
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 =
|
|
4728
|
-
results.find(
|
|
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
|
-
|
|
4871
|
-
|
|
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 =
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
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 =
|
|
5039
|
-
|
|
5040
|
-
|
|
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(
|
|
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 ||
|
|
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 ??
|
|
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
|
|
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(
|
|
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(
|
|
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 ??
|
|
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]))
|