@librechat/agents 3.8.1 → 3.8.3
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/graphs/Graph.cjs +2 -2
- package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
- package/dist/cjs/hitl/approvalReview.cjs +89 -0
- package/dist/cjs/hitl/approvalReview.cjs.map +1 -0
- package/dist/cjs/hooks/index.cjs +2 -0
- package/dist/cjs/hooks/index.cjs.map +1 -1
- package/dist/cjs/hooks/types.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +1 -1
- package/dist/cjs/main.cjs +5 -3
- package/dist/cjs/messages/core.cjs +1 -1
- package/dist/cjs/messages/format.cjs +1 -1
- package/dist/cjs/messages/prune.cjs +2 -2
- package/dist/cjs/run.cjs +17 -6
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +1 -1
- package/dist/cjs/stream.cjs +2 -2
- package/dist/cjs/summarization/node.cjs +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +2 -3
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +3 -2
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +63 -11
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +8 -7
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +271 -84
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +24 -9
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentReplay.cjs +2 -0
- package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -1
- package/dist/cjs/tools/toolBatchReplay.cjs +198 -0
- package/dist/cjs/tools/toolBatchReplay.cjs.map +1 -0
- package/dist/cjs/tools/toolOutputReferences.cjs +12 -0
- package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
- package/dist/cjs/types/hitl.cjs +4 -0
- package/dist/cjs/types/hitl.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +2 -2
- package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
- package/dist/esm/hitl/approvalReview.mjs +83 -0
- package/dist/esm/hitl/approvalReview.mjs.map +1 -0
- package/dist/esm/hooks/index.mjs +2 -1
- package/dist/esm/hooks/index.mjs.map +1 -1
- package/dist/esm/hooks/types.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +1 -1
- package/dist/esm/main.mjs +6 -6
- package/dist/esm/messages/core.mjs +1 -1
- package/dist/esm/messages/format.mjs +1 -1
- package/dist/esm/messages/prune.mjs +2 -2
- package/dist/esm/run.mjs +18 -7
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +1 -1
- package/dist/esm/stream.mjs +2 -2
- package/dist/esm/summarization/node.mjs +1 -1
- package/dist/esm/tools/BashExecutor.mjs +2 -3
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +3 -2
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +63 -12
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +8 -7
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +271 -84
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +24 -9
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentReplay.mjs +1 -1
- package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -1
- package/dist/esm/tools/toolBatchReplay.mjs +186 -0
- package/dist/esm/tools/toolBatchReplay.mjs.map +1 -0
- package/dist/esm/tools/toolOutputReferences.mjs +12 -0
- package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
- package/dist/esm/types/hitl.mjs +4 -1
- package/dist/esm/types/hitl.mjs.map +1 -1
- package/dist/types/hitl/approvalReview.d.ts +32 -0
- package/dist/types/hooks/index.d.ts +2 -0
- package/dist/types/hooks/types.d.ts +6 -0
- package/dist/types/tools/CodeExecutor.d.ts +2 -0
- package/dist/types/tools/ToolNode.d.ts +12 -6
- package/dist/types/tools/subagent/SubagentReplay.d.ts +2 -0
- package/dist/types/tools/toolBatchReplay.d.ts +63 -0
- package/dist/types/tools/toolOutputReferences.d.ts +2 -0
- package/package.json +1 -1
- package/src/hitl/approvalReview.ts +209 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/types.ts +6 -0
- package/src/run.ts +34 -20
- package/src/tools/BashExecutor.ts +2 -2
- package/src/tools/BashProgrammaticToolCalling.ts +5 -3
- package/src/tools/CodeExecutor.ts +111 -17
- package/src/tools/ProgrammaticToolCalling.ts +10 -8
- package/src/tools/ToolNode.ts +536 -184
- package/src/tools/subagent/SubagentExecutor.ts +35 -6
- package/src/tools/subagent/SubagentReplay.ts +2 -2
- package/src/tools/toolBatchReplay.ts +423 -0
- package/src/tools/toolOutputReferences.ts +16 -0
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
const require_truncation = require("../utils/truncation.cjs");
|
|
2
1
|
const require_enum = require("../common/enum.cjs");
|
|
3
2
|
require("../common/index.cjs");
|
|
3
|
+
const require_truncation = require("../utils/truncation.cjs");
|
|
4
4
|
const require_runStepResume = require("./runStepResume.cjs");
|
|
5
5
|
const require_SubagentReplay = require("./subagent/SubagentReplay.cjs");
|
|
6
6
|
const require_misc = require("../utils/misc.cjs");
|
|
7
7
|
const require_langfuseRuntimeScope = require("../langfuseRuntimeScope.cjs");
|
|
8
8
|
const require_toolContent = require("../utils/toolContent.cjs");
|
|
9
9
|
const require_provenance = require("../messages/provenance.cjs");
|
|
10
|
+
const require_eagerEventExecution = require("./eagerEventExecution.cjs");
|
|
11
|
+
const require_approvalReview = require("../hitl/approvalReview.cjs");
|
|
10
12
|
const require_types = require("../hooks/types.cjs");
|
|
13
|
+
const require_toolBatchReplay = require("./toolBatchReplay.cjs");
|
|
11
14
|
const require_executeHooks = require("../hooks/executeHooks.cjs");
|
|
12
15
|
require("../hooks/index.cjs");
|
|
13
16
|
const require_injected = require("../messages/injected.cjs");
|
|
14
17
|
const require_events = require("../utils/events.cjs");
|
|
15
18
|
const require_streamLimits = require("../llm/streamLimits.cjs");
|
|
16
|
-
const require_eagerEventExecution = require("./eagerEventExecution.cjs");
|
|
17
19
|
const require_intentArg = require("./intentArg.cjs");
|
|
18
20
|
const require_toolOutputReferences = require("./toolOutputReferences.cjs");
|
|
19
21
|
const require_preparedSubagents = require("./preparedSubagents.cjs");
|
|
@@ -28,10 +30,17 @@ let _langchain_core_runnables = require("@langchain/core/runnables");
|
|
|
28
30
|
let _langchain_core_messages = require("@langchain/core/messages");
|
|
29
31
|
let _langchain_langgraph = require("@langchain/langgraph");
|
|
30
32
|
let _langchain_core_singletons = require("@langchain/core/singletons");
|
|
33
|
+
let crypto = require("crypto");
|
|
31
34
|
//#region src/tools/ToolNode.ts
|
|
32
|
-
function
|
|
33
|
-
if (configurable == null || !("
|
|
34
|
-
const { [
|
|
35
|
+
function stripToolApprovalReviewConfig(configurable) {
|
|
36
|
+
if (configurable == null || !("__librechat_tool_approval_review" in configurable) && !("__librechat_tool_batch_replay" in configurable)) return configurable;
|
|
37
|
+
const { [require_approvalReview.TOOL_APPROVAL_REVIEW_CONFIG_KEY]: _review, [require_toolBatchReplay.TOOL_BATCH_REPLAY_KEY]: _replay, ...rest } = configurable;
|
|
38
|
+
return rest;
|
|
39
|
+
}
|
|
40
|
+
function stripHostExecutionConfig(configurable) {
|
|
41
|
+
const withoutReview = stripToolApprovalReviewConfig(configurable);
|
|
42
|
+
if (withoutReview == null || !("lc_run_breaker_scope" in withoutReview)) return withoutReview;
|
|
43
|
+
const { [require_streamLimits.RUN_BREAKER_SCOPE_CONFIG_KEY]: _scope, ...rest } = withoutReview;
|
|
35
44
|
return rest;
|
|
36
45
|
}
|
|
37
46
|
function createToolApprovalReplayKey(config, agentId, toolUseId) {
|
|
@@ -60,6 +69,9 @@ function withSubagentReplayBatch(config, replayBatchKey) {
|
|
|
60
69
|
}
|
|
61
70
|
};
|
|
62
71
|
}
|
|
72
|
+
function directReplayKey(call, batchIndex) {
|
|
73
|
+
return call.id == null || call.id === "" ? JSON.stringify(["position", batchIndex]) : JSON.stringify(["id", call.id]);
|
|
74
|
+
}
|
|
63
75
|
function createToolErrorOwnership() {
|
|
64
76
|
return {
|
|
65
77
|
handlerOwned: /* @__PURE__ */ new Set(),
|
|
@@ -113,20 +125,14 @@ function describeOfferedShape(value) {
|
|
|
113
125
|
function findAssistantBatch(messages) {
|
|
114
126
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
115
127
|
const message = messages[i];
|
|
116
|
-
if ((0, _langchain_core_messages.isAIMessage)(message)) return {
|
|
117
|
-
message,
|
|
118
|
-
index: i,
|
|
119
|
-
messageCount: messages.length
|
|
120
|
-
};
|
|
128
|
+
if ((0, _langchain_core_messages.isAIMessage)(message)) return { message };
|
|
121
129
|
}
|
|
122
130
|
}
|
|
123
|
-
function getAssistantBatchReplayKey(batch,
|
|
131
|
+
function getAssistantBatchReplayKey(batch, threadId) {
|
|
124
132
|
return JSON.stringify([
|
|
125
|
-
runId ?? null,
|
|
126
133
|
threadId ?? null,
|
|
127
|
-
batch.message.id
|
|
128
|
-
batch.
|
|
129
|
-
batch.messageCount
|
|
134
|
+
batch.message.id == null || batch.message.id === "" ? null : batch.message.id,
|
|
135
|
+
(0, crypto.createHash)("sha256").update(require_eagerEventExecution.stableStringify(batch.message.tool_calls ?? [])).digest("hex")
|
|
130
136
|
]);
|
|
131
137
|
}
|
|
132
138
|
function buildToolApprovalInterruptPayload(askEntries, hookSessionId) {
|
|
@@ -244,7 +250,7 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
244
250
|
toolUsageCount;
|
|
245
251
|
toolCallTurns = /* @__PURE__ */ new Map();
|
|
246
252
|
directPathTurns = /* @__PURE__ */ new Map();
|
|
247
|
-
|
|
253
|
+
settledDirectResultsByBatch = /* @__PURE__ */ new Map();
|
|
248
254
|
toolRegistry;
|
|
249
255
|
toolDefinitions;
|
|
250
256
|
localImplementationNames = /* @__PURE__ */ new Set();
|
|
@@ -279,16 +285,59 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
279
285
|
name: name ?? TOOL_NODE_RUN_NAME,
|
|
280
286
|
tags,
|
|
281
287
|
func: async (input, config) => {
|
|
288
|
+
const messages = Array.isArray(input) ? input : input.messages;
|
|
289
|
+
const assistantBatch = messages == null ? void 0 : findAssistantBatch(messages);
|
|
290
|
+
const activeReplayKey = assistantBatch == null || require_toolBatchReplay.getToolBatchReplayScope(config) == null ? void 0 : getAssistantBatchReplayKey(assistantBatch, require_toolBatchReplay.getToolBatchReplayScope(config));
|
|
291
|
+
const replayOwner = require_toolBatchReplay.getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? "");
|
|
292
|
+
let reviewEvidence = require_approvalReview.getToolApprovalReviewEvidence(config);
|
|
293
|
+
const replayState = require_toolBatchReplay.getToolBatchReplayState(config.configurable);
|
|
294
|
+
const ownsParentBatch = replayState?.records.some((record) => record.owner === replayOwner && record.batch === activeReplayKey) === true;
|
|
295
|
+
let isChildReplay = false;
|
|
296
|
+
if (ownsParentBatch) {
|
|
297
|
+
const calls = this.isSendInput(input) ? [input.lg_tool_call] : assistantBatch?.message.tool_calls ?? [];
|
|
298
|
+
const trustedParentCallIds = new Set(calls.filter((call) => call.name === "subagent" && this.toolMap.get(call.name)?.[require_SubagentReplay.SUBAGENT_REPLAY_CONTROLLER] != null).map((call) => call.id ?? ""));
|
|
299
|
+
isChildReplay = reviewEvidence?.owner != null ? require_toolBatchReplay.isChildToolReplayOwner(config, reviewEvidence.owner, trustedParentCallIds) : replayState.records.some((record) => require_toolBatchReplay.isChildToolReplayOwner(config, record.owner, trustedParentCallIds));
|
|
300
|
+
}
|
|
301
|
+
if ((reviewEvidence?.owner != null || replayState != null) && !require_toolBatchReplay.isToolReplayResume(config, reviewEvidence?.interruptId ?? replayState?.interruptId, isChildReplay)) {
|
|
302
|
+
config = {
|
|
303
|
+
...config,
|
|
304
|
+
configurable: {
|
|
305
|
+
...config.configurable,
|
|
306
|
+
[require_approvalReview.TOOL_APPROVAL_REVIEW_CONFIG_KEY]: void 0,
|
|
307
|
+
[require_toolBatchReplay.TOOL_BATCH_REPLAY_KEY]: void 0
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
reviewEvidence = void 0;
|
|
311
|
+
}
|
|
312
|
+
if (reviewEvidence?.owner != null && reviewEvidence.owner !== replayOwner && !isChildReplay) throw new Error("Tool approval execution owner changed before resume — failing closed");
|
|
313
|
+
const referenceReplay = {};
|
|
314
|
+
const restoredBatches = await require_toolBatchReplay.restoreToolBatchReplayState(config, replayOwner);
|
|
315
|
+
if (restoredBatches.length > 0 && !restoredBatches.some(({ batch }) => batch === activeReplayKey)) throw new Error("Tool batch proposal changed before approval replay — failing closed");
|
|
316
|
+
if (activeReplayKey != null && require_toolBatchReplay.getToolBatchReplayState(config.configurable) != null) this.settledDirectResultsByBatch.delete(activeReplayKey);
|
|
317
|
+
for (const { batch, results, turnState, referenceState } of restoredBatches) {
|
|
318
|
+
if (batch !== activeReplayKey) continue;
|
|
319
|
+
this.settledDirectResultsByBatch.set(batch, new Map(results));
|
|
320
|
+
if (turnState != null) this.restoreSubagentResumeState(turnState);
|
|
321
|
+
referenceReplay.state = referenceState;
|
|
322
|
+
}
|
|
282
323
|
restoreRunStepResumeState?.(input.runStepState, config);
|
|
283
324
|
let result;
|
|
284
325
|
try {
|
|
285
|
-
result = await this.run(input, config);
|
|
326
|
+
result = await this.run(input, config, referenceReplay);
|
|
286
327
|
} catch (error) {
|
|
287
|
-
if (!(0, _langchain_langgraph.isGraphInterrupt)(error)
|
|
288
|
-
const resumeState = createRunStepResumeState();
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
328
|
+
if (!(0, _langchain_langgraph.isGraphInterrupt)(error)) throw error;
|
|
329
|
+
const resumeState = createRunStepResumeState?.();
|
|
330
|
+
const activeResults = activeReplayKey == null ? void 0 : this.settledDirectResultsByBatch.get(activeReplayKey);
|
|
331
|
+
const activeBatches = activeReplayKey == null ? /* @__PURE__ */ new Map() : new Map([[activeReplayKey, activeResults ?? /* @__PURE__ */ new Map()]]);
|
|
332
|
+
const turnState = this.createSubagentResumeState();
|
|
333
|
+
const activeCallIds = new Set(assistantBatch?.message.tool_calls?.map((call) => call.id));
|
|
334
|
+
turnState.directPathTurns = turnState.directPathTurns.filter(({ toolCallId }) => activeCallIds.has(toolCallId));
|
|
335
|
+
throw new _langchain_langgraph.GraphInterrupt(await Promise.all(error.interrupts.map(async (pendingInterrupt) => {
|
|
336
|
+
const value = await require_toolBatchReplay.attachToolBatchReplayState(pendingInterrupt.value, replayOwner, activeBatches, turnState, referenceReplay.state);
|
|
337
|
+
return {
|
|
338
|
+
...pendingInterrupt,
|
|
339
|
+
value: resumeState == null ? value : require_runStepResume.attachRunStepResumeState(value, resumeState)
|
|
340
|
+
};
|
|
292
341
|
})));
|
|
293
342
|
}
|
|
294
343
|
if (createRunStepResumeState == null) return result;
|
|
@@ -416,7 +465,7 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
416
465
|
}
|
|
417
466
|
clearDirectPathTurns() {
|
|
418
467
|
this.directPathTurns.clear();
|
|
419
|
-
this.
|
|
468
|
+
this.settledDirectResultsByBatch.clear();
|
|
420
469
|
}
|
|
421
470
|
getCallerCapabilityProjection() {
|
|
422
471
|
const discoveredToolNames = new Set(this.getDiscoveredToolNames?.() ?? []);
|
|
@@ -486,11 +535,16 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
486
535
|
}
|
|
487
536
|
invokeWithRuntime(tool, invokeParams, call, config, runInput) {
|
|
488
537
|
const lgConfig = config;
|
|
489
|
-
const
|
|
538
|
+
const isTrustedSubagentHop = call.name === "subagent" && tool[require_SubagentReplay.SUBAGENT_REPLAY_CONTROLLER] != null;
|
|
539
|
+
const toolConfig = {
|
|
490
540
|
...config,
|
|
541
|
+
configurable: isTrustedSubagentHop ? config.configurable : stripToolApprovalReviewConfig(config.configurable)
|
|
542
|
+
};
|
|
543
|
+
const runtime = {
|
|
544
|
+
...toolConfig,
|
|
491
545
|
state: runInput,
|
|
492
546
|
toolCallId: call.id ?? "",
|
|
493
|
-
config,
|
|
547
|
+
config: toolConfig,
|
|
494
548
|
context: lgConfig.context,
|
|
495
549
|
store: lgConfig.store ?? null,
|
|
496
550
|
writer: lgConfig.writer ?? config.configurable?.writer ?? null
|
|
@@ -576,6 +630,8 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
576
630
|
if (call.id != null && call.id !== "") this.toolCallTurns.set(call.id, next);
|
|
577
631
|
return next;
|
|
578
632
|
})();
|
|
633
|
+
this.recordToolUsageTurn(call.name, usageCount, call.id);
|
|
634
|
+
if (batchContext.replayBatchKey != null && call.id != null && call.id !== "") this.directPathTurns.set(call.id, usageCount);
|
|
579
635
|
let args = call.args;
|
|
580
636
|
if (resolveFn != null) {
|
|
581
637
|
const { resolved, unresolved } = resolveFn(runId, args);
|
|
@@ -724,6 +780,7 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
724
780
|
throw new require_preparedSubagents.PreparedSubagentError("Tool hooks changed after a subagent started.");
|
|
725
781
|
}
|
|
726
782
|
const replayController = this.toolMap.get(call.name)?.[require_SubagentReplay.SUBAGENT_REPLAY_CONTROLLER];
|
|
783
|
+
const isTrustedSubagentReviewHop = call.name === "subagent" && replayController != null;
|
|
727
784
|
const replayConfig = replayController == null ? config : withSubagentReplayBatch(config, batchContext.replayBatchKey);
|
|
728
785
|
const settledOutput = await replayController?.getSettledOutput(call, replayConfig);
|
|
729
786
|
if (settledOutput != null) {
|
|
@@ -754,9 +811,12 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
754
811
|
const approvalReplayKey = call.id == null || call.id === "" ? void 0 : createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", call.id);
|
|
755
812
|
const approvalReplaySessionId = getToolApprovalReplaySessionId(config, runId);
|
|
756
813
|
const pendingApproval = approvalReplayKey == null ? void 0 : hookRegistry?.getPendingToolApproval(approvalReplaySessionId, approvalReplayKey);
|
|
814
|
+
const approvalReviewEvidence = require_approvalReview.getToolApprovalReviewEvidence(config, require_toolBatchReplay.getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? ""));
|
|
815
|
+
const reviewedApproval = require_approvalReview.getReviewedToolApproval(approvalReviewEvidence?.payload, call.id);
|
|
816
|
+
const approvalEvidenceAppliesToBatch = approvalReviewEvidence?.payload.action_requests.some((request) => batchContext.approvalScopeCallIds == null ? request.tool_call_id === call.id : batchContext.approvalScopeCallIds.has(request.tool_call_id)) === true;
|
|
757
817
|
const hasPostHook = hookRegistry?.hasHookFor("PostToolUse", runId) === true;
|
|
758
818
|
const hasFailureHook = hookRegistry?.hasHookFor("PostToolUseFailure", runId) === true;
|
|
759
|
-
if (
|
|
819
|
+
if (!hasPreHook && pendingApproval == null && reviewedApproval == null && approvalReviewEvidence == null && !hasPostHook && !hasFailureHook) {
|
|
760
820
|
const output = await this.runTool(call, replayConfig, batchContext);
|
|
761
821
|
return output instanceof _langchain_core_messages.ToolMessage ? persistOutput(output) : output;
|
|
762
822
|
}
|
|
@@ -774,18 +834,21 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
774
834
|
}
|
|
775
835
|
const turn = usageCount;
|
|
776
836
|
const stepId = this.toolCallStepIds?.get(call.id ?? "") ?? "";
|
|
777
|
-
let resolvedArgs = call.args;
|
|
778
837
|
const hookResolveOptions = { substituteIntentKey: this.toolDeclaresBusinessIntent(call.name) };
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
838
|
+
const resolveDirectArgs = (args) => {
|
|
839
|
+
if (batchContext.preBatchSnapshot != null) return batchContext.preBatchSnapshot.resolve(args, hookResolveOptions).resolved;
|
|
840
|
+
if (this.toolOutputRegistry != null) return this.toolOutputRegistry.resolve(registryRunId, args, hookResolveOptions).resolved;
|
|
841
|
+
return args;
|
|
842
|
+
};
|
|
843
|
+
const originalResolvedArgs = resolveDirectArgs(call.args);
|
|
844
|
+
let resolvedArgs = hasPreHook || pendingApproval != null ? originalResolvedArgs : reviewedApproval?.request.arguments ?? originalResolvedArgs;
|
|
786
845
|
let effectiveCall = call;
|
|
787
|
-
if (hasPreHook || pendingApproval != null) {
|
|
788
|
-
const preResult =
|
|
846
|
+
if (hasPreHook || pendingApproval != null || reviewedApproval != null || approvalReviewEvidence != null) {
|
|
847
|
+
const preResult = hookRegistry == null ? {
|
|
848
|
+
additionalContexts: [],
|
|
849
|
+
injectedMessages: [],
|
|
850
|
+
errors: []
|
|
851
|
+
} : await require_executeHooks.executeHooks({
|
|
789
852
|
registry: hookRegistry,
|
|
790
853
|
input: {
|
|
791
854
|
hook_event_name: "PreToolUse",
|
|
@@ -803,16 +866,31 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
803
866
|
matchQuery: call.name,
|
|
804
867
|
onceReplayKey: approvalReplayKey,
|
|
805
868
|
onceReplaySessionId: approvalReplaySessionId
|
|
806
|
-
}).catch(() => void 0
|
|
869
|
+
}).catch(() => approvalReviewEvidence == null ? void 0 : {
|
|
870
|
+
decision: "deny",
|
|
871
|
+
reason: "Approval policy could not be evaluated on resume",
|
|
872
|
+
additionalContexts: [],
|
|
873
|
+
injectedMessages: [],
|
|
874
|
+
errors: []
|
|
875
|
+
});
|
|
807
876
|
if (preResult != null) {
|
|
808
877
|
if (preResult.additionalContexts.length > 0) {
|
|
809
878
|
replayAdditionalContexts.push(...preResult.additionalContexts);
|
|
810
879
|
batchContext.additionalContextsSink?.push(...preResult.additionalContexts);
|
|
811
880
|
}
|
|
812
|
-
if (preResult.updatedInput != null)
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
881
|
+
if (preResult.updatedInput != null) {
|
|
882
|
+
resolvedArgs = resolveDirectArgs(preResult.updatedInput);
|
|
883
|
+
effectiveCall = {
|
|
884
|
+
...call,
|
|
885
|
+
args: resolvedArgs
|
|
886
|
+
};
|
|
887
|
+
} else if (reviewedApproval != null) {
|
|
888
|
+
resolvedArgs = reviewedApproval.request.arguments;
|
|
889
|
+
effectiveCall = {
|
|
890
|
+
...call,
|
|
891
|
+
args: resolvedArgs
|
|
892
|
+
};
|
|
893
|
+
}
|
|
816
894
|
if (preResult.decision === "deny") return persistOutput(this.blockDirectCall({
|
|
817
895
|
call,
|
|
818
896
|
resolvedArgs,
|
|
@@ -821,7 +899,15 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
821
899
|
runId,
|
|
822
900
|
threadId
|
|
823
901
|
}), effectiveCall.args);
|
|
824
|
-
if (preResult.decision
|
|
902
|
+
if (approvalEvidenceAppliesToBatch && reviewedApproval == null && !isTrustedSubagentReviewHop && preResult.decision !== "ask") return persistOutput(this.blockDirectCall({
|
|
903
|
+
call,
|
|
904
|
+
resolvedArgs,
|
|
905
|
+
reason: "Tool call was not included in the restored approval batch — failing closed",
|
|
906
|
+
hookRegistry,
|
|
907
|
+
runId,
|
|
908
|
+
threadId
|
|
909
|
+
}), resolvedArgs);
|
|
910
|
+
if (preResult.decision === "ask" || reviewedApproval != null) {
|
|
825
911
|
if (this.humanInTheLoop?.enabled !== true) {
|
|
826
912
|
const reason = this.resolveAskDecisionForDirectTool(preResult.reason, call.name);
|
|
827
913
|
return persistOutput(this.blockDirectCall({
|
|
@@ -842,26 +928,41 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
842
928
|
runId,
|
|
843
929
|
threadId
|
|
844
930
|
}), effectiveCall.args);
|
|
931
|
+
const allowedDecisions = preResult.decision === "ask" ? preResult.allowedDecisions : reviewedApproval?.reviewConfig.allowed_decisions;
|
|
932
|
+
const askReason = preResult.decision === "ask" ? preResult.reason : reviewedApproval?.request.description;
|
|
933
|
+
if (effectiveCall === call) effectiveCall = {
|
|
934
|
+
...call,
|
|
935
|
+
args: resolvedArgs
|
|
936
|
+
};
|
|
845
937
|
const payload = buildToolApprovalInterruptPayload([{
|
|
846
938
|
entry: {
|
|
847
939
|
call: effectiveCall,
|
|
848
940
|
args: effectiveCall.args,
|
|
849
941
|
stepId
|
|
850
942
|
},
|
|
851
|
-
reason:
|
|
852
|
-
allowedDecisions
|
|
943
|
+
reason: askReason,
|
|
944
|
+
allowedDecisions
|
|
853
945
|
}], runId);
|
|
854
946
|
const resumeValue = _langchain_core_singletons.AsyncLocalStorageProviderSingleton.runWithConfig(config, () => (0, _langchain_langgraph.interrupt)(payload));
|
|
855
|
-
hookRegistry
|
|
947
|
+
hookRegistry?.clearPendingToolApproval(approvalReplaySessionId, approvalReplayKey);
|
|
948
|
+
const currentApproval = require_approvalReview.getReviewedToolApproval(payload, toolCallId);
|
|
949
|
+
if (reviewedApproval != null && (currentApproval == null || !require_approvalReview.toolApprovalProposalMatches(currentApproval, reviewedApproval))) return persistOutput(this.blockDirectCall({
|
|
950
|
+
call,
|
|
951
|
+
resolvedArgs,
|
|
952
|
+
reason: "Reviewed tool proposal changed before resume; retry approval",
|
|
953
|
+
hookRegistry,
|
|
954
|
+
runId,
|
|
955
|
+
threadId
|
|
956
|
+
}), effectiveCall.args);
|
|
856
957
|
const decision = normalizeApprovalDecisions([toolCallId], resumeValue).get(toolCallId) ?? {
|
|
857
958
|
type: "reject",
|
|
858
959
|
reason: "No decision provided for tool approval"
|
|
859
960
|
};
|
|
860
961
|
const declaredType = decision.type;
|
|
861
|
-
if (
|
|
962
|
+
if (allowedDecisions != null && (typeof declaredType !== "string" || !allowedDecisions.includes(declaredType))) return persistOutput(this.blockDirectCall({
|
|
862
963
|
call,
|
|
863
964
|
resolvedArgs,
|
|
864
|
-
reason: `Decision "${typeof declaredType === "string" ? declaredType : "<missing>"}" not in allowedDecisions [${
|
|
965
|
+
reason: `Decision "${typeof declaredType === "string" ? declaredType : "<missing>"}" not in allowedDecisions [${allowedDecisions.join(", ")}] — failing closed`,
|
|
865
966
|
hookRegistry,
|
|
866
967
|
runId,
|
|
867
968
|
threadId
|
|
@@ -869,7 +970,7 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
869
970
|
if (decision.type === "reject") return persistOutput(this.blockDirectCall({
|
|
870
971
|
call,
|
|
871
972
|
resolvedArgs,
|
|
872
|
-
reason: decision.reason ??
|
|
973
|
+
reason: decision.reason ?? askReason ?? "Rejected by user",
|
|
873
974
|
hookRegistry,
|
|
874
975
|
runId,
|
|
875
976
|
threadId
|
|
@@ -997,7 +1098,7 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
997
1098
|
}
|
|
998
1099
|
blockDirectCall(args) {
|
|
999
1100
|
const { call, resolvedArgs, reason, hookRegistry, runId, threadId } = args;
|
|
1000
|
-
if (hookRegistry
|
|
1101
|
+
if (hookRegistry?.hasHookFor("PermissionDenied", runId) === true) require_executeHooks.executeHooks({
|
|
1001
1102
|
registry: hookRegistry,
|
|
1002
1103
|
input: {
|
|
1003
1104
|
hook_event_name: "PermissionDenied",
|
|
@@ -1126,7 +1227,8 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1126
1227
|
resolvedArgs = pre.resolved;
|
|
1127
1228
|
if (pre.unresolved.length > 0 && call.id != null) unresolvedByCallId.set(call.id, pre.unresolved);
|
|
1128
1229
|
} else if (registry != null) {
|
|
1129
|
-
const
|
|
1230
|
+
const options = { substituteIntentKey: this.toolDeclaresBusinessIntent(call.name) };
|
|
1231
|
+
const { resolved, unresolved } = preBatchSnapshot != null ? preBatchSnapshot.resolve(originalArgs, options) : registry.resolve(registryRunId, originalArgs, options);
|
|
1130
1232
|
resolvedArgs = resolved;
|
|
1131
1233
|
if (unresolved.length > 0 && call.id != null) unresolvedByCallId.set(call.id, unresolved);
|
|
1132
1234
|
}
|
|
@@ -1148,8 +1250,9 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1148
1250
|
});
|
|
1149
1251
|
const hookRegistry = this.hookRegistry;
|
|
1150
1252
|
const approvalReplaySessionId = getToolApprovalReplaySessionId(config, runId);
|
|
1253
|
+
const approvalReviewEvidence = require_approvalReview.getToolApprovalReviewEvidence(config, require_toolBatchReplay.getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? ""));
|
|
1151
1254
|
const hasPendingApproval = preToolCalls.some((entry) => entry.call.id != null && hookRegistry?.getPendingToolApproval(approvalReplaySessionId, createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", entry.call.id)) != null);
|
|
1152
|
-
if (hookRegistry
|
|
1255
|
+
if (hookRegistry?.hasHookFor("PreToolUse", runId) === true || hasPendingApproval || approvalReviewEvidence != null) {
|
|
1153
1256
|
const preemptedEagerRecords = /* @__PURE__ */ new Map();
|
|
1154
1257
|
if (this.eagerEventToolExecutions != null) for (const entry of preToolCalls) {
|
|
1155
1258
|
const callId = entry.call.id;
|
|
@@ -1160,7 +1263,7 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1160
1263
|
this.eagerEventToolExecutions.delete(callId);
|
|
1161
1264
|
}
|
|
1162
1265
|
}
|
|
1163
|
-
const preResults = await Promise.all(preToolCalls.map((entry) => {
|
|
1266
|
+
const preResults = hookRegistry == null ? preToolCalls.map(() => HOOK_FALLBACK) : await Promise.all(preToolCalls.map((entry) => {
|
|
1164
1267
|
const toolUseId = entry.call.id;
|
|
1165
1268
|
const approvalReplayKey = toolUseId == null ? void 0 : createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", toolUseId);
|
|
1166
1269
|
return require_executeHooks.executeHooks({
|
|
@@ -1213,7 +1316,7 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1213
1316
|
const flushDeferredBlockedSideEffects = async () => {
|
|
1214
1317
|
for (const item of deferredBlockedSideEffects) {
|
|
1215
1318
|
await this.dispatchStepCompleted(item.callId, item.toolName, item.args, item.contentString, config);
|
|
1216
|
-
if (hookRegistry
|
|
1319
|
+
if (hookRegistry?.hasHookFor("PermissionDenied", runId) === true) require_executeHooks.executeHooks({
|
|
1217
1320
|
registry: hookRegistry,
|
|
1218
1321
|
input: {
|
|
1219
1322
|
hook_event_name: "PermissionDenied",
|
|
@@ -1257,6 +1360,13 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1257
1360
|
continue;
|
|
1258
1361
|
}
|
|
1259
1362
|
if (hookResult.updatedInput != null) applyInputOverride(entry, hookResult.updatedInput);
|
|
1363
|
+
else {
|
|
1364
|
+
const reviewed = require_approvalReview.getReviewedToolApproval(approvalReviewEvidence?.payload, entry.call.id);
|
|
1365
|
+
if (reviewed != null) {
|
|
1366
|
+
entry.args = this.coerceEventToolArgs(entry.call.name, reviewed.request.arguments);
|
|
1367
|
+
if (entry.call.id != null) unresolvedByCallId.delete(entry.call.id);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1260
1370
|
askEntries.push({
|
|
1261
1371
|
entry,
|
|
1262
1372
|
reason: hookResult.reason,
|
|
@@ -1265,6 +1375,19 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1265
1375
|
continue;
|
|
1266
1376
|
}
|
|
1267
1377
|
if (hookResult.updatedInput != null) applyInputOverride(entry, hookResult.updatedInput);
|
|
1378
|
+
const reviewedApproval = require_approvalReview.getReviewedToolApproval(approvalReviewEvidence?.payload, entry.call.id);
|
|
1379
|
+
if (reviewedApproval != null) {
|
|
1380
|
+
if (hookResult.updatedInput == null) {
|
|
1381
|
+
entry.args = this.coerceEventToolArgs(entry.call.name, reviewedApproval.request.arguments);
|
|
1382
|
+
if (entry.call.id != null) unresolvedByCallId.delete(entry.call.id);
|
|
1383
|
+
}
|
|
1384
|
+
askEntries.push({
|
|
1385
|
+
entry,
|
|
1386
|
+
reason: reviewedApproval.request.description,
|
|
1387
|
+
allowedDecisions: reviewedApproval.reviewConfig.allowed_decisions
|
|
1388
|
+
});
|
|
1389
|
+
continue;
|
|
1390
|
+
}
|
|
1268
1391
|
if (entry.call.id != null) {
|
|
1269
1392
|
const preempted = preemptedEagerRecords.get(entry.call.id);
|
|
1270
1393
|
if (preempted != null) this.eagerEventToolExecutions?.set(entry.call.id, preempted);
|
|
@@ -1274,9 +1397,12 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1274
1397
|
if (askEntries.length > 0) {
|
|
1275
1398
|
const payload = buildToolApprovalInterruptPayload(askEntries, runId);
|
|
1276
1399
|
const resumeValue = _langchain_core_singletons.AsyncLocalStorageProviderSingleton.runWithConfig(config, () => (0, _langchain_langgraph.interrupt)(payload));
|
|
1277
|
-
for (const { entry } of askEntries) hookRegistry
|
|
1278
|
-
const
|
|
1279
|
-
for (const { entry
|
|
1400
|
+
for (const { entry } of askEntries) hookRegistry?.clearPendingToolApproval(approvalReplaySessionId, createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", entry.call.id));
|
|
1401
|
+
const proposalMatches = approvalReviewEvidence == null || require_approvalReview.toolApprovalPayloadMatches(payload, approvalReviewEvidence.payload);
|
|
1402
|
+
if (!proposalMatches) for (const { entry } of askEntries) blockEntry(entry, "Reviewed tool proposal changed before resume; retry approval");
|
|
1403
|
+
const decisionEntries = proposalMatches ? askEntries : [];
|
|
1404
|
+
const decisionByCallId = normalizeApprovalDecisions(decisionEntries.map(({ entry }) => entry.call.id), resumeValue);
|
|
1405
|
+
for (const { entry, reason: askReason, allowedDecisions } of decisionEntries) {
|
|
1280
1406
|
const decision = decisionByCallId.get(entry.call.id) ?? {
|
|
1281
1407
|
type: "reject",
|
|
1282
1408
|
reason: "No decision provided for tool approval"
|
|
@@ -1402,7 +1528,7 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1402
1528
|
userId: config.configurable?.user_id,
|
|
1403
1529
|
agentId: this.executingAgentId,
|
|
1404
1530
|
callerCapabilityProjection: this.getCallerCapabilityProjectionSnapshot(),
|
|
1405
|
-
configurable:
|
|
1531
|
+
configurable: stripHostExecutionConfig(config.configurable),
|
|
1406
1532
|
metadata: config.metadata,
|
|
1407
1533
|
signal: config.signal,
|
|
1408
1534
|
resolve: (results) => {
|
|
@@ -1661,15 +1787,39 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1661
1787
|
return this.dispatchStepCompleted(result.toolCallId, request.name, request.args, output, config, request.turn, require_intentArg.resolveToolOutcome(request.args, require_intentArg.outcomeFieldsFromResult(result), { isError: result.status === "error" }));
|
|
1662
1788
|
}
|
|
1663
1789
|
async runDirectBatchInterruptSafe(directCalls, batchIndices, config, baseContext) {
|
|
1664
|
-
const settledBatchResults = baseContext.replayBatchKey == null ? void 0 : this.
|
|
1790
|
+
const settledBatchResults = baseContext.replayBatchKey == null ? void 0 : this.settledDirectResultsByBatch.get(baseContext.replayBatchKey) ?? /* @__PURE__ */ new Map();
|
|
1791
|
+
const cacheResult = (call, position, result) => {
|
|
1792
|
+
if (baseContext.replayBatchKey == null || settledBatchResults == null) return;
|
|
1793
|
+
settledBatchResults.set(directReplayKey(call, baseContext.replayCallPositions?.get(call) ?? batchIndices[position]), result);
|
|
1794
|
+
this.settledDirectResultsByBatch.set(baseContext.replayBatchKey, settledBatchResults);
|
|
1795
|
+
};
|
|
1665
1796
|
const restoreResult = (call, result) => {
|
|
1666
1797
|
baseContext.additionalContextsSink?.push(...result.additionalContexts);
|
|
1798
|
+
const turn = result.turn;
|
|
1799
|
+
if (turn != null) this.recordToolUsageTurn(call.name, turn, call.id);
|
|
1800
|
+
const refMeta = (0, _langchain_core_messages.isBaseMessage)(result.output) ? result.output.additional_kwargs : void 0;
|
|
1801
|
+
if (refMeta?._refKey != null && result.referenceContent != null) {
|
|
1802
|
+
this.toolOutputRegistry?.set(baseContext.batchScopeId, refMeta._refKey, result.referenceContent);
|
|
1803
|
+
if ((0, _langchain_core_messages.isBaseMessage)(result.output)) result = {
|
|
1804
|
+
...result,
|
|
1805
|
+
output: new _langchain_core_messages.ToolMessage({
|
|
1806
|
+
...result.output,
|
|
1807
|
+
additional_kwargs: {
|
|
1808
|
+
...result.output.additional_kwargs,
|
|
1809
|
+
_refScope: baseContext.batchScopeId
|
|
1810
|
+
}
|
|
1811
|
+
})
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1667
1814
|
if (call.id != null && result.resolvedArgs != null && baseContext.resolvedArgsByCallId != null) baseContext.resolvedArgsByCallId.set(call.id, result.resolvedArgs);
|
|
1668
1815
|
return result;
|
|
1669
1816
|
};
|
|
1670
1817
|
const runOne = async (call, position) => {
|
|
1671
|
-
const cachedResult =
|
|
1672
|
-
if (cachedResult != null)
|
|
1818
|
+
const cachedResult = settledBatchResults?.get(directReplayKey(call, baseContext.replayCallPositions?.get(call) ?? batchIndices[position]));
|
|
1819
|
+
if (cachedResult != null) {
|
|
1820
|
+
if (cachedResult.proposal.name !== call.name || !require_eagerEventExecution.recordArgsEqual(cachedResult.proposal.args, call.args)) throw new Error("Cannot change an already completed tool call during approval replay");
|
|
1821
|
+
return restoreResult(call, cachedResult);
|
|
1822
|
+
}
|
|
1673
1823
|
const additionalContexts = [];
|
|
1674
1824
|
const output = await this.runDirectToolWithLifecycleHooks(call, config, {
|
|
1675
1825
|
...baseContext,
|
|
@@ -1677,14 +1827,32 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1677
1827
|
additionalContextsSink: additionalContexts
|
|
1678
1828
|
});
|
|
1679
1829
|
const resolvedArgs = call.id == null ? void 0 : baseContext.resolvedArgsByCallId?.get(call.id);
|
|
1680
|
-
|
|
1830
|
+
const result = {
|
|
1831
|
+
proposal: structuredClone({
|
|
1832
|
+
name: call.name,
|
|
1833
|
+
args: call.args
|
|
1834
|
+
}),
|
|
1835
|
+
turn: call.id == null ? void 0 : this.toolCallTurns.get(call.id) ?? this.directPathTurns.get(call.id),
|
|
1681
1836
|
output,
|
|
1682
1837
|
additionalContexts,
|
|
1683
1838
|
...resolvedArgs == null ? {} : { resolvedArgs }
|
|
1684
|
-
}
|
|
1839
|
+
};
|
|
1840
|
+
const refMeta = (0, _langchain_core_messages.isBaseMessage)(output) ? output.additional_kwargs : void 0;
|
|
1841
|
+
if (refMeta?._refKey != null) result.referenceContent = this.toolOutputRegistry?.get(refMeta._refScope, refMeta._refKey);
|
|
1842
|
+
cacheResult(call, position, result);
|
|
1843
|
+
return restoreResult(call, result);
|
|
1685
1844
|
};
|
|
1686
1845
|
const interrupting = this.interruptingToolNames;
|
|
1687
|
-
if (!(interrupting != null && directCalls.some((call) => interrupting.has(call.name))))
|
|
1846
|
+
if (!(interrupting != null && directCalls.some((call) => interrupting.has(call.name)))) {
|
|
1847
|
+
const results = await Promise.allSettled(directCalls.map((call, i) => runOne(call, i)));
|
|
1848
|
+
this.throwIfBreakerTripped(config);
|
|
1849
|
+
const failed = results.find((result) => result.status === "rejected" && !(0, _langchain_langgraph.isGraphInterrupt)(result.reason)) ?? results.find((result) => result.status === "rejected");
|
|
1850
|
+
if (failed?.status === "rejected") throw failed.reason;
|
|
1851
|
+
return results.map((result) => {
|
|
1852
|
+
if (result.status === "rejected") throw result.reason;
|
|
1853
|
+
return result.value.output;
|
|
1854
|
+
});
|
|
1855
|
+
}
|
|
1688
1856
|
const outputs = new Array(directCalls.length);
|
|
1689
1857
|
const interruptingPositions = [];
|
|
1690
1858
|
const regularPositions = [];
|
|
@@ -1705,12 +1873,6 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1705
1873
|
}
|
|
1706
1874
|
const position = interruptingPositions[i];
|
|
1707
1875
|
outputs[position] = result.value.output;
|
|
1708
|
-
const callId = directCalls[position].id;
|
|
1709
|
-
if (baseContext.replayBatchKey != null && typeof callId === "string" && callId !== "") {
|
|
1710
|
-
const batchResults = this.settledInterruptingResults.get(baseContext.replayBatchKey) ?? /* @__PURE__ */ new Map();
|
|
1711
|
-
batchResults.set(callId, result.value);
|
|
1712
|
-
this.settledInterruptingResults.set(baseContext.replayBatchKey, batchResults);
|
|
1713
|
-
}
|
|
1714
1876
|
}
|
|
1715
1877
|
if (hasInterruptingError) {
|
|
1716
1878
|
if ((0, _langchain_langgraph.isGraphInterrupt)(interruptingError)) {
|
|
@@ -1752,7 +1914,7 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1752
1914
|
const outputs = [...toolMessages, ...injected];
|
|
1753
1915
|
return Array.isArray(input) ? outputs : { messages: outputs };
|
|
1754
1916
|
}
|
|
1755
|
-
async run(input, config) {
|
|
1917
|
+
async run(input, config, referenceReplay = {}) {
|
|
1756
1918
|
const breakerSignal = this.getBreakerSignal?.();
|
|
1757
1919
|
const composedSignal = require_misc.composeAbortSignals(config.signal, breakerSignal);
|
|
1758
1920
|
const batchRunScope = this.getRunScope?.();
|
|
@@ -1768,10 +1930,11 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1768
1930
|
this.toolCallTurns.clear();
|
|
1769
1931
|
const resolvedArgsByCallId = /* @__PURE__ */ new Map();
|
|
1770
1932
|
const errorOwnership = createToolErrorOwnership();
|
|
1771
|
-
const
|
|
1772
|
-
const
|
|
1773
|
-
const
|
|
1774
|
-
|
|
1933
|
+
const batchScopeId = config.configurable?.run_id ?? `\0anon-${this.anonBatchCounter++}`;
|
|
1934
|
+
const resumedReferenceState = referenceReplay.state;
|
|
1935
|
+
const replayInputSnapshot = resumedReferenceState == null ? void 0 : this.toolOutputRegistry?.resumeBatch(batchScopeId, resumedReferenceState);
|
|
1936
|
+
referenceReplay.state = resumedReferenceState ?? this.toolOutputRegistry?.snapshotState(batchScopeId);
|
|
1937
|
+
const turn = resumedReferenceState?.turnCounter ?? this.toolOutputRegistry?.nextTurn(batchScopeId) ?? 0;
|
|
1775
1938
|
let outputs;
|
|
1776
1939
|
let replayBatchKey;
|
|
1777
1940
|
let promotedAiMessage;
|
|
@@ -1781,17 +1944,19 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1781
1944
|
if (this.eventDrivenMode && !isLocalTool) return this.executeViaEvent([input.lg_tool_call], config, input, {
|
|
1782
1945
|
batchIndices: [0],
|
|
1783
1946
|
turn,
|
|
1784
|
-
batchScopeId
|
|
1947
|
+
batchScopeId,
|
|
1948
|
+
preBatchSnapshot: replayInputSnapshot
|
|
1785
1949
|
});
|
|
1786
1950
|
const directAdditionalContexts = [];
|
|
1787
1951
|
const { lg_tool_call: _sendToolCall, ...sendState } = input;
|
|
1788
1952
|
const sendMessages = sendState.messages;
|
|
1789
1953
|
const sendAssistantBatch = Array.isArray(sendMessages) ? findAssistantBatch(sendMessages) : void 0;
|
|
1790
|
-
const sendReplayBatchKey = sendAssistantBatch == null ? void 0 : getAssistantBatchReplayKey(sendAssistantBatch,
|
|
1954
|
+
const sendReplayBatchKey = sendAssistantBatch == null || require_toolBatchReplay.getToolBatchReplayScope(config) == null ? void 0 : getAssistantBatchReplayKey(sendAssistantBatch, require_toolBatchReplay.getToolBatchReplayScope(config));
|
|
1791
1955
|
const sendOutput = await this.runDirectToolWithLifecycleHooks(input.lg_tool_call, config, {
|
|
1792
1956
|
batchIndex: 0,
|
|
1793
1957
|
turn,
|
|
1794
1958
|
batchScopeId,
|
|
1959
|
+
preBatchSnapshot: replayInputSnapshot,
|
|
1795
1960
|
resolvedArgsByCallId,
|
|
1796
1961
|
errorOwnership,
|
|
1797
1962
|
additionalContextsSink: directAdditionalContexts,
|
|
@@ -1815,14 +1980,15 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1815
1980
|
const assistantBatch = findAssistantBatch(messages);
|
|
1816
1981
|
if (assistantBatch == null) throw new Error("ToolNode only accepts AIMessages as input.");
|
|
1817
1982
|
const aiMessage = assistantBatch.message;
|
|
1818
|
-
replayBatchKey = getAssistantBatchReplayKey(assistantBatch,
|
|
1983
|
+
replayBatchKey = require_toolBatchReplay.getToolBatchReplayScope(config) == null ? void 0 : getAssistantBatchReplayKey(assistantBatch, require_toolBatchReplay.getToolBatchReplayScope(config));
|
|
1819
1984
|
if (this.loadRuntimeTools) {
|
|
1820
1985
|
const { tools, toolMap } = this.loadRuntimeTools(aiMessage.tool_calls ?? []);
|
|
1821
1986
|
this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
|
|
1822
1987
|
this.applyToolExecutionOverrides();
|
|
1823
1988
|
}
|
|
1989
|
+
const replayCallPositions = new Map((aiMessage.tool_calls ?? []).map((call, i) => [call, i]));
|
|
1824
1990
|
const filteredCalls = aiMessage.tool_calls?.filter((call) => {
|
|
1825
|
-
return (call.id == null || !toolMessageIds.has(call.id)) && !(call.id?.startsWith("srvtoolu_") ?? false);
|
|
1991
|
+
return (call.id == null || call.id === "" || !toolMessageIds.has(call.id)) && !(call.id?.startsWith("srvtoolu_") ?? false);
|
|
1826
1992
|
}) ?? [];
|
|
1827
1993
|
const attributableInvalidCalls = !(!Array.isArray(input) && typeof aiMessage.id === "string" && aiMessage.id.length > 0) ? [] : (aiMessage.invalid_tool_calls ?? []).filter((call) => call.id != null && call.id !== "" && !toolMessageIds.has(call.id) && !call.id.startsWith("srvtoolu_"));
|
|
1828
1994
|
invalidCallResults = attributableInvalidCalls.map((call) => new _langchain_core_messages.ToolMessage({
|
|
@@ -1864,14 +2030,16 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1864
2030
|
if (directEntries.length === 0 && invalidCallResults.length === 0) return this.executeViaEvent(filteredCalls, config, input, {
|
|
1865
2031
|
batchIndices: eventEntries.map((entry) => entry.batchIndex),
|
|
1866
2032
|
turn,
|
|
1867
|
-
batchScopeId
|
|
2033
|
+
batchScopeId,
|
|
2034
|
+
preBatchSnapshot: replayInputSnapshot
|
|
1868
2035
|
});
|
|
1869
2036
|
const directCalls = directEntries.map((e) => e.call);
|
|
1870
2037
|
const directIndices = directEntries.map((e) => e.batchIndex);
|
|
1871
2038
|
const eventCalls = eventEntries.map((e) => e.call);
|
|
1872
2039
|
const eventIndices = eventEntries.map((e) => e.batchIndex);
|
|
2040
|
+
const approvalScopeCallIds = new Set(filteredCalls.flatMap((call) => call.id == null ? [] : [call.id]));
|
|
1873
2041
|
const preResolvedEventArgs = /* @__PURE__ */ new Map();
|
|
1874
|
-
const preBatchSnapshot = this.toolOutputRegistry?.snapshot(batchScopeId);
|
|
2042
|
+
const preBatchSnapshot = replayInputSnapshot ?? this.toolOutputRegistry?.snapshot(batchScopeId);
|
|
1875
2043
|
if (preBatchSnapshot != null) {
|
|
1876
2044
|
for (const entry of eventEntries) if (entry.call.id != null) {
|
|
1877
2045
|
const { resolved, unresolved } = preBatchSnapshot.resolve(entry.call.args, { substituteIntentKey: this.toolDeclaresBusinessIntent(entry.call.name) });
|
|
@@ -1890,9 +2058,25 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1890
2058
|
preBatchSnapshot,
|
|
1891
2059
|
additionalContextsSink: directAdditionalContexts,
|
|
1892
2060
|
replayBatchKey,
|
|
2061
|
+
replayCallPositions,
|
|
2062
|
+
approvalScopeCallIds,
|
|
1893
2063
|
runInput: input
|
|
1894
2064
|
}) : [];
|
|
1895
|
-
|
|
2065
|
+
const settledDirectResults = replayBatchKey == null ? void 0 : this.settledDirectResultsByBatch.get(replayBatchKey);
|
|
2066
|
+
const unhandledDirectCalls = [];
|
|
2067
|
+
const unhandledDirectOutputs = [];
|
|
2068
|
+
for (let i = 0; i < directCalls.length; i++) {
|
|
2069
|
+
if ((settledDirectResults?.get(directReplayKey(directCalls[i], replayCallPositions.get(directCalls[i]))))?.completionHandled === true) continue;
|
|
2070
|
+
unhandledDirectCalls.push(directCalls[i]);
|
|
2071
|
+
unhandledDirectOutputs.push(directOutputs[i]);
|
|
2072
|
+
}
|
|
2073
|
+
if (unhandledDirectCalls.length > 0) {
|
|
2074
|
+
await this.handleRunToolCompletions(unhandledDirectCalls, unhandledDirectOutputs, config, resolvedArgsByCallId, errorOwnership);
|
|
2075
|
+
for (let i = 0; i < directCalls.length; i++) {
|
|
2076
|
+
const settled = settledDirectResults?.get(directReplayKey(directCalls[i], replayCallPositions.get(directCalls[i])));
|
|
2077
|
+
if (settled != null) settled.completionHandled = true;
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
1896
2080
|
const eventResult = eventCalls.length > 0 ? await this.dispatchToolEvents(eventCalls, config, {
|
|
1897
2081
|
batchIndices: eventIndices,
|
|
1898
2082
|
turn,
|
|
@@ -1919,8 +2103,9 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1919
2103
|
...eventResult.injected
|
|
1920
2104
|
];
|
|
1921
2105
|
} else {
|
|
1922
|
-
const preBatchSnapshot = this.toolOutputRegistry?.snapshot(batchScopeId);
|
|
2106
|
+
const preBatchSnapshot = replayInputSnapshot ?? this.toolOutputRegistry?.snapshot(batchScopeId);
|
|
1923
2107
|
const directAdditionalContexts = [];
|
|
2108
|
+
const approvalScopeCallIds = new Set(filteredCalls.flatMap((call) => call.id == null ? [] : [call.id]));
|
|
1924
2109
|
const toolOutputs = await this.runDirectBatchInterruptSafe(filteredCalls, filteredCalls.map((_call, i) => i), config, {
|
|
1925
2110
|
turn,
|
|
1926
2111
|
batchScopeId,
|
|
@@ -1929,6 +2114,8 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1929
2114
|
preBatchSnapshot,
|
|
1930
2115
|
additionalContextsSink: directAdditionalContexts,
|
|
1931
2116
|
replayBatchKey,
|
|
2117
|
+
replayCallPositions,
|
|
2118
|
+
approvalScopeCallIds,
|
|
1932
2119
|
runInput: input
|
|
1933
2120
|
});
|
|
1934
2121
|
await this.handleRunToolCompletions(filteredCalls, toolOutputs, config, resolvedArgsByCallId, errorOwnership);
|
|
@@ -1957,7 +2144,7 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
1957
2144
|
}
|
|
1958
2145
|
}
|
|
1959
2146
|
if (!outputs.some(_langchain_langgraph.isCommand)) {
|
|
1960
|
-
if (replayBatchKey != null) this.
|
|
2147
|
+
if (replayBatchKey != null) this.settledDirectResultsByBatch.delete(replayBatchKey);
|
|
1961
2148
|
return Array.isArray(input) ? outputs : { messages: outputs };
|
|
1962
2149
|
}
|
|
1963
2150
|
if (promotedAiMessage != null) outputs = outputs.map((output) => (0, _langchain_langgraph.isCommand)(output) ? patchCommandUpdateForPromotedInvalidCalls(output, promotedAiMessage, invalidCallResults) : output);
|
|
@@ -2007,7 +2194,7 @@ var ToolNode = class extends require_run.RunnableCallable {
|
|
|
2007
2194
|
combinedOutputs.push(parallelCommand);
|
|
2008
2195
|
} else if (handoffCommands.length === 1) combinedOutputs.push(handoffCommands[0]);
|
|
2009
2196
|
if (parentCommand) combinedOutputs.push(parentCommand);
|
|
2010
|
-
if (replayBatchKey != null) this.
|
|
2197
|
+
if (replayBatchKey != null) this.settledDirectResultsByBatch.delete(replayBatchKey);
|
|
2011
2198
|
return combinedOutputs;
|
|
2012
2199
|
}
|
|
2013
2200
|
isSendInput(input) {
|