@librechat/agents 3.8.5 → 3.8.7
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 +8 -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/BashExecutor.cjs +79 -27
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +47 -7
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +5 -2
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/CodeSessionFileSummary.cjs +11 -1
- package/dist/cjs/tools/CodeSessionFileSummary.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +54 -8
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +176 -43
- 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 +4 -2
- 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 +7 -7
- 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/BashExecutor.mjs +81 -29
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +48 -9
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +5 -2
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/CodeSessionFileSummary.mjs +11 -2
- package/dist/esm/tools/CodeSessionFileSummary.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +55 -9
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +176 -43
- 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 +4 -2
- 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/BashExecutor.d.ts +2 -0
- package/dist/types/tools/BashProgrammaticToolCalling.d.ts +1 -0
- package/dist/types/tools/CodeExecutor.d.ts +1 -1
- package/dist/types/tools/CodeSessionFileSummary.d.ts +3 -0
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +2 -2
- package/dist/types/tools/ToolNode.d.ts +6 -2
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +7 -4
- package/dist/types/tools/toolBatchReplay.d.ts +2 -0
- 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 +27 -3
- package/package.json +2 -2
- 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/BashExecutor.ts +143 -40
- package/src/tools/BashProgrammaticToolCalling.ts +82 -10
- package/src/tools/CodeExecutor.ts +12 -2
- package/src/tools/CodeSessionFileSummary.ts +25 -1
- package/src/tools/ProgrammaticToolCalling.ts +77 -6
- package/src/tools/ToolNode.ts +665 -170
- 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/tools/toolBatchReplay.ts +23 -1
- package/src/types/graph.ts +37 -0
- package/src/types/run.ts +2 -0
- package/src/types/tools.ts +30 -3
- 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
|
}
|
|
@@ -221,6 +221,8 @@ type RunToolBatchContext<T = unknown> = {
|
|
|
221
221
|
batchScopeId?: string;
|
|
222
222
|
/** Batch-local sink for post-substitution args. */
|
|
223
223
|
resolvedArgsByCallId?: ResolvedArgsByCallId;
|
|
224
|
+
/** File identities actually injected into each direct code call. */
|
|
225
|
+
codeSessionBaselineByCallId?: Map<string, ReadonlyMap<string, string>>;
|
|
224
226
|
/**
|
|
225
227
|
* Frozen pre-batch view of the tool-output registry. When supplied,
|
|
226
228
|
* `runTool` resolves `{{tool…turn…}}` placeholders against this
|
|
@@ -474,8 +476,12 @@ function getAssistantBatchReplayKey(
|
|
|
474
476
|
): string {
|
|
475
477
|
return JSON.stringify([
|
|
476
478
|
threadId ?? null,
|
|
477
|
-
batch.message.id == null || batch.message.id === ''
|
|
478
|
-
|
|
479
|
+
batch.message.id == null || batch.message.id === ''
|
|
480
|
+
? null
|
|
481
|
+
: batch.message.id,
|
|
482
|
+
createHash('sha256')
|
|
483
|
+
.update(stableStringify(batch.message.tool_calls ?? []))
|
|
484
|
+
.digest('hex'),
|
|
479
485
|
]);
|
|
480
486
|
}
|
|
481
487
|
|
|
@@ -647,29 +653,78 @@ function toInjectedFileRef(
|
|
|
647
653
|
|
|
648
654
|
/* Stable file identity = `(storage_session_id, id)`. Same name in
|
|
649
655
|
* different storage sessions are distinct files. */
|
|
650
|
-
function fileIdentityKey(
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
656
|
+
function fileIdentityKey(
|
|
657
|
+
file: {
|
|
658
|
+
storage_session_id?: string;
|
|
659
|
+
id: string;
|
|
660
|
+
},
|
|
661
|
+
fallbackSessionId = ''
|
|
662
|
+
): string {
|
|
663
|
+
return `${file.storage_session_id ?? fallbackSessionId}\0${file.id}`;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function codeSessionIdentityByName(
|
|
667
|
+
context:
|
|
668
|
+
| t.ToolCallRequest['codeSessionContext']
|
|
669
|
+
| t.CodeSessionContext
|
|
670
|
+
| undefined
|
|
671
|
+
): ReadonlyMap<string, string> {
|
|
672
|
+
return new Map(
|
|
673
|
+
(context?.files ?? []).map((file) => [
|
|
674
|
+
file.name,
|
|
675
|
+
fileIdentityKey(file, context?.session_id),
|
|
676
|
+
])
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function storageSessionIdFromIdentity(
|
|
681
|
+
identity: string | undefined,
|
|
682
|
+
fileId: string
|
|
683
|
+
): string | undefined {
|
|
684
|
+
const suffix = `\0${fileId}`;
|
|
685
|
+
return identity != null && identity.endsWith(suffix)
|
|
686
|
+
? identity.slice(0, identity.length - suffix.length)
|
|
687
|
+
: undefined;
|
|
655
688
|
}
|
|
656
689
|
|
|
657
690
|
function updateCodeSession(
|
|
658
691
|
sessions: t.ToolSessionMap,
|
|
659
692
|
sessionKey: string,
|
|
660
693
|
execSessionId: string,
|
|
661
|
-
files: t.FileRefs | undefined
|
|
694
|
+
files: t.FileRefs | undefined,
|
|
695
|
+
deletedFiles: readonly string[] | undefined,
|
|
696
|
+
baselineIdentityByName: ReadonlyMap<string, string> | undefined
|
|
662
697
|
): void {
|
|
663
698
|
const newFiles = files ?? [];
|
|
664
699
|
const existingSession = sessions.get(sessionKey) as
|
|
665
700
|
| t.CodeSessionContext
|
|
666
701
|
| undefined;
|
|
667
|
-
const
|
|
702
|
+
const existingSessionId = existingSession?.session_id ?? '';
|
|
703
|
+
const existingFiles = (existingSession?.files ?? []).map((file) =>
|
|
704
|
+
file.storage_session_id == null
|
|
705
|
+
? { ...file, storage_session_id: existingSessionId }
|
|
706
|
+
: file
|
|
707
|
+
);
|
|
708
|
+
const deletedIdentityByName = new Map<string, string>();
|
|
709
|
+
for (const name of deletedFiles ?? []) {
|
|
710
|
+
const identity = baselineIdentityByName?.get(name);
|
|
711
|
+
if (identity != null) {
|
|
712
|
+
deletedIdentityByName.set(name, identity);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
668
715
|
|
|
716
|
+
/* Absence from `files` is not a deletion signal: older Code API versions
|
|
717
|
+
* and truncated responses can omit otherwise-live refs. Remove only paths
|
|
718
|
+
* explicitly attested by `deleted_files`, and only while the current ref
|
|
719
|
+
* still has the identity injected into that request. */
|
|
669
720
|
if (newFiles.length === 0) {
|
|
670
721
|
sessions.set(sessionKey, {
|
|
671
722
|
session_id: execSessionId,
|
|
672
|
-
files: existingFiles
|
|
723
|
+
files: existingFiles.filter(
|
|
724
|
+
(file) =>
|
|
725
|
+
deletedIdentityByName.get(file.name) !==
|
|
726
|
+
fileIdentityKey(file, existingSessionId)
|
|
727
|
+
),
|
|
673
728
|
lastUpdated: Date.now(),
|
|
674
729
|
});
|
|
675
730
|
return;
|
|
@@ -681,26 +736,46 @@ function updateCodeSession(
|
|
|
681
736
|
const filesWithSession: t.FileRefs = [];
|
|
682
737
|
const newFileNames = new Set<string>();
|
|
683
738
|
const incomingByIdentity = new Map<string, number>();
|
|
739
|
+
const existingIdentities = new Set(
|
|
740
|
+
existingFiles.map((file) => fileIdentityKey(file, existingSessionId))
|
|
741
|
+
);
|
|
684
742
|
for (const file of newFiles) {
|
|
743
|
+
const baselineIdentity = baselineIdentityByName?.get(file.name);
|
|
685
744
|
const withSession = {
|
|
686
745
|
...file,
|
|
687
|
-
storage_session_id:
|
|
746
|
+
storage_session_id:
|
|
747
|
+
file.storage_session_id ??
|
|
748
|
+
storageSessionIdFromIdentity(baselineIdentity, file.id) ??
|
|
749
|
+
execSessionId,
|
|
688
750
|
};
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
751
|
+
const identity = fileIdentityKey(withSession);
|
|
752
|
+
const wasTrackedByRequest = baselineIdentity === identity;
|
|
753
|
+
/* An inherited echo is evidence that a request observed an existing ref,
|
|
754
|
+
* not authority to resurrect one removed by a concurrent sibling. Fresh
|
|
755
|
+
* and modified outputs remain eligible to replace by path below. */
|
|
756
|
+
if (
|
|
757
|
+
file.inherited === true &&
|
|
758
|
+
wasTrackedByRequest &&
|
|
759
|
+
(!existingIdentities.has(identity) ||
|
|
760
|
+
deletedIdentityByName.get(file.name) === identity)
|
|
761
|
+
) {
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
incomingByIdentity.set(identity, filesWithSession.length);
|
|
693
765
|
newFileNames.add(withSession.name);
|
|
694
766
|
filesWithSession.push(withSession);
|
|
695
767
|
}
|
|
696
768
|
|
|
697
769
|
const filteredExisting: t.FileRefs = [];
|
|
698
770
|
for (const e of existingFiles) {
|
|
699
|
-
const idx = incomingByIdentity.get(fileIdentityKey(e));
|
|
771
|
+
const idx = incomingByIdentity.get(fileIdentityKey(e, existingSessionId));
|
|
700
772
|
if (idx !== undefined) {
|
|
701
773
|
filesWithSession[idx] = { ...e, ...filesWithSession[idx] };
|
|
702
774
|
}
|
|
703
|
-
|
|
775
|
+
const explicitlyDeleted =
|
|
776
|
+
deletedIdentityByName.get(e.name) ===
|
|
777
|
+
fileIdentityKey(e, existingSessionId);
|
|
778
|
+
if (!newFileNames.has(e.name) && !explicitlyDeleted) {
|
|
704
779
|
filteredExisting.push(e);
|
|
705
780
|
}
|
|
706
781
|
}
|
|
@@ -712,6 +787,53 @@ function updateCodeSession(
|
|
|
712
787
|
});
|
|
713
788
|
}
|
|
714
789
|
|
|
790
|
+
/** Retains host-provisioned inputs even when execution returns no usable artifact. */
|
|
791
|
+
function retainCodeSessionInputs(
|
|
792
|
+
sessions: t.ToolSessionMap,
|
|
793
|
+
sessionKey: string,
|
|
794
|
+
context: t.ToolCallRequest['codeSessionContext']
|
|
795
|
+
): void {
|
|
796
|
+
if (
|
|
797
|
+
context?.session_id == null ||
|
|
798
|
+
context.session_id === '' ||
|
|
799
|
+
context.files == null
|
|
800
|
+
)
|
|
801
|
+
return;
|
|
802
|
+
const existing = sessions.get(sessionKey) as t.CodeSessionContext | undefined;
|
|
803
|
+
const existingFiles = existing?.files ?? [];
|
|
804
|
+
const files = [...existingFiles];
|
|
805
|
+
const indexByIdentity = new Map<string, number>();
|
|
806
|
+
const indexByName = new Map<string, number>();
|
|
807
|
+
for (let i = 0; i < files.length; i++) {
|
|
808
|
+
indexByIdentity.set(fileIdentityKey(files[i]), i);
|
|
809
|
+
indexByName.set(files[i].name, i);
|
|
810
|
+
}
|
|
811
|
+
let changed = existing?.session_id !== context.session_id;
|
|
812
|
+
for (const file of context.files) {
|
|
813
|
+
if (!file.id || !file.name || !file.storage_session_id) continue;
|
|
814
|
+
const identity = fileIdentityKey(file);
|
|
815
|
+
if (indexByIdentity.has(identity)) continue;
|
|
816
|
+
const replacementIndex = indexByName.get(file.name);
|
|
817
|
+
if (replacementIndex !== undefined) {
|
|
818
|
+
indexByIdentity.delete(fileIdentityKey(files[replacementIndex]));
|
|
819
|
+
files[replacementIndex] = { ...file };
|
|
820
|
+
indexByIdentity.set(identity, replacementIndex);
|
|
821
|
+
changed = true;
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
indexByIdentity.set(identity, files.length);
|
|
825
|
+
indexByName.set(file.name, files.length);
|
|
826
|
+
files.push({ ...file });
|
|
827
|
+
changed = true;
|
|
828
|
+
}
|
|
829
|
+
if (!changed) return;
|
|
830
|
+
sessions.set(sessionKey, {
|
|
831
|
+
session_id: context.session_id,
|
|
832
|
+
files,
|
|
833
|
+
lastUpdated: Date.now(),
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
|
|
715
837
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
716
838
|
export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
717
839
|
private toolMap: Map<string, StructuredToolInterface | RunnableToolLike>;
|
|
@@ -787,6 +909,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
787
909
|
* even where `agentId` (the subagent-scope marker) is undefined.
|
|
788
910
|
*/
|
|
789
911
|
private executingAgentId?: string;
|
|
912
|
+
private readonly executionContext?: t.ToolNodeOptions['executionContext'];
|
|
790
913
|
private executingAgentName?: string;
|
|
791
914
|
private rootAgentId?: string;
|
|
792
915
|
private rootAgentName?: string;
|
|
@@ -876,6 +999,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
876
999
|
eagerEventToolSuppressions,
|
|
877
1000
|
agentId,
|
|
878
1001
|
executingAgentId,
|
|
1002
|
+
executionContext,
|
|
879
1003
|
executingAgentName,
|
|
880
1004
|
rootAgentId,
|
|
881
1005
|
rootAgentName,
|
|
@@ -900,65 +1024,124 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
900
1024
|
name: name ?? TOOL_NODE_RUN_NAME,
|
|
901
1025
|
tags,
|
|
902
1026
|
func: async (input, config) => {
|
|
903
|
-
const messages = Array.isArray(input)
|
|
904
|
-
(input as
|
|
905
|
-
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
1027
|
+
const messages = Array.isArray(input)
|
|
1028
|
+
? (input as BaseMessage[])
|
|
1029
|
+
: (input as { messages?: BaseMessage[] }).messages;
|
|
1030
|
+
const assistantBatch =
|
|
1031
|
+
messages == null ? undefined : findAssistantBatch(messages);
|
|
1032
|
+
const activeReplayKey =
|
|
1033
|
+
assistantBatch == null || getToolBatchReplayScope(config) == null
|
|
1034
|
+
? undefined
|
|
1035
|
+
: getAssistantBatchReplayKey(
|
|
1036
|
+
assistantBatch,
|
|
1037
|
+
getToolBatchReplayScope(config)
|
|
1038
|
+
);
|
|
909
1039
|
const replayOwner = getToolBatchReplayOwner(
|
|
910
1040
|
config,
|
|
911
1041
|
this.executingAgentId ?? this.agentId ?? ''
|
|
912
1042
|
);
|
|
913
1043
|
let reviewEvidence = getToolApprovalReviewEvidence(config);
|
|
914
1044
|
const replayState = getToolBatchReplayState(config.configurable);
|
|
915
|
-
const ownsParentBatch =
|
|
916
|
-
(
|
|
917
|
-
|
|
1045
|
+
const ownsParentBatch =
|
|
1046
|
+
replayState?.records.some(
|
|
1047
|
+
(record) =>
|
|
1048
|
+
record.owner === replayOwner && record.batch === activeReplayKey
|
|
1049
|
+
) === true;
|
|
918
1050
|
let isChildReplay = false;
|
|
919
1051
|
if (ownsParentBatch) {
|
|
920
|
-
const calls = this.isSendInput(input)
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1052
|
+
const calls = this.isSendInput(input)
|
|
1053
|
+
? [input.lg_tool_call]
|
|
1054
|
+
: (assistantBatch?.message.tool_calls ?? []);
|
|
1055
|
+
const trustedParentCallIds = new Set(
|
|
1056
|
+
calls
|
|
1057
|
+
.filter(
|
|
1058
|
+
(call) =>
|
|
1059
|
+
call.name === Constants.SUBAGENT &&
|
|
1060
|
+
(
|
|
1061
|
+
this.toolMap.get(call.name) as
|
|
1062
|
+
| ReplayableSubagentTool
|
|
1063
|
+
| undefined
|
|
1064
|
+
)?.[SUBAGENT_REPLAY_CONTROLLER] != null
|
|
1065
|
+
)
|
|
1066
|
+
.map((call) => call.id ?? '')
|
|
1067
|
+
);
|
|
1068
|
+
isChildReplay =
|
|
1069
|
+
reviewEvidence?.owner != null
|
|
1070
|
+
? isChildToolReplayOwner(
|
|
1071
|
+
config,
|
|
1072
|
+
reviewEvidence.owner,
|
|
1073
|
+
trustedParentCallIds
|
|
1074
|
+
)
|
|
1075
|
+
: replayState.records.some((record) =>
|
|
1076
|
+
isChildToolReplayOwner(
|
|
1077
|
+
config,
|
|
1078
|
+
record.owner,
|
|
1079
|
+
trustedParentCallIds
|
|
1080
|
+
)
|
|
1081
|
+
);
|
|
928
1082
|
}
|
|
929
1083
|
const hasApprovalAuthorization =
|
|
930
1084
|
reviewEvidence?.owner != null || replayState?.approvalOwner != null;
|
|
931
|
-
const replayResumeStatus =
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1085
|
+
const replayResumeStatus =
|
|
1086
|
+
hasApprovalAuthorization || replayState != null
|
|
1087
|
+
? getToolReplayResumeStatus(
|
|
1088
|
+
config,
|
|
1089
|
+
reviewEvidence?.interruptId ?? replayState?.interruptId,
|
|
1090
|
+
isChildReplay
|
|
1091
|
+
)
|
|
1092
|
+
: undefined;
|
|
938
1093
|
if (replayResumeStatus === 'unverifiable' && hasApprovalAuthorization) {
|
|
939
|
-
throw new Error(
|
|
1094
|
+
throw new Error(
|
|
1095
|
+
'Cannot verify the active tool approval resume — failing closed'
|
|
1096
|
+
);
|
|
940
1097
|
}
|
|
941
1098
|
if (replayResumeStatus === 'stale') {
|
|
942
1099
|
const configurable = { ...config.configurable };
|
|
943
|
-
clearStaleToolApprovalConfig(
|
|
1100
|
+
clearStaleToolApprovalConfig(
|
|
1101
|
+
configurable,
|
|
1102
|
+
replayOwner,
|
|
1103
|
+
activeReplayKey
|
|
1104
|
+
);
|
|
944
1105
|
config = {
|
|
945
1106
|
...config,
|
|
946
1107
|
configurable,
|
|
947
1108
|
};
|
|
948
1109
|
reviewEvidence = undefined;
|
|
949
1110
|
}
|
|
950
|
-
if (
|
|
951
|
-
|
|
1111
|
+
if (
|
|
1112
|
+
reviewEvidence?.owner != null &&
|
|
1113
|
+
reviewEvidence.owner !== replayOwner &&
|
|
1114
|
+
!isChildReplay
|
|
1115
|
+
) {
|
|
1116
|
+
throw new Error(
|
|
1117
|
+
'Tool approval execution owner changed before resume — failing closed'
|
|
1118
|
+
);
|
|
952
1119
|
}
|
|
953
1120
|
const referenceReplay: { state?: ToolOutputReferenceState } = {};
|
|
954
|
-
const restoredBatches = await restoreToolBatchReplayState(
|
|
955
|
-
|
|
956
|
-
|
|
1121
|
+
const restoredBatches = await restoreToolBatchReplayState(
|
|
1122
|
+
config,
|
|
1123
|
+
replayOwner
|
|
1124
|
+
);
|
|
1125
|
+
if (
|
|
1126
|
+
restoredBatches.length > 0 &&
|
|
1127
|
+
!restoredBatches.some(({ batch }) => batch === activeReplayKey)
|
|
1128
|
+
) {
|
|
1129
|
+
throw new Error(
|
|
1130
|
+
'Tool batch proposal changed before approval replay — failing closed'
|
|
1131
|
+
);
|
|
957
1132
|
}
|
|
958
|
-
if (
|
|
1133
|
+
if (
|
|
1134
|
+
activeReplayKey != null &&
|
|
1135
|
+
getToolBatchReplayState(config.configurable) != null
|
|
1136
|
+
) {
|
|
959
1137
|
this.settledDirectResultsByBatch.delete(activeReplayKey);
|
|
960
1138
|
}
|
|
961
|
-
for (const {
|
|
1139
|
+
for (const {
|
|
1140
|
+
batch,
|
|
1141
|
+
results,
|
|
1142
|
+
turnState,
|
|
1143
|
+
referenceState,
|
|
1144
|
+
} of restoredBatches) {
|
|
962
1145
|
if (batch !== activeReplayKey) {
|
|
963
1146
|
continue;
|
|
964
1147
|
}
|
|
@@ -978,27 +1161,45 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
978
1161
|
throw error;
|
|
979
1162
|
}
|
|
980
1163
|
const resumeState = createRunStepResumeState?.();
|
|
981
|
-
const activeResults =
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1164
|
+
const activeResults =
|
|
1165
|
+
activeReplayKey == null
|
|
1166
|
+
? undefined
|
|
1167
|
+
: this.settledDirectResultsByBatch.get(activeReplayKey);
|
|
1168
|
+
const activeBatches =
|
|
1169
|
+
activeReplayKey == null
|
|
1170
|
+
? new Map<string, Map<string, SettledDirectToolResult>>()
|
|
1171
|
+
: new Map([
|
|
1172
|
+
[
|
|
1173
|
+
activeReplayKey,
|
|
1174
|
+
activeResults ?? new Map<string, SettledDirectToolResult>(),
|
|
1175
|
+
],
|
|
1176
|
+
]);
|
|
985
1177
|
const turnState = this.createSubagentResumeState();
|
|
986
|
-
const activeCallIds = new Set(
|
|
987
|
-
|
|
1178
|
+
const activeCallIds = new Set(
|
|
1179
|
+
assistantBatch?.message.tool_calls?.map((call) => call.id)
|
|
1180
|
+
);
|
|
1181
|
+
turnState.directPathTurns = turnState.directPathTurns.filter(
|
|
1182
|
+
({ toolCallId }) => activeCallIds.has(toolCallId)
|
|
1183
|
+
);
|
|
988
1184
|
throw new GraphInterrupt(
|
|
989
|
-
await Promise.all(
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1185
|
+
await Promise.all(
|
|
1186
|
+
error.interrupts.map(async (pendingInterrupt) => {
|
|
1187
|
+
const value = await attachToolBatchReplayState(
|
|
1188
|
+
pendingInterrupt.value,
|
|
1189
|
+
replayOwner,
|
|
1190
|
+
activeBatches,
|
|
1191
|
+
turnState,
|
|
1192
|
+
referenceReplay.state
|
|
1193
|
+
);
|
|
1194
|
+
return {
|
|
1195
|
+
...pendingInterrupt,
|
|
1196
|
+
value:
|
|
1197
|
+
resumeState == null
|
|
1198
|
+
? value
|
|
1199
|
+
: attachRunStepResumeState(value, resumeState),
|
|
1200
|
+
};
|
|
1201
|
+
})
|
|
1202
|
+
)
|
|
1002
1203
|
);
|
|
1003
1204
|
}
|
|
1004
1205
|
if (createRunStepResumeState == null) {
|
|
@@ -1040,6 +1241,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1040
1241
|
// Default to agentId so callers constructing ToolNode directly (who pass the
|
|
1041
1242
|
// existing agentId option) still get attribution without knowing the new option.
|
|
1042
1243
|
this.executingAgentId = executingAgentId ?? agentId;
|
|
1244
|
+
this.executionContext = executionContext;
|
|
1043
1245
|
this.executingAgentName = executingAgentName;
|
|
1044
1246
|
this.rootAgentId = rootAgentId;
|
|
1045
1247
|
this.rootAgentName = rootAgentName;
|
|
@@ -1109,8 +1311,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1109
1311
|
? options
|
|
1110
1312
|
: {
|
|
1111
1313
|
...options,
|
|
1314
|
+
configurable: {
|
|
1315
|
+
...options?.configurable,
|
|
1316
|
+
executionContext: this.executionContext,
|
|
1317
|
+
},
|
|
1112
1318
|
metadata: {
|
|
1113
1319
|
...options?.metadata,
|
|
1320
|
+
executionContext: this.executionContext,
|
|
1114
1321
|
agentId: this.executingAgentId,
|
|
1115
1322
|
activeAgentId: this.executingAgentId,
|
|
1116
1323
|
...(this.executingAgentName == null
|
|
@@ -1668,7 +1875,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1668
1875
|
return next;
|
|
1669
1876
|
})();
|
|
1670
1877
|
this.recordToolUsageTurn(call.name, usageCount, call.id);
|
|
1671
|
-
if (
|
|
1878
|
+
if (
|
|
1879
|
+
batchContext.replayBatchKey != null &&
|
|
1880
|
+
call.id != null &&
|
|
1881
|
+
call.id !== ''
|
|
1882
|
+
) {
|
|
1672
1883
|
this.directPathTurns.set(call.id, usageCount);
|
|
1673
1884
|
}
|
|
1674
1885
|
let args = call.args;
|
|
@@ -1732,6 +1943,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1732
1943
|
runId: (config.configurable?.run_id as string | undefined) ?? '',
|
|
1733
1944
|
threadId: config.configurable?.thread_id as string | undefined,
|
|
1734
1945
|
agentId: this.agentId,
|
|
1946
|
+
executionContext: this.executionContext,
|
|
1735
1947
|
executingAgentId: this.executingAgentId,
|
|
1736
1948
|
},
|
|
1737
1949
|
};
|
|
@@ -1757,6 +1969,12 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1757
1969
|
| t.CodeSessionContext
|
|
1758
1970
|
| undefined;
|
|
1759
1971
|
const execSessionId = codeSession?.session_id;
|
|
1972
|
+
if (batchContext.codeSessionBaselineByCallId != null) {
|
|
1973
|
+
batchContext.codeSessionBaselineByCallId.set(
|
|
1974
|
+
call.id ?? '',
|
|
1975
|
+
codeSessionIdentityByName(codeSession)
|
|
1976
|
+
);
|
|
1977
|
+
}
|
|
1760
1978
|
if (execSessionId != null && execSessionId !== '') {
|
|
1761
1979
|
invokeParams = {
|
|
1762
1980
|
...invokeParams,
|
|
@@ -2167,8 +2385,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2167
2385
|
approvalReplaySessionId,
|
|
2168
2386
|
approvalReplayKey
|
|
2169
2387
|
);
|
|
2170
|
-
const approvalReviewEvidence = getToolApprovalReviewEvidence(
|
|
2171
|
-
|
|
2388
|
+
const approvalReviewEvidence = getToolApprovalReviewEvidence(
|
|
2389
|
+
config,
|
|
2390
|
+
getToolBatchReplayOwner(
|
|
2391
|
+
config,
|
|
2392
|
+
this.executingAgentId ?? this.agentId ?? ''
|
|
2393
|
+
)
|
|
2394
|
+
);
|
|
2172
2395
|
const reviewedApproval = getReviewedToolApproval(
|
|
2173
2396
|
approvalReviewEvidence?.payload,
|
|
2174
2397
|
call.id
|
|
@@ -2294,6 +2517,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2294
2517
|
runId,
|
|
2295
2518
|
threadId,
|
|
2296
2519
|
agentId: this.agentId,
|
|
2520
|
+
executionContext: this.executionContext,
|
|
2297
2521
|
executingAgentId: this.executingAgentId,
|
|
2298
2522
|
toolName: call.name,
|
|
2299
2523
|
toolInput: resolvedArgs,
|
|
@@ -2305,11 +2529,17 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2305
2529
|
matchQuery: call.name,
|
|
2306
2530
|
onceReplayKey: approvalReplayKey,
|
|
2307
2531
|
onceReplaySessionId: approvalReplaySessionId,
|
|
2308
|
-
}).catch((): AggregatedHookResult | undefined =>
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2532
|
+
}).catch((): AggregatedHookResult | undefined =>
|
|
2533
|
+
approvalReviewEvidence == null
|
|
2534
|
+
? undefined
|
|
2535
|
+
: {
|
|
2536
|
+
decision: 'deny',
|
|
2537
|
+
reason: 'Approval policy could not be evaluated on resume',
|
|
2538
|
+
additionalContexts: [],
|
|
2539
|
+
injectedMessages: [],
|
|
2540
|
+
errors: [],
|
|
2541
|
+
}
|
|
2542
|
+
);
|
|
2313
2543
|
|
|
2314
2544
|
if (preResult != null) {
|
|
2315
2545
|
// Forward any additionalContext strings hooks returned into
|
|
@@ -2651,6 +2881,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2651
2881
|
runId,
|
|
2652
2882
|
threadId,
|
|
2653
2883
|
agentId: this.agentId,
|
|
2884
|
+
executionContext: this.executionContext,
|
|
2654
2885
|
executingAgentId: this.executingAgentId,
|
|
2655
2886
|
toolName: call.name,
|
|
2656
2887
|
toolInput: effectiveCall.args as Record<string, unknown>,
|
|
@@ -2688,6 +2919,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2688
2919
|
runId,
|
|
2689
2920
|
threadId,
|
|
2690
2921
|
agentId: this.agentId,
|
|
2922
|
+
executionContext: this.executionContext,
|
|
2691
2923
|
executingAgentId: this.executingAgentId,
|
|
2692
2924
|
toolName: call.name,
|
|
2693
2925
|
toolInput: effectiveCall.args as Record<string, unknown>,
|
|
@@ -2804,6 +3036,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2804
3036
|
runId,
|
|
2805
3037
|
threadId,
|
|
2806
3038
|
agentId: this.agentId,
|
|
3039
|
+
executionContext: this.executionContext,
|
|
2807
3040
|
executingAgentId: this.executingAgentId,
|
|
2808
3041
|
toolName: call.name,
|
|
2809
3042
|
toolInput: resolvedArgs,
|
|
@@ -2935,18 +3168,26 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2935
3168
|
|
|
2936
3169
|
private storeCodeSessionFromResults(
|
|
2937
3170
|
results: t.ToolExecuteResult[],
|
|
2938
|
-
requestMap: Map<string, t.ToolCallRequest
|
|
3171
|
+
requestMap: Map<string, t.ToolCallRequest>,
|
|
3172
|
+
baselineByRequestId: ReadonlyMap<
|
|
3173
|
+
string,
|
|
3174
|
+
ReadonlyMap<string, string>
|
|
3175
|
+
> = new Map(),
|
|
3176
|
+
executionBaselineByRequestId: ReadonlyMap<
|
|
3177
|
+
string,
|
|
3178
|
+
ReadonlyMap<string, string>
|
|
3179
|
+
> = baselineByRequestId
|
|
2939
3180
|
): void {
|
|
2940
3181
|
if (!this.sessions) {
|
|
2941
3182
|
return;
|
|
2942
3183
|
}
|
|
2943
3184
|
|
|
3185
|
+
this.retainCodeSessionInputsFromRequests(
|
|
3186
|
+
requestMap.values(),
|
|
3187
|
+
baselineByRequestId
|
|
3188
|
+
);
|
|
2944
3189
|
for (let i = 0; i < results.length; i++) {
|
|
2945
3190
|
const result = results[i];
|
|
2946
|
-
if (result.status !== 'success' || result.artifact == null) {
|
|
2947
|
-
continue;
|
|
2948
|
-
}
|
|
2949
|
-
|
|
2950
3191
|
const request = requestMap.get(result.toolCallId);
|
|
2951
3192
|
if (
|
|
2952
3193
|
request?.name == null ||
|
|
@@ -2957,6 +3198,10 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2957
3198
|
continue;
|
|
2958
3199
|
}
|
|
2959
3200
|
|
|
3201
|
+
if (result.status !== 'success' || result.artifact == null) {
|
|
3202
|
+
continue;
|
|
3203
|
+
}
|
|
3204
|
+
|
|
2960
3205
|
const artifact = result.artifact as t.CodeExecutionArtifact | undefined;
|
|
2961
3206
|
const execSessionId = artifact?.session_id;
|
|
2962
3207
|
if (execSessionId == null || execSessionId === '') {
|
|
@@ -2967,11 +3212,59 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2967
3212
|
this.sessions,
|
|
2968
3213
|
this.codeSessionKey,
|
|
2969
3214
|
execSessionId,
|
|
2970
|
-
artifact?.files
|
|
3215
|
+
artifact?.files,
|
|
3216
|
+
artifact?.deleted_files,
|
|
3217
|
+
executionBaselineByRequestId.get(request.id)
|
|
2971
3218
|
);
|
|
2972
3219
|
}
|
|
2973
3220
|
}
|
|
2974
3221
|
|
|
3222
|
+
private retainCodeSessionInputsFromRequests(
|
|
3223
|
+
requests: Iterable<t.ToolCallRequest>,
|
|
3224
|
+
baselineByRequestId: ReadonlyMap<
|
|
3225
|
+
string,
|
|
3226
|
+
ReadonlyMap<string, string>
|
|
3227
|
+
>
|
|
3228
|
+
): void {
|
|
3229
|
+
if (!this.sessions) {
|
|
3230
|
+
return;
|
|
3231
|
+
}
|
|
3232
|
+
const refreshedByName = new Map<string, t.CodeEnvFile>();
|
|
3233
|
+
let refreshedSessionId: string | undefined;
|
|
3234
|
+
let fileSessionId: string | undefined;
|
|
3235
|
+
for (const request of requests) {
|
|
3236
|
+
if (
|
|
3237
|
+
request.name === '' ||
|
|
3238
|
+
(!this.participatesInCodeSession(request.name) &&
|
|
3239
|
+
request.name !== Constants.SKILL_TOOL &&
|
|
3240
|
+
request.name !== Constants.READ_FILE &&
|
|
3241
|
+
request.retainCodeSessionInputs !== true)
|
|
3242
|
+
) {
|
|
3243
|
+
continue;
|
|
3244
|
+
}
|
|
3245
|
+
const context = request.codeSessionContext;
|
|
3246
|
+
if (context?.session_id == null || context.session_id === '') continue;
|
|
3247
|
+
if (context.session_id !== request.codeSessionBaselineId) {
|
|
3248
|
+
refreshedSessionId = context.session_id;
|
|
3249
|
+
}
|
|
3250
|
+
const baselineIdentityByName = baselineByRequestId.get(request.id);
|
|
3251
|
+
for (const file of context.files ?? []) {
|
|
3252
|
+
if (!file.id || !file.name || !file.storage_session_id) continue;
|
|
3253
|
+
if (baselineIdentityByName?.get(file.name) === fileIdentityKey(file)) {
|
|
3254
|
+
continue;
|
|
3255
|
+
}
|
|
3256
|
+
fileSessionId = context.session_id;
|
|
3257
|
+
refreshedByName.set(file.name, file);
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
const sessionId = refreshedSessionId ?? fileSessionId;
|
|
3261
|
+
if (sessionId == null) return;
|
|
3262
|
+
retainCodeSessionInputs(this.sessions, this.codeSessionKey, {
|
|
3263
|
+
session_id: sessionId,
|
|
3264
|
+
files: [...refreshedByName.values()],
|
|
3265
|
+
});
|
|
3266
|
+
}
|
|
3267
|
+
|
|
2975
3268
|
/**
|
|
2976
3269
|
* Post-processes standard runTool outputs: dispatches ON_RUN_STEP_COMPLETED
|
|
2977
3270
|
* and stores code session context. Mirrors the completion handling in
|
|
@@ -2990,6 +3283,10 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2990
3283
|
outputs: (BaseMessage | Command)[],
|
|
2991
3284
|
config: RunnableConfig,
|
|
2992
3285
|
resolvedArgsByCallId?: ResolvedArgsByCallId,
|
|
3286
|
+
codeSessionBaselineByCallId?: ReadonlyMap<
|
|
3287
|
+
string,
|
|
3288
|
+
ReadonlyMap<string, string>
|
|
3289
|
+
>,
|
|
2993
3290
|
errorOwnership?: ToolErrorOwnership
|
|
2994
3291
|
): Promise<void> {
|
|
2995
3292
|
const ownership = errorOwnership ?? this.looseErrorOwnership;
|
|
@@ -3025,7 +3322,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3025
3322
|
ownership.undispatched.delete(toolCallId);
|
|
3026
3323
|
}
|
|
3027
3324
|
|
|
3028
|
-
if (
|
|
3325
|
+
if (
|
|
3326
|
+
toolMessage.status !== 'error' &&
|
|
3327
|
+
this.sessions &&
|
|
3328
|
+
this.participatesInCodeSession(call.name)
|
|
3329
|
+
) {
|
|
3029
3330
|
const artifact = toolMessage.artifact as
|
|
3030
3331
|
| t.CodeExecutionArtifact
|
|
3031
3332
|
| undefined;
|
|
@@ -3035,7 +3336,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3035
3336
|
this.sessions,
|
|
3036
3337
|
this.codeSessionKey,
|
|
3037
3338
|
execSessionId,
|
|
3038
|
-
artifact?.files
|
|
3339
|
+
artifact?.files,
|
|
3340
|
+
artifact?.deleted_files,
|
|
3341
|
+
codeSessionBaselineByCallId?.get(toolCallId)
|
|
3039
3342
|
);
|
|
3040
3343
|
}
|
|
3041
3344
|
}
|
|
@@ -3178,10 +3481,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3178
3481
|
unresolvedByCallId.set(call.id, pre.unresolved);
|
|
3179
3482
|
}
|
|
3180
3483
|
} else if (registry != null) {
|
|
3181
|
-
const options = {
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3484
|
+
const options = {
|
|
3485
|
+
substituteIntentKey: this.toolDeclaresBusinessIntent(call.name),
|
|
3486
|
+
};
|
|
3487
|
+
const { resolved, unresolved } =
|
|
3488
|
+
preBatchSnapshot != null
|
|
3489
|
+
? preBatchSnapshot.resolve(originalArgs, options)
|
|
3490
|
+
: registry.resolve(registryRunId, originalArgs, options);
|
|
3185
3491
|
resolvedArgs = resolved as Record<string, unknown>;
|
|
3186
3492
|
if (unresolved.length > 0 && call.id != null) {
|
|
3187
3493
|
unresolvedByCallId.set(call.id, unresolved);
|
|
@@ -3229,8 +3535,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3229
3535
|
config,
|
|
3230
3536
|
runId
|
|
3231
3537
|
);
|
|
3232
|
-
const approvalReviewEvidence = getToolApprovalReviewEvidence(
|
|
3233
|
-
|
|
3538
|
+
const approvalReviewEvidence = getToolApprovalReviewEvidence(
|
|
3539
|
+
config,
|
|
3540
|
+
getToolBatchReplayOwner(
|
|
3541
|
+
config,
|
|
3542
|
+
this.executingAgentId ?? this.agentId ?? ''
|
|
3543
|
+
)
|
|
3544
|
+
);
|
|
3234
3545
|
const hasPendingApproval = preToolCalls.some(
|
|
3235
3546
|
(entry) =>
|
|
3236
3547
|
entry.call.id != null &&
|
|
@@ -3295,6 +3606,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3295
3606
|
runId,
|
|
3296
3607
|
threadId,
|
|
3297
3608
|
agentId: this.agentId,
|
|
3609
|
+
executionContext: this.executionContext,
|
|
3298
3610
|
executingAgentId: this.executingAgentId,
|
|
3299
3611
|
toolName: entry.call.name,
|
|
3300
3612
|
toolInput: entry.args,
|
|
@@ -3397,6 +3709,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3397
3709
|
runId,
|
|
3398
3710
|
threadId,
|
|
3399
3711
|
agentId: this.agentId,
|
|
3712
|
+
executionContext: this.executionContext,
|
|
3400
3713
|
executingAgentId: this.executingAgentId,
|
|
3401
3714
|
toolName: item.toolName,
|
|
3402
3715
|
toolInput: item.args,
|
|
@@ -3504,9 +3817,15 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3504
3817
|
if (hookResult.updatedInput != null) {
|
|
3505
3818
|
applyInputOverride(entry, hookResult.updatedInput);
|
|
3506
3819
|
} else {
|
|
3507
|
-
const reviewed = getReviewedToolApproval(
|
|
3820
|
+
const reviewed = getReviewedToolApproval(
|
|
3821
|
+
approvalReviewEvidence?.payload,
|
|
3822
|
+
entry.call.id
|
|
3823
|
+
);
|
|
3508
3824
|
if (reviewed != null) {
|
|
3509
|
-
entry.args = this.coerceEventToolArgs(
|
|
3825
|
+
entry.args = this.coerceEventToolArgs(
|
|
3826
|
+
entry.call.name,
|
|
3827
|
+
reviewed.request.arguments
|
|
3828
|
+
);
|
|
3510
3829
|
if (entry.call.id != null) {
|
|
3511
3830
|
unresolvedByCallId.delete(entry.call.id);
|
|
3512
3831
|
}
|
|
@@ -3877,6 +4196,18 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3877
4196
|
}
|
|
3878
4197
|
|
|
3879
4198
|
const requestMap = new Map(plan.allRequests.map((r) => [r.id, r]));
|
|
4199
|
+
for (const request of plan.allRequests) {
|
|
4200
|
+
request.codeSessionBaselineId = request.codeSessionContext?.session_id;
|
|
4201
|
+
}
|
|
4202
|
+
const codeSessionBaselineByRequestId = new Map<
|
|
4203
|
+
string,
|
|
4204
|
+
ReadonlyMap<string, string>
|
|
4205
|
+
>(
|
|
4206
|
+
plan.allRequests.map((request) => [
|
|
4207
|
+
request.id,
|
|
4208
|
+
codeSessionIdentityByName(request.codeSessionContext),
|
|
4209
|
+
])
|
|
4210
|
+
);
|
|
3880
4211
|
const eagerExecutions: Array<{
|
|
3881
4212
|
request: t.ToolCallRequest;
|
|
3882
4213
|
execution: t.EagerEventToolExecution;
|
|
@@ -3887,6 +4218,14 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3887
4218
|
const eagerExecution = this.takeMatchingEagerEventExecution(request);
|
|
3888
4219
|
if (eagerExecution != null) {
|
|
3889
4220
|
eagerExecutions.push({ request, execution: eagerExecution });
|
|
4221
|
+
if (eagerExecution.codeSessionBaselineByName != null) {
|
|
4222
|
+
codeSessionBaselineByRequestId.set(
|
|
4223
|
+
request.id,
|
|
4224
|
+
eagerExecution.codeSessionBaselineByName
|
|
4225
|
+
);
|
|
4226
|
+
}
|
|
4227
|
+
request.codeSessionBaselineId =
|
|
4228
|
+
eagerExecution.request.codeSessionBaselineId;
|
|
3890
4229
|
} else {
|
|
3891
4230
|
dispatchRequests.push(request);
|
|
3892
4231
|
}
|
|
@@ -3972,6 +4311,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3972
4311
|
};
|
|
3973
4312
|
|
|
3974
4313
|
const batchRequest: t.ToolExecuteBatchRequest = {
|
|
4314
|
+
executionContext: this.executionContext,
|
|
3975
4315
|
toolCalls: dispatchRequests,
|
|
3976
4316
|
userId: config.configurable?.user_id as string | undefined,
|
|
3977
4317
|
// Dispatch attribution, NOT the hook subagent-scope marker:
|
|
@@ -4011,24 +4351,46 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4011
4351
|
|
|
4012
4352
|
const eagerResultsPromise = Promise.all(
|
|
4013
4353
|
eagerExecutions.map(async ({ request, execution }) => {
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4354
|
+
try {
|
|
4355
|
+
const results = await this.resolveEagerEventExecution(
|
|
4356
|
+
request,
|
|
4357
|
+
execution
|
|
4358
|
+
);
|
|
4359
|
+
return {
|
|
4360
|
+
results,
|
|
4361
|
+
completionDispatched:
|
|
4362
|
+
execution.completionDispatched === true &&
|
|
4363
|
+
execution.request.turn === request.turn,
|
|
4364
|
+
toolCallId: request.id,
|
|
4365
|
+
};
|
|
4366
|
+
} finally {
|
|
4367
|
+
if (execution.request.codeSessionContext != null) {
|
|
4368
|
+
request.codeSessionContext = execution.request.codeSessionContext;
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4025
4371
|
})
|
|
4026
4372
|
);
|
|
4027
4373
|
|
|
4028
|
-
const [
|
|
4374
|
+
const [eagerOutcome, dispatchedOutcome] = await Promise.allSettled([
|
|
4029
4375
|
eagerResultsPromise,
|
|
4030
4376
|
dispatchPromise,
|
|
4031
4377
|
]);
|
|
4378
|
+
if (eagerOutcome.status === 'rejected') {
|
|
4379
|
+
this.retainCodeSessionInputsFromRequests(
|
|
4380
|
+
requestMap.values(),
|
|
4381
|
+
codeSessionBaselineByRequestId
|
|
4382
|
+
);
|
|
4383
|
+
throw eagerOutcome.reason;
|
|
4384
|
+
}
|
|
4385
|
+
if (dispatchedOutcome.status === 'rejected') {
|
|
4386
|
+
this.retainCodeSessionInputsFromRequests(
|
|
4387
|
+
requestMap.values(),
|
|
4388
|
+
codeSessionBaselineByRequestId
|
|
4389
|
+
);
|
|
4390
|
+
throw dispatchedOutcome.reason;
|
|
4391
|
+
}
|
|
4392
|
+
const eagerResults = eagerOutcome.value;
|
|
4393
|
+
const dispatchedResults = dispatchedOutcome.value;
|
|
4032
4394
|
// Settle in-flight early completion dispatches before the batch loop
|
|
4033
4395
|
// below decides which completions still need emitting.
|
|
4034
4396
|
await Promise.allSettled(earlyCompletionDispatches);
|
|
@@ -4045,8 +4407,26 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4045
4407
|
...flattenedEagerResults,
|
|
4046
4408
|
...dispatchedResults,
|
|
4047
4409
|
];
|
|
4410
|
+
/* Hosts may lazily refresh codeSessionContext while dispatching. The
|
|
4411
|
+
* pre-dispatch snapshot above remains the authority for detecting those
|
|
4412
|
+
* additions, while deletion reconciliation must use the identities the
|
|
4413
|
+
* execution actually received. */
|
|
4414
|
+
const executionBaselineByRequestId = new Map<
|
|
4415
|
+
string,
|
|
4416
|
+
ReadonlyMap<string, string>
|
|
4417
|
+
>(
|
|
4418
|
+
plan.allRequests.map((request) => [
|
|
4419
|
+
request.id,
|
|
4420
|
+
codeSessionIdentityByName(request.codeSessionContext),
|
|
4421
|
+
])
|
|
4422
|
+
);
|
|
4048
4423
|
|
|
4049
|
-
this.storeCodeSessionFromResults(
|
|
4424
|
+
this.storeCodeSessionFromResults(
|
|
4425
|
+
results,
|
|
4426
|
+
requestMap,
|
|
4427
|
+
codeSessionBaselineByRequestId,
|
|
4428
|
+
executionBaselineByRequestId
|
|
4429
|
+
);
|
|
4050
4430
|
|
|
4051
4431
|
for (const result of results) {
|
|
4052
4432
|
if (result.injectedMessages && result.injectedMessages.length > 0) {
|
|
@@ -4115,6 +4495,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4115
4495
|
runId,
|
|
4116
4496
|
threadId,
|
|
4117
4497
|
agentId: this.agentId,
|
|
4498
|
+
executionContext: this.executionContext,
|
|
4118
4499
|
executingAgentId: this.executingAgentId,
|
|
4119
4500
|
toolName,
|
|
4120
4501
|
toolInput: request?.args ?? {},
|
|
@@ -4167,6 +4548,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4167
4548
|
runId,
|
|
4168
4549
|
threadId,
|
|
4169
4550
|
agentId: this.agentId,
|
|
4551
|
+
executionContext: this.executionContext,
|
|
4170
4552
|
executingAgentId: this.executingAgentId,
|
|
4171
4553
|
toolName,
|
|
4172
4554
|
toolInput: request?.args ?? {},
|
|
@@ -4326,28 +4708,40 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4326
4708
|
// effects) every round.
|
|
4327
4709
|
this.eagerEventToolSuppressions?.add(request.name);
|
|
4328
4710
|
this.eagerEventToolSuppressions?.add(execution.toolName);
|
|
4711
|
+
if (
|
|
4712
|
+
this.participatesInCodeSession(execution.toolName) ||
|
|
4713
|
+
execution.toolName === Constants.SKILL_TOOL ||
|
|
4714
|
+
execution.toolName === Constants.READ_FILE
|
|
4715
|
+
) {
|
|
4716
|
+
request.retainCodeSessionInputs = true;
|
|
4717
|
+
}
|
|
4329
4718
|
// eslint-disable-next-line no-console
|
|
4330
4719
|
console.warn(
|
|
4331
4720
|
'[ToolNode] eager prestart args diverged from the final request for ' +
|
|
4332
4721
|
`tool "${request.name}" (toolCallId=${request.id}); suppressing ` +
|
|
4333
4722
|
'eager prestart for this tool for the rest of the run'
|
|
4334
4723
|
);
|
|
4724
|
+
const createMismatchOutcome = (): t.EagerEventToolExecutionOutcome => ({
|
|
4725
|
+
results: [
|
|
4726
|
+
{
|
|
4727
|
+
toolCallId: request.id,
|
|
4728
|
+
status: 'error',
|
|
4729
|
+
content: '',
|
|
4730
|
+
errorMessage:
|
|
4731
|
+
'Tool call changed after eager execution started; refusing to re-run the tool to avoid duplicate side effects.',
|
|
4732
|
+
},
|
|
4733
|
+
],
|
|
4734
|
+
});
|
|
4335
4735
|
return {
|
|
4336
4736
|
toolCallId: request.id,
|
|
4337
4737
|
toolName: request.name,
|
|
4338
4738
|
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
|
-
}),
|
|
4739
|
+
request: execution.request,
|
|
4740
|
+
codeSessionBaselineByName: execution.codeSessionBaselineByName,
|
|
4741
|
+
promise: execution.promise.then(
|
|
4742
|
+
createMismatchOutcome,
|
|
4743
|
+
createMismatchOutcome
|
|
4744
|
+
),
|
|
4351
4745
|
};
|
|
4352
4746
|
}
|
|
4353
4747
|
|
|
@@ -4439,6 +4833,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4439
4833
|
runId,
|
|
4440
4834
|
threadId,
|
|
4441
4835
|
agentId: this.agentId,
|
|
4836
|
+
executionContext: this.executionContext,
|
|
4442
4837
|
executingAgentId: this.executingAgentId,
|
|
4443
4838
|
entries: orderedBatchEntries,
|
|
4444
4839
|
},
|
|
@@ -4647,13 +5042,16 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4647
5042
|
position: number,
|
|
4648
5043
|
result: SettledDirectToolResult
|
|
4649
5044
|
): void => {
|
|
4650
|
-
if (
|
|
4651
|
-
baseContext.replayBatchKey == null ||
|
|
4652
|
-
settledBatchResults == null
|
|
4653
|
-
) {
|
|
5045
|
+
if (baseContext.replayBatchKey == null || settledBatchResults == null) {
|
|
4654
5046
|
return;
|
|
4655
5047
|
}
|
|
4656
|
-
settledBatchResults.set(
|
|
5048
|
+
settledBatchResults.set(
|
|
5049
|
+
directReplayKey(
|
|
5050
|
+
call,
|
|
5051
|
+
baseContext.replayCallPositions?.get(call) ?? batchIndices[position]
|
|
5052
|
+
),
|
|
5053
|
+
result
|
|
5054
|
+
);
|
|
4657
5055
|
this.settledDirectResultsByBatch.set(
|
|
4658
5056
|
baseContext.replayBatchKey,
|
|
4659
5057
|
settledBatchResults
|
|
@@ -4668,14 +5066,26 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4668
5066
|
if (turn != null) {
|
|
4669
5067
|
this.recordToolUsageTurn(call.name, turn, call.id);
|
|
4670
5068
|
}
|
|
4671
|
-
const refMeta = isBaseMessage(result.output)
|
|
5069
|
+
const refMeta = isBaseMessage(result.output)
|
|
5070
|
+
? (result.output.additional_kwargs as t.ToolMessageRefMetadata)
|
|
5071
|
+
: undefined;
|
|
4672
5072
|
if (refMeta?._refKey != null && result.referenceContent != null) {
|
|
4673
|
-
this.toolOutputRegistry?.set(
|
|
5073
|
+
this.toolOutputRegistry?.set(
|
|
5074
|
+
baseContext.batchScopeId,
|
|
5075
|
+
refMeta._refKey,
|
|
5076
|
+
result.referenceContent
|
|
5077
|
+
);
|
|
4674
5078
|
if (isBaseMessage(result.output)) {
|
|
4675
|
-
result = {
|
|
4676
|
-
...
|
|
4677
|
-
|
|
4678
|
-
|
|
5079
|
+
result = {
|
|
5080
|
+
...result,
|
|
5081
|
+
output: new ToolMessage({
|
|
5082
|
+
...(result.output as ToolMessage),
|
|
5083
|
+
additional_kwargs: {
|
|
5084
|
+
...result.output.additional_kwargs,
|
|
5085
|
+
_refScope: baseContext.batchScopeId,
|
|
5086
|
+
},
|
|
5087
|
+
}),
|
|
5088
|
+
};
|
|
4679
5089
|
}
|
|
4680
5090
|
}
|
|
4681
5091
|
if (
|
|
@@ -4685,17 +5095,38 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4685
5095
|
) {
|
|
4686
5096
|
baseContext.resolvedArgsByCallId.set(call.id, result.resolvedArgs);
|
|
4687
5097
|
}
|
|
5098
|
+
if (
|
|
5099
|
+
result.codeSessionBaseline != null &&
|
|
5100
|
+
baseContext.codeSessionBaselineByCallId != null
|
|
5101
|
+
) {
|
|
5102
|
+
baseContext.codeSessionBaselineByCallId.set(
|
|
5103
|
+
call.id ?? '',
|
|
5104
|
+
new Map(result.codeSessionBaseline)
|
|
5105
|
+
);
|
|
5106
|
+
}
|
|
4688
5107
|
return result;
|
|
4689
5108
|
};
|
|
4690
5109
|
const runOne = async (
|
|
4691
5110
|
call: ToolCall,
|
|
4692
5111
|
position: number
|
|
4693
5112
|
): Promise<SettledDirectToolResult> => {
|
|
4694
|
-
const cachedResult = settledBatchResults?.get(
|
|
5113
|
+
const cachedResult = settledBatchResults?.get(
|
|
5114
|
+
directReplayKey(
|
|
5115
|
+
call,
|
|
5116
|
+
baseContext.replayCallPositions?.get(call) ?? batchIndices[position]
|
|
5117
|
+
)
|
|
5118
|
+
);
|
|
4695
5119
|
if (cachedResult != null) {
|
|
4696
|
-
if (
|
|
4697
|
-
|
|
4698
|
-
|
|
5120
|
+
if (
|
|
5121
|
+
cachedResult.proposal.name !== call.name ||
|
|
5122
|
+
!recordArgsEqual(
|
|
5123
|
+
cachedResult.proposal.args,
|
|
5124
|
+
call.args as Record<string, unknown>
|
|
5125
|
+
)
|
|
5126
|
+
) {
|
|
5127
|
+
throw new Error(
|
|
5128
|
+
'Cannot change an already completed tool call during approval replay'
|
|
5129
|
+
);
|
|
4699
5130
|
}
|
|
4700
5131
|
return restoreResult(call, cachedResult);
|
|
4701
5132
|
}
|
|
@@ -4709,16 +5140,33 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4709
5140
|
call.id == null
|
|
4710
5141
|
? undefined
|
|
4711
5142
|
: baseContext.resolvedArgsByCallId?.get(call.id);
|
|
5143
|
+
const codeSessionBaseline =
|
|
5144
|
+
baseContext.codeSessionBaselineByCallId?.get(call.id ?? '');
|
|
4712
5145
|
const result: SettledDirectToolResult = {
|
|
4713
|
-
proposal: structuredClone({
|
|
4714
|
-
|
|
5146
|
+
proposal: structuredClone({
|
|
5147
|
+
name: call.name,
|
|
5148
|
+
args: call.args as Record<string, unknown>,
|
|
5149
|
+
}),
|
|
5150
|
+
turn:
|
|
5151
|
+
call.id == null
|
|
5152
|
+
? undefined
|
|
5153
|
+
: (this.toolCallTurns.get(call.id) ??
|
|
5154
|
+
this.directPathTurns.get(call.id)),
|
|
4715
5155
|
output,
|
|
4716
5156
|
additionalContexts,
|
|
4717
5157
|
...(resolvedArgs == null ? {} : { resolvedArgs }),
|
|
5158
|
+
...(codeSessionBaseline == null
|
|
5159
|
+
? {}
|
|
5160
|
+
: { codeSessionBaseline: [...codeSessionBaseline.entries()] }),
|
|
4718
5161
|
};
|
|
4719
|
-
const refMeta = isBaseMessage(output)
|
|
5162
|
+
const refMeta = isBaseMessage(output)
|
|
5163
|
+
? (output.additional_kwargs as t.ToolMessageRefMetadata)
|
|
5164
|
+
: undefined;
|
|
4720
5165
|
if (refMeta?._refKey != null) {
|
|
4721
|
-
result.referenceContent = this.toolOutputRegistry?.get(
|
|
5166
|
+
result.referenceContent = this.toolOutputRegistry?.get(
|
|
5167
|
+
refMeta._refScope,
|
|
5168
|
+
refMeta._refKey
|
|
5169
|
+
);
|
|
4722
5170
|
}
|
|
4723
5171
|
cacheResult(call, position, result);
|
|
4724
5172
|
return restoreResult(call, result);
|
|
@@ -4734,8 +5182,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4734
5182
|
directCalls.map((call, i) => runOne(call, i))
|
|
4735
5183
|
);
|
|
4736
5184
|
this.throwIfBreakerTripped(config);
|
|
4737
|
-
const failed =
|
|
4738
|
-
results.find(
|
|
5185
|
+
const failed =
|
|
5186
|
+
results.find(
|
|
5187
|
+
(result) =>
|
|
5188
|
+
result.status === 'rejected' && !isGraphInterrupt(result.reason)
|
|
5189
|
+
) ?? results.find((result) => result.status === 'rejected');
|
|
4739
5190
|
if (failed?.status === 'rejected') {
|
|
4740
5191
|
throw failed.reason;
|
|
4741
5192
|
}
|
|
@@ -4877,8 +5328,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4877
5328
|
return (Array.isArray(input) ? outputs : { messages: outputs }) as T;
|
|
4878
5329
|
}
|
|
4879
5330
|
|
|
4880
|
-
|
|
4881
|
-
|
|
5331
|
+
protected async run(
|
|
5332
|
+
input: T,
|
|
5333
|
+
config: RunnableConfig,
|
|
5334
|
+
referenceReplay: { state?: ToolOutputReferenceState } = {}
|
|
5335
|
+
): Promise<T> {
|
|
4882
5336
|
/**
|
|
4883
5337
|
* Breaker read once at batch entry: every downstream path (direct
|
|
4884
5338
|
* `tool.invoke` runtimes and the ON_TOOL_EXECUTE dispatch) inherits this
|
|
@@ -4922,6 +5376,10 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4922
5376
|
* ToolNode cannot read or wipe each other's entries.
|
|
4923
5377
|
*/
|
|
4924
5378
|
const resolvedArgsByCallId = new Map<string, Record<string, unknown>>();
|
|
5379
|
+
const codeSessionBaselineByCallId = new Map<
|
|
5380
|
+
string,
|
|
5381
|
+
ReadonlyMap<string, string>
|
|
5382
|
+
>();
|
|
4925
5383
|
/** Per-invocation error-completion ownership — see `ToolErrorOwnership`. */
|
|
4926
5384
|
const errorOwnership = createToolErrorOwnership();
|
|
4927
5385
|
/**
|
|
@@ -4938,10 +5396,20 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4938
5396
|
const incomingRunId = config.configurable?.run_id as string | undefined;
|
|
4939
5397
|
const batchScopeId = incomingRunId ?? `\0anon-${this.anonBatchCounter++}`;
|
|
4940
5398
|
const resumedReferenceState = referenceReplay.state;
|
|
4941
|
-
const replayInputSnapshot =
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
5399
|
+
const replayInputSnapshot =
|
|
5400
|
+
resumedReferenceState == null
|
|
5401
|
+
? undefined
|
|
5402
|
+
: this.toolOutputRegistry?.resumeBatch(
|
|
5403
|
+
batchScopeId,
|
|
5404
|
+
resumedReferenceState
|
|
5405
|
+
);
|
|
5406
|
+
referenceReplay.state =
|
|
5407
|
+
resumedReferenceState ??
|
|
5408
|
+
this.toolOutputRegistry?.snapshotState(batchScopeId);
|
|
5409
|
+
const turn =
|
|
5410
|
+
resumedReferenceState?.turnCounter ??
|
|
5411
|
+
this.toolOutputRegistry?.nextTurn(batchScopeId) ??
|
|
5412
|
+
0;
|
|
4945
5413
|
let outputs: (BaseMessage | Command)[];
|
|
4946
5414
|
let replayBatchKey: string | undefined;
|
|
4947
5415
|
/** Hoisted from the messages-state branch so the Command tail can carry
|
|
@@ -4992,6 +5460,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4992
5460
|
batchScopeId,
|
|
4993
5461
|
preBatchSnapshot: replayInputSnapshot,
|
|
4994
5462
|
resolvedArgsByCallId,
|
|
5463
|
+
codeSessionBaselineByCallId,
|
|
4995
5464
|
errorOwnership,
|
|
4996
5465
|
additionalContextsSink: directAdditionalContexts,
|
|
4997
5466
|
replayBatchKey: sendReplayBatchKey,
|
|
@@ -5020,6 +5489,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5020
5489
|
[sendOutput],
|
|
5021
5490
|
config,
|
|
5022
5491
|
resolvedArgsByCallId,
|
|
5492
|
+
codeSessionBaselineByCallId,
|
|
5023
5493
|
errorOwnership
|
|
5024
5494
|
);
|
|
5025
5495
|
} else {
|
|
@@ -5045,10 +5515,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5045
5515
|
throw new Error('ToolNode only accepts AIMessages as input.');
|
|
5046
5516
|
}
|
|
5047
5517
|
const aiMessage = assistantBatch.message;
|
|
5048
|
-
replayBatchKey =
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5518
|
+
replayBatchKey =
|
|
5519
|
+
getToolBatchReplayScope(config) == null
|
|
5520
|
+
? undefined
|
|
5521
|
+
: getAssistantBatchReplayKey(
|
|
5522
|
+
assistantBatch,
|
|
5523
|
+
getToolBatchReplayScope(config)
|
|
5524
|
+
);
|
|
5052
5525
|
|
|
5053
5526
|
if (this.loadRuntimeTools) {
|
|
5054
5527
|
const { tools, toolMap } = this.loadRuntimeTools(
|
|
@@ -5059,7 +5532,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5059
5532
|
this.applyToolExecutionOverrides();
|
|
5060
5533
|
}
|
|
5061
5534
|
|
|
5062
|
-
const replayCallPositions = new Map(
|
|
5535
|
+
const replayCallPositions = new Map(
|
|
5536
|
+
(aiMessage.tool_calls ?? []).map((call, i) => [call, i])
|
|
5537
|
+
);
|
|
5063
5538
|
const filteredCalls =
|
|
5064
5539
|
aiMessage.tool_calls?.filter((call) => {
|
|
5065
5540
|
/**
|
|
@@ -5069,7 +5544,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5069
5544
|
* which are executed by the provider's API and don't require invocation
|
|
5070
5545
|
*/
|
|
5071
5546
|
return (
|
|
5072
|
-
(call.id == null ||
|
|
5547
|
+
(call.id == null ||
|
|
5548
|
+
call.id === '' ||
|
|
5549
|
+
!toolMessageIds.has(call.id)) &&
|
|
5073
5550
|
!(
|
|
5074
5551
|
call.id?.startsWith(Constants.ANTHROPIC_SERVER_TOOL_PREFIX) ??
|
|
5075
5552
|
false
|
|
@@ -5234,7 +5711,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5234
5711
|
* same-turn direct outputs.
|
|
5235
5712
|
*/
|
|
5236
5713
|
const preBatchSnapshot =
|
|
5237
|
-
replayInputSnapshot ??
|
|
5714
|
+
replayInputSnapshot ??
|
|
5715
|
+
this.toolOutputRegistry?.snapshot(batchScopeId);
|
|
5238
5716
|
if (preBatchSnapshot != null) {
|
|
5239
5717
|
for (const entry of eventEntries) {
|
|
5240
5718
|
if (entry.call.id != null) {
|
|
@@ -5269,6 +5747,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5269
5747
|
turn,
|
|
5270
5748
|
batchScopeId,
|
|
5271
5749
|
resolvedArgsByCallId,
|
|
5750
|
+
codeSessionBaselineByCallId,
|
|
5272
5751
|
errorOwnership,
|
|
5273
5752
|
preBatchSnapshot,
|
|
5274
5753
|
additionalContextsSink: directAdditionalContexts,
|
|
@@ -5281,11 +5760,18 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5281
5760
|
: [];
|
|
5282
5761
|
|
|
5283
5762
|
const settledDirectResults =
|
|
5284
|
-
replayBatchKey == null
|
|
5763
|
+
replayBatchKey == null
|
|
5764
|
+
? undefined
|
|
5765
|
+
: this.settledDirectResultsByBatch.get(replayBatchKey);
|
|
5285
5766
|
const unhandledDirectCalls: ToolCall[] = [];
|
|
5286
5767
|
const unhandledDirectOutputs: (BaseMessage | Command)[] = [];
|
|
5287
5768
|
for (let i = 0; i < directCalls.length; i++) {
|
|
5288
|
-
const settled = settledDirectResults?.get(
|
|
5769
|
+
const settled = settledDirectResults?.get(
|
|
5770
|
+
directReplayKey(
|
|
5771
|
+
directCalls[i],
|
|
5772
|
+
replayCallPositions.get(directCalls[i])!
|
|
5773
|
+
)
|
|
5774
|
+
);
|
|
5289
5775
|
if (settled?.completionHandled === true) {
|
|
5290
5776
|
continue;
|
|
5291
5777
|
}
|
|
@@ -5299,10 +5785,16 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5299
5785
|
unhandledDirectOutputs,
|
|
5300
5786
|
config,
|
|
5301
5787
|
resolvedArgsByCallId,
|
|
5788
|
+
codeSessionBaselineByCallId,
|
|
5302
5789
|
errorOwnership
|
|
5303
5790
|
);
|
|
5304
5791
|
for (let i = 0; i < directCalls.length; i++) {
|
|
5305
|
-
const settled = settledDirectResults?.get(
|
|
5792
|
+
const settled = settledDirectResults?.get(
|
|
5793
|
+
directReplayKey(
|
|
5794
|
+
directCalls[i],
|
|
5795
|
+
replayCallPositions.get(directCalls[i])!
|
|
5796
|
+
)
|
|
5797
|
+
);
|
|
5306
5798
|
if (settled != null) {
|
|
5307
5799
|
settled.completionHandled = true;
|
|
5308
5800
|
}
|
|
@@ -5355,7 +5847,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5355
5847
|
// leak a sibling's just-registered output into a sister
|
|
5356
5848
|
// call's args mid-await (Codex P1 #18).
|
|
5357
5849
|
const preBatchSnapshot =
|
|
5358
|
-
replayInputSnapshot ??
|
|
5850
|
+
replayInputSnapshot ??
|
|
5851
|
+
this.toolOutputRegistry?.snapshot(batchScopeId);
|
|
5359
5852
|
const directAdditionalContexts: string[] = [];
|
|
5360
5853
|
const approvalScopeCallIds = new Set(
|
|
5361
5854
|
filteredCalls.flatMap((call) => (call.id == null ? [] : [call.id]))
|
|
@@ -5368,6 +5861,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5368
5861
|
turn,
|
|
5369
5862
|
batchScopeId,
|
|
5370
5863
|
resolvedArgsByCallId,
|
|
5864
|
+
codeSessionBaselineByCallId,
|
|
5371
5865
|
errorOwnership,
|
|
5372
5866
|
preBatchSnapshot,
|
|
5373
5867
|
additionalContextsSink: directAdditionalContexts,
|
|
@@ -5382,6 +5876,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5382
5876
|
toolOutputs,
|
|
5383
5877
|
config,
|
|
5384
5878
|
resolvedArgsByCallId,
|
|
5879
|
+
codeSessionBaselineByCallId,
|
|
5385
5880
|
errorOwnership
|
|
5386
5881
|
);
|
|
5387
5882
|
// Append accumulated additionalContexts as a single
|