@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.
- 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 +127 -26
- 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 +120 -26
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/toolBatchReplay.cjs +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 +127 -26
- 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 +120 -26
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/toolBatchReplay.mjs +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/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 +521 -152
- package/src/tools/local/LocalProgrammaticToolCalling.ts +1 -0
- package/src/tools/subagent/SubagentExecutionRegistry.ts +0 -1
- package/src/tools/subagent/SubagentExecutor.ts +214 -55
- 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,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
|
-
|
|
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 === ''
|
|
478
|
-
|
|
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)
|
|
904
|
-
(input as
|
|
905
|
-
|
|
906
|
-
const
|
|
907
|
-
|
|
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 =
|
|
916
|
-
(
|
|
917
|
-
|
|
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)
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
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 =
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
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(
|
|
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(
|
|
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 (
|
|
951
|
-
|
|
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(
|
|
955
|
-
|
|
956
|
-
|
|
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 (
|
|
1062
|
+
if (
|
|
1063
|
+
activeReplayKey != null &&
|
|
1064
|
+
getToolBatchReplayState(config.configurable) != null
|
|
1065
|
+
) {
|
|
959
1066
|
this.settledDirectResultsByBatch.delete(activeReplayKey);
|
|
960
1067
|
}
|
|
961
|
-
for (const {
|
|
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 =
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
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(
|
|
987
|
-
|
|
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(
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
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 (
|
|
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(
|
|
2171
|
-
|
|
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 =>
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
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 = {
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
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(
|
|
3233
|
-
|
|
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(
|
|
3727
|
+
const reviewed = getReviewedToolApproval(
|
|
3728
|
+
approvalReviewEvidence?.payload,
|
|
3729
|
+
entry.call.id
|
|
3730
|
+
);
|
|
3508
3731
|
if (reviewed != null) {
|
|
3509
|
-
entry.args = this.coerceEventToolArgs(
|
|
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
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
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 [
|
|
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(
|
|
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
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
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(
|
|
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)
|
|
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(
|
|
4971
|
+
this.toolOutputRegistry?.set(
|
|
4972
|
+
baseContext.batchScopeId,
|
|
4973
|
+
refMeta._refKey,
|
|
4974
|
+
result.referenceContent
|
|
4975
|
+
);
|
|
4674
4976
|
if (isBaseMessage(result.output)) {
|
|
4675
|
-
result = {
|
|
4676
|
-
...
|
|
4677
|
-
|
|
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(
|
|
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 (
|
|
4697
|
-
|
|
4698
|
-
|
|
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({
|
|
4714
|
-
|
|
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)
|
|
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(
|
|
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 =
|
|
4738
|
-
results.find(
|
|
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
|
-
|
|
4881
|
-
|
|
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 =
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
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 =
|
|
5049
|
-
|
|
5050
|
-
|
|
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(
|
|
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 ||
|
|
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 ??
|
|
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
|
|
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(
|
|
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(
|
|
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 ??
|
|
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]))
|