@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
|
-
import { calculateMaxToolResultChars, truncateToolResultContent } from "../utils/truncation.mjs";
|
|
2
1
|
import { CODE_EXECUTION_TOOLS } from "../common/enum.mjs";
|
|
3
2
|
import "../common/index.mjs";
|
|
3
|
+
import { calculateMaxToolResultChars, truncateToolResultContent } from "../utils/truncation.mjs";
|
|
4
4
|
import { attachRunStepResumeState } from "./runStepResume.mjs";
|
|
5
5
|
import { SUBAGENT_PARENT_BATCH_CONFIG_KEY, SUBAGENT_REPLAY_CONTROLLER, attachSubagentResumeManifest } from "./subagent/SubagentReplay.mjs";
|
|
6
6
|
import { composeAbortSignals } from "../utils/misc.mjs";
|
|
7
7
|
import { resolveLangfuseRuntimeScope, withLangfuseRuntimeScope } from "../langfuseRuntimeScope.mjs";
|
|
8
8
|
import { cloneToolMessageWithContent, compactToolContent, hasComputerCallOutputMarker, isComputerCallOutputContent, isComputerCallOutputMessage, serializeStructuredValueBounded, serializeToolContentBounded } from "../utils/toolContent.mjs";
|
|
9
9
|
import { stampSyntheticProviderMessage } from "../messages/provenance.mjs";
|
|
10
|
+
import { buildToolExecutionRequestPlan, coerceArgsForSchema, coerceRecordArgs, recordArgsEqual, resolveRuntimeSessionHint, stableStringify } from "./eagerEventExecution.mjs";
|
|
11
|
+
import { TOOL_APPROVAL_REVIEW_CONFIG_KEY, getReviewedToolApproval, getToolApprovalReviewEvidence, toolApprovalPayloadMatches, toolApprovalProposalMatches } from "../hitl/approvalReview.mjs";
|
|
10
12
|
import { TOOL_APPROVAL_EXECUTION_SCOPE_CONFIG_KEY } from "../hooks/types.mjs";
|
|
13
|
+
import { TOOL_BATCH_REPLAY_KEY, attachToolBatchReplayState, getToolBatchReplayOwner, getToolBatchReplayScope, getToolBatchReplayState, isChildToolReplayOwner, isToolReplayResume, restoreToolBatchReplayState } from "./toolBatchReplay.mjs";
|
|
11
14
|
import { executeHooks } from "../hooks/executeHooks.mjs";
|
|
12
15
|
import "../hooks/index.mjs";
|
|
13
16
|
import { convertInjectedMessages } from "../messages/injected.mjs";
|
|
14
17
|
import { safeDispatchCustomEvent } from "../utils/events.mjs";
|
|
15
18
|
import { RUN_BREAKER_SCOPE_CONFIG_KEY, StreamLimitExceededError } from "../llm/streamLimits.mjs";
|
|
16
|
-
import { buildToolExecutionRequestPlan, coerceArgsForSchema, coerceRecordArgs, recordArgsEqual, resolveRuntimeSessionHint } from "./eagerEventExecution.mjs";
|
|
17
19
|
import { INTENT_ARG, isIntentLabelProperty, outcomeFieldsFromResult, readOutcomeFields, resolveToolOutcome } from "./intentArg.mjs";
|
|
18
20
|
import { ToolOutputReferenceRegistry, buildReferenceKey } from "./toolOutputReferences.mjs";
|
|
19
21
|
import { PreparedSubagentError } from "./preparedSubagents.mjs";
|
|
@@ -28,10 +30,17 @@ import { RunnableLambda } from "@langchain/core/runnables";
|
|
|
28
30
|
import { AIMessage, HumanMessage, ToolMessage, isAIMessage, isBaseMessage } from "@langchain/core/messages";
|
|
29
31
|
import { Command, END, GraphInterrupt, Send, interrupt, isCommand, isGraphInterrupt } from "@langchain/langgraph";
|
|
30
32
|
import { AsyncLocalStorageProviderSingleton } from "@langchain/core/singletons";
|
|
33
|
+
import { createHash } from "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 { [TOOL_APPROVAL_REVIEW_CONFIG_KEY]: _review, [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 { [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 (isAIMessage(message)) return {
|
|
117
|
-
message,
|
|
118
|
-
index: i,
|
|
119
|
-
messageCount: messages.length
|
|
120
|
-
};
|
|
128
|
+
if (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
|
+
createHash("sha256").update(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 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 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 || getToolBatchReplayScope(config) == null ? void 0 : getAssistantBatchReplayKey(assistantBatch, getToolBatchReplayScope(config));
|
|
291
|
+
const replayOwner = getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? "");
|
|
292
|
+
let reviewEvidence = getToolApprovalReviewEvidence(config);
|
|
293
|
+
const replayState = 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)?.[SUBAGENT_REPLAY_CONTROLLER] != null).map((call) => call.id ?? ""));
|
|
299
|
+
isChildReplay = reviewEvidence?.owner != null ? isChildToolReplayOwner(config, reviewEvidence.owner, trustedParentCallIds) : replayState.records.some((record) => isChildToolReplayOwner(config, record.owner, trustedParentCallIds));
|
|
300
|
+
}
|
|
301
|
+
if ((reviewEvidence?.owner != null || replayState != null) && !isToolReplayResume(config, reviewEvidence?.interruptId ?? replayState?.interruptId, isChildReplay)) {
|
|
302
|
+
config = {
|
|
303
|
+
...config,
|
|
304
|
+
configurable: {
|
|
305
|
+
...config.configurable,
|
|
306
|
+
[TOOL_APPROVAL_REVIEW_CONFIG_KEY]: void 0,
|
|
307
|
+
[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 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 && 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 (!isGraphInterrupt(error)
|
|
288
|
-
const resumeState = createRunStepResumeState();
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
328
|
+
if (!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 GraphInterrupt(await Promise.all(error.interrupts.map(async (pendingInterrupt) => {
|
|
336
|
+
const value = await attachToolBatchReplayState(pendingInterrupt.value, replayOwner, activeBatches, turnState, referenceReplay.state);
|
|
337
|
+
return {
|
|
338
|
+
...pendingInterrupt,
|
|
339
|
+
value: resumeState == null ? value : attachRunStepResumeState(value, resumeState)
|
|
340
|
+
};
|
|
292
341
|
})));
|
|
293
342
|
}
|
|
294
343
|
if (createRunStepResumeState == null) return result;
|
|
@@ -416,7 +465,7 @@ var ToolNode = class extends 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 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[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 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 RunnableCallable {
|
|
|
724
780
|
throw new PreparedSubagentError("Tool hooks changed after a subagent started.");
|
|
725
781
|
}
|
|
726
782
|
const replayController = this.toolMap.get(call.name)?.[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 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 = getToolApprovalReviewEvidence(config, getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? ""));
|
|
815
|
+
const reviewedApproval = 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 ToolMessage ? persistOutput(output) : output;
|
|
762
822
|
}
|
|
@@ -774,18 +834,21 @@ var ToolNode = class extends 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 executeHooks({
|
|
789
852
|
registry: hookRegistry,
|
|
790
853
|
input: {
|
|
791
854
|
hook_event_name: "PreToolUse",
|
|
@@ -803,16 +866,31 @@ var ToolNode = class extends 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 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 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 = AsyncLocalStorageProviderSingleton.runWithConfig(config, () => interrupt(payload));
|
|
855
|
-
hookRegistry
|
|
947
|
+
hookRegistry?.clearPendingToolApproval(approvalReplaySessionId, approvalReplayKey);
|
|
948
|
+
const currentApproval = getReviewedToolApproval(payload, toolCallId);
|
|
949
|
+
if (reviewedApproval != null && (currentApproval == null || !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 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 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) executeHooks({
|
|
1001
1102
|
registry: hookRegistry,
|
|
1002
1103
|
input: {
|
|
1003
1104
|
hook_event_name: "PermissionDenied",
|
|
@@ -1125,7 +1226,8 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1125
1226
|
resolvedArgs = pre.resolved;
|
|
1126
1227
|
if (pre.unresolved.length > 0 && call.id != null) unresolvedByCallId.set(call.id, pre.unresolved);
|
|
1127
1228
|
} else if (registry != null) {
|
|
1128
|
-
const
|
|
1229
|
+
const options = { substituteIntentKey: this.toolDeclaresBusinessIntent(call.name) };
|
|
1230
|
+
const { resolved, unresolved } = preBatchSnapshot != null ? preBatchSnapshot.resolve(originalArgs, options) : registry.resolve(registryRunId, originalArgs, options);
|
|
1129
1231
|
resolvedArgs = resolved;
|
|
1130
1232
|
if (unresolved.length > 0 && call.id != null) unresolvedByCallId.set(call.id, unresolved);
|
|
1131
1233
|
}
|
|
@@ -1147,8 +1249,9 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1147
1249
|
});
|
|
1148
1250
|
const hookRegistry = this.hookRegistry;
|
|
1149
1251
|
const approvalReplaySessionId = getToolApprovalReplaySessionId(config, runId);
|
|
1252
|
+
const approvalReviewEvidence = getToolApprovalReviewEvidence(config, getToolBatchReplayOwner(config, this.executingAgentId ?? this.agentId ?? ""));
|
|
1150
1253
|
const hasPendingApproval = preToolCalls.some((entry) => entry.call.id != null && hookRegistry?.getPendingToolApproval(approvalReplaySessionId, createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", entry.call.id)) != null);
|
|
1151
|
-
if (hookRegistry
|
|
1254
|
+
if (hookRegistry?.hasHookFor("PreToolUse", runId) === true || hasPendingApproval || approvalReviewEvidence != null) {
|
|
1152
1255
|
const preemptedEagerRecords = /* @__PURE__ */ new Map();
|
|
1153
1256
|
if (this.eagerEventToolExecutions != null) for (const entry of preToolCalls) {
|
|
1154
1257
|
const callId = entry.call.id;
|
|
@@ -1159,7 +1262,7 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1159
1262
|
this.eagerEventToolExecutions.delete(callId);
|
|
1160
1263
|
}
|
|
1161
1264
|
}
|
|
1162
|
-
const preResults = await Promise.all(preToolCalls.map((entry) => {
|
|
1265
|
+
const preResults = hookRegistry == null ? preToolCalls.map(() => HOOK_FALLBACK) : await Promise.all(preToolCalls.map((entry) => {
|
|
1163
1266
|
const toolUseId = entry.call.id;
|
|
1164
1267
|
const approvalReplayKey = toolUseId == null ? void 0 : createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", toolUseId);
|
|
1165
1268
|
return executeHooks({
|
|
@@ -1212,7 +1315,7 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1212
1315
|
const flushDeferredBlockedSideEffects = async () => {
|
|
1213
1316
|
for (const item of deferredBlockedSideEffects) {
|
|
1214
1317
|
await this.dispatchStepCompleted(item.callId, item.toolName, item.args, item.contentString, config);
|
|
1215
|
-
if (hookRegistry
|
|
1318
|
+
if (hookRegistry?.hasHookFor("PermissionDenied", runId) === true) executeHooks({
|
|
1216
1319
|
registry: hookRegistry,
|
|
1217
1320
|
input: {
|
|
1218
1321
|
hook_event_name: "PermissionDenied",
|
|
@@ -1256,6 +1359,13 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1256
1359
|
continue;
|
|
1257
1360
|
}
|
|
1258
1361
|
if (hookResult.updatedInput != null) applyInputOverride(entry, hookResult.updatedInput);
|
|
1362
|
+
else {
|
|
1363
|
+
const reviewed = getReviewedToolApproval(approvalReviewEvidence?.payload, entry.call.id);
|
|
1364
|
+
if (reviewed != null) {
|
|
1365
|
+
entry.args = this.coerceEventToolArgs(entry.call.name, reviewed.request.arguments);
|
|
1366
|
+
if (entry.call.id != null) unresolvedByCallId.delete(entry.call.id);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1259
1369
|
askEntries.push({
|
|
1260
1370
|
entry,
|
|
1261
1371
|
reason: hookResult.reason,
|
|
@@ -1264,6 +1374,19 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1264
1374
|
continue;
|
|
1265
1375
|
}
|
|
1266
1376
|
if (hookResult.updatedInput != null) applyInputOverride(entry, hookResult.updatedInput);
|
|
1377
|
+
const reviewedApproval = getReviewedToolApproval(approvalReviewEvidence?.payload, entry.call.id);
|
|
1378
|
+
if (reviewedApproval != null) {
|
|
1379
|
+
if (hookResult.updatedInput == null) {
|
|
1380
|
+
entry.args = this.coerceEventToolArgs(entry.call.name, reviewedApproval.request.arguments);
|
|
1381
|
+
if (entry.call.id != null) unresolvedByCallId.delete(entry.call.id);
|
|
1382
|
+
}
|
|
1383
|
+
askEntries.push({
|
|
1384
|
+
entry,
|
|
1385
|
+
reason: reviewedApproval.request.description,
|
|
1386
|
+
allowedDecisions: reviewedApproval.reviewConfig.allowed_decisions
|
|
1387
|
+
});
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1267
1390
|
if (entry.call.id != null) {
|
|
1268
1391
|
const preempted = preemptedEagerRecords.get(entry.call.id);
|
|
1269
1392
|
if (preempted != null) this.eagerEventToolExecutions?.set(entry.call.id, preempted);
|
|
@@ -1273,9 +1396,12 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1273
1396
|
if (askEntries.length > 0) {
|
|
1274
1397
|
const payload = buildToolApprovalInterruptPayload(askEntries, runId);
|
|
1275
1398
|
const resumeValue = AsyncLocalStorageProviderSingleton.runWithConfig(config, () => interrupt(payload));
|
|
1276
|
-
for (const { entry } of askEntries) hookRegistry
|
|
1277
|
-
const
|
|
1278
|
-
for (const { entry
|
|
1399
|
+
for (const { entry } of askEntries) hookRegistry?.clearPendingToolApproval(approvalReplaySessionId, createToolApprovalReplayKey(config, this.executingAgentId ?? this.agentId ?? "", entry.call.id));
|
|
1400
|
+
const proposalMatches = approvalReviewEvidence == null || toolApprovalPayloadMatches(payload, approvalReviewEvidence.payload);
|
|
1401
|
+
if (!proposalMatches) for (const { entry } of askEntries) blockEntry(entry, "Reviewed tool proposal changed before resume; retry approval");
|
|
1402
|
+
const decisionEntries = proposalMatches ? askEntries : [];
|
|
1403
|
+
const decisionByCallId = normalizeApprovalDecisions(decisionEntries.map(({ entry }) => entry.call.id), resumeValue);
|
|
1404
|
+
for (const { entry, reason: askReason, allowedDecisions } of decisionEntries) {
|
|
1279
1405
|
const decision = decisionByCallId.get(entry.call.id) ?? {
|
|
1280
1406
|
type: "reject",
|
|
1281
1407
|
reason: "No decision provided for tool approval"
|
|
@@ -1401,7 +1527,7 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1401
1527
|
userId: config.configurable?.user_id,
|
|
1402
1528
|
agentId: this.executingAgentId,
|
|
1403
1529
|
callerCapabilityProjection: this.getCallerCapabilityProjectionSnapshot(),
|
|
1404
|
-
configurable:
|
|
1530
|
+
configurable: stripHostExecutionConfig(config.configurable),
|
|
1405
1531
|
metadata: config.metadata,
|
|
1406
1532
|
signal: config.signal,
|
|
1407
1533
|
resolve: (results) => {
|
|
@@ -1659,15 +1785,39 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1659
1785
|
return this.dispatchStepCompleted(result.toolCallId, request.name, request.args, output, config, request.turn, resolveToolOutcome(request.args, outcomeFieldsFromResult(result), { isError: result.status === "error" }));
|
|
1660
1786
|
}
|
|
1661
1787
|
async runDirectBatchInterruptSafe(directCalls, batchIndices, config, baseContext) {
|
|
1662
|
-
const settledBatchResults = baseContext.replayBatchKey == null ? void 0 : this.
|
|
1788
|
+
const settledBatchResults = baseContext.replayBatchKey == null ? void 0 : this.settledDirectResultsByBatch.get(baseContext.replayBatchKey) ?? /* @__PURE__ */ new Map();
|
|
1789
|
+
const cacheResult = (call, position, result) => {
|
|
1790
|
+
if (baseContext.replayBatchKey == null || settledBatchResults == null) return;
|
|
1791
|
+
settledBatchResults.set(directReplayKey(call, baseContext.replayCallPositions?.get(call) ?? batchIndices[position]), result);
|
|
1792
|
+
this.settledDirectResultsByBatch.set(baseContext.replayBatchKey, settledBatchResults);
|
|
1793
|
+
};
|
|
1663
1794
|
const restoreResult = (call, result) => {
|
|
1664
1795
|
baseContext.additionalContextsSink?.push(...result.additionalContexts);
|
|
1796
|
+
const turn = result.turn;
|
|
1797
|
+
if (turn != null) this.recordToolUsageTurn(call.name, turn, call.id);
|
|
1798
|
+
const refMeta = isBaseMessage(result.output) ? result.output.additional_kwargs : void 0;
|
|
1799
|
+
if (refMeta?._refKey != null && result.referenceContent != null) {
|
|
1800
|
+
this.toolOutputRegistry?.set(baseContext.batchScopeId, refMeta._refKey, result.referenceContent);
|
|
1801
|
+
if (isBaseMessage(result.output)) result = {
|
|
1802
|
+
...result,
|
|
1803
|
+
output: new ToolMessage({
|
|
1804
|
+
...result.output,
|
|
1805
|
+
additional_kwargs: {
|
|
1806
|
+
...result.output.additional_kwargs,
|
|
1807
|
+
_refScope: baseContext.batchScopeId
|
|
1808
|
+
}
|
|
1809
|
+
})
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1665
1812
|
if (call.id != null && result.resolvedArgs != null && baseContext.resolvedArgsByCallId != null) baseContext.resolvedArgsByCallId.set(call.id, result.resolvedArgs);
|
|
1666
1813
|
return result;
|
|
1667
1814
|
};
|
|
1668
1815
|
const runOne = async (call, position) => {
|
|
1669
|
-
const cachedResult =
|
|
1670
|
-
if (cachedResult != null)
|
|
1816
|
+
const cachedResult = settledBatchResults?.get(directReplayKey(call, baseContext.replayCallPositions?.get(call) ?? batchIndices[position]));
|
|
1817
|
+
if (cachedResult != null) {
|
|
1818
|
+
if (cachedResult.proposal.name !== call.name || !recordArgsEqual(cachedResult.proposal.args, call.args)) throw new Error("Cannot change an already completed tool call during approval replay");
|
|
1819
|
+
return restoreResult(call, cachedResult);
|
|
1820
|
+
}
|
|
1671
1821
|
const additionalContexts = [];
|
|
1672
1822
|
const output = await this.runDirectToolWithLifecycleHooks(call, config, {
|
|
1673
1823
|
...baseContext,
|
|
@@ -1675,14 +1825,32 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1675
1825
|
additionalContextsSink: additionalContexts
|
|
1676
1826
|
});
|
|
1677
1827
|
const resolvedArgs = call.id == null ? void 0 : baseContext.resolvedArgsByCallId?.get(call.id);
|
|
1678
|
-
|
|
1828
|
+
const result = {
|
|
1829
|
+
proposal: structuredClone({
|
|
1830
|
+
name: call.name,
|
|
1831
|
+
args: call.args
|
|
1832
|
+
}),
|
|
1833
|
+
turn: call.id == null ? void 0 : this.toolCallTurns.get(call.id) ?? this.directPathTurns.get(call.id),
|
|
1679
1834
|
output,
|
|
1680
1835
|
additionalContexts,
|
|
1681
1836
|
...resolvedArgs == null ? {} : { resolvedArgs }
|
|
1682
|
-
}
|
|
1837
|
+
};
|
|
1838
|
+
const refMeta = isBaseMessage(output) ? output.additional_kwargs : void 0;
|
|
1839
|
+
if (refMeta?._refKey != null) result.referenceContent = this.toolOutputRegistry?.get(refMeta._refScope, refMeta._refKey);
|
|
1840
|
+
cacheResult(call, position, result);
|
|
1841
|
+
return restoreResult(call, result);
|
|
1683
1842
|
};
|
|
1684
1843
|
const interrupting = this.interruptingToolNames;
|
|
1685
|
-
if (!(interrupting != null && directCalls.some((call) => interrupting.has(call.name))))
|
|
1844
|
+
if (!(interrupting != null && directCalls.some((call) => interrupting.has(call.name)))) {
|
|
1845
|
+
const results = await Promise.allSettled(directCalls.map((call, i) => runOne(call, i)));
|
|
1846
|
+
this.throwIfBreakerTripped(config);
|
|
1847
|
+
const failed = results.find((result) => result.status === "rejected" && !isGraphInterrupt(result.reason)) ?? results.find((result) => result.status === "rejected");
|
|
1848
|
+
if (failed?.status === "rejected") throw failed.reason;
|
|
1849
|
+
return results.map((result) => {
|
|
1850
|
+
if (result.status === "rejected") throw result.reason;
|
|
1851
|
+
return result.value.output;
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1686
1854
|
const outputs = new Array(directCalls.length);
|
|
1687
1855
|
const interruptingPositions = [];
|
|
1688
1856
|
const regularPositions = [];
|
|
@@ -1703,12 +1871,6 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1703
1871
|
}
|
|
1704
1872
|
const position = interruptingPositions[i];
|
|
1705
1873
|
outputs[position] = result.value.output;
|
|
1706
|
-
const callId = directCalls[position].id;
|
|
1707
|
-
if (baseContext.replayBatchKey != null && typeof callId === "string" && callId !== "") {
|
|
1708
|
-
const batchResults = this.settledInterruptingResults.get(baseContext.replayBatchKey) ?? /* @__PURE__ */ new Map();
|
|
1709
|
-
batchResults.set(callId, result.value);
|
|
1710
|
-
this.settledInterruptingResults.set(baseContext.replayBatchKey, batchResults);
|
|
1711
|
-
}
|
|
1712
1874
|
}
|
|
1713
1875
|
if (hasInterruptingError) {
|
|
1714
1876
|
if (isGraphInterrupt(interruptingError)) {
|
|
@@ -1750,7 +1912,7 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1750
1912
|
const outputs = [...toolMessages, ...injected];
|
|
1751
1913
|
return Array.isArray(input) ? outputs : { messages: outputs };
|
|
1752
1914
|
}
|
|
1753
|
-
async run(input, config) {
|
|
1915
|
+
async run(input, config, referenceReplay = {}) {
|
|
1754
1916
|
const breakerSignal = this.getBreakerSignal?.();
|
|
1755
1917
|
const composedSignal = composeAbortSignals(config.signal, breakerSignal);
|
|
1756
1918
|
const batchRunScope = this.getRunScope?.();
|
|
@@ -1766,10 +1928,11 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1766
1928
|
this.toolCallTurns.clear();
|
|
1767
1929
|
const resolvedArgsByCallId = /* @__PURE__ */ new Map();
|
|
1768
1930
|
const errorOwnership = createToolErrorOwnership();
|
|
1769
|
-
const
|
|
1770
|
-
const
|
|
1771
|
-
const
|
|
1772
|
-
|
|
1931
|
+
const batchScopeId = config.configurable?.run_id ?? `\0anon-${this.anonBatchCounter++}`;
|
|
1932
|
+
const resumedReferenceState = referenceReplay.state;
|
|
1933
|
+
const replayInputSnapshot = resumedReferenceState == null ? void 0 : this.toolOutputRegistry?.resumeBatch(batchScopeId, resumedReferenceState);
|
|
1934
|
+
referenceReplay.state = resumedReferenceState ?? this.toolOutputRegistry?.snapshotState(batchScopeId);
|
|
1935
|
+
const turn = resumedReferenceState?.turnCounter ?? this.toolOutputRegistry?.nextTurn(batchScopeId) ?? 0;
|
|
1773
1936
|
let outputs;
|
|
1774
1937
|
let replayBatchKey;
|
|
1775
1938
|
let promotedAiMessage;
|
|
@@ -1779,17 +1942,19 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1779
1942
|
if (this.eventDrivenMode && !isLocalTool) return this.executeViaEvent([input.lg_tool_call], config, input, {
|
|
1780
1943
|
batchIndices: [0],
|
|
1781
1944
|
turn,
|
|
1782
|
-
batchScopeId
|
|
1945
|
+
batchScopeId,
|
|
1946
|
+
preBatchSnapshot: replayInputSnapshot
|
|
1783
1947
|
});
|
|
1784
1948
|
const directAdditionalContexts = [];
|
|
1785
1949
|
const { lg_tool_call: _sendToolCall, ...sendState } = input;
|
|
1786
1950
|
const sendMessages = sendState.messages;
|
|
1787
1951
|
const sendAssistantBatch = Array.isArray(sendMessages) ? findAssistantBatch(sendMessages) : void 0;
|
|
1788
|
-
const sendReplayBatchKey = sendAssistantBatch == null ? void 0 : getAssistantBatchReplayKey(sendAssistantBatch,
|
|
1952
|
+
const sendReplayBatchKey = sendAssistantBatch == null || getToolBatchReplayScope(config) == null ? void 0 : getAssistantBatchReplayKey(sendAssistantBatch, getToolBatchReplayScope(config));
|
|
1789
1953
|
const sendOutput = await this.runDirectToolWithLifecycleHooks(input.lg_tool_call, config, {
|
|
1790
1954
|
batchIndex: 0,
|
|
1791
1955
|
turn,
|
|
1792
1956
|
batchScopeId,
|
|
1957
|
+
preBatchSnapshot: replayInputSnapshot,
|
|
1793
1958
|
resolvedArgsByCallId,
|
|
1794
1959
|
errorOwnership,
|
|
1795
1960
|
additionalContextsSink: directAdditionalContexts,
|
|
@@ -1813,14 +1978,15 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1813
1978
|
const assistantBatch = findAssistantBatch(messages);
|
|
1814
1979
|
if (assistantBatch == null) throw new Error("ToolNode only accepts AIMessages as input.");
|
|
1815
1980
|
const aiMessage = assistantBatch.message;
|
|
1816
|
-
replayBatchKey = getAssistantBatchReplayKey(assistantBatch,
|
|
1981
|
+
replayBatchKey = getToolBatchReplayScope(config) == null ? void 0 : getAssistantBatchReplayKey(assistantBatch, getToolBatchReplayScope(config));
|
|
1817
1982
|
if (this.loadRuntimeTools) {
|
|
1818
1983
|
const { tools, toolMap } = this.loadRuntimeTools(aiMessage.tool_calls ?? []);
|
|
1819
1984
|
this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
|
|
1820
1985
|
this.applyToolExecutionOverrides();
|
|
1821
1986
|
}
|
|
1987
|
+
const replayCallPositions = new Map((aiMessage.tool_calls ?? []).map((call, i) => [call, i]));
|
|
1822
1988
|
const filteredCalls = aiMessage.tool_calls?.filter((call) => {
|
|
1823
|
-
return (call.id == null || !toolMessageIds.has(call.id)) && !(call.id?.startsWith("srvtoolu_") ?? false);
|
|
1989
|
+
return (call.id == null || call.id === "" || !toolMessageIds.has(call.id)) && !(call.id?.startsWith("srvtoolu_") ?? false);
|
|
1824
1990
|
}) ?? [];
|
|
1825
1991
|
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_"));
|
|
1826
1992
|
invalidCallResults = attributableInvalidCalls.map((call) => new ToolMessage({
|
|
@@ -1862,14 +2028,16 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1862
2028
|
if (directEntries.length === 0 && invalidCallResults.length === 0) return this.executeViaEvent(filteredCalls, config, input, {
|
|
1863
2029
|
batchIndices: eventEntries.map((entry) => entry.batchIndex),
|
|
1864
2030
|
turn,
|
|
1865
|
-
batchScopeId
|
|
2031
|
+
batchScopeId,
|
|
2032
|
+
preBatchSnapshot: replayInputSnapshot
|
|
1866
2033
|
});
|
|
1867
2034
|
const directCalls = directEntries.map((e) => e.call);
|
|
1868
2035
|
const directIndices = directEntries.map((e) => e.batchIndex);
|
|
1869
2036
|
const eventCalls = eventEntries.map((e) => e.call);
|
|
1870
2037
|
const eventIndices = eventEntries.map((e) => e.batchIndex);
|
|
2038
|
+
const approvalScopeCallIds = new Set(filteredCalls.flatMap((call) => call.id == null ? [] : [call.id]));
|
|
1871
2039
|
const preResolvedEventArgs = /* @__PURE__ */ new Map();
|
|
1872
|
-
const preBatchSnapshot = this.toolOutputRegistry?.snapshot(batchScopeId);
|
|
2040
|
+
const preBatchSnapshot = replayInputSnapshot ?? this.toolOutputRegistry?.snapshot(batchScopeId);
|
|
1873
2041
|
if (preBatchSnapshot != null) {
|
|
1874
2042
|
for (const entry of eventEntries) if (entry.call.id != null) {
|
|
1875
2043
|
const { resolved, unresolved } = preBatchSnapshot.resolve(entry.call.args, { substituteIntentKey: this.toolDeclaresBusinessIntent(entry.call.name) });
|
|
@@ -1888,9 +2056,25 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1888
2056
|
preBatchSnapshot,
|
|
1889
2057
|
additionalContextsSink: directAdditionalContexts,
|
|
1890
2058
|
replayBatchKey,
|
|
2059
|
+
replayCallPositions,
|
|
2060
|
+
approvalScopeCallIds,
|
|
1891
2061
|
runInput: input
|
|
1892
2062
|
}) : [];
|
|
1893
|
-
|
|
2063
|
+
const settledDirectResults = replayBatchKey == null ? void 0 : this.settledDirectResultsByBatch.get(replayBatchKey);
|
|
2064
|
+
const unhandledDirectCalls = [];
|
|
2065
|
+
const unhandledDirectOutputs = [];
|
|
2066
|
+
for (let i = 0; i < directCalls.length; i++) {
|
|
2067
|
+
if ((settledDirectResults?.get(directReplayKey(directCalls[i], replayCallPositions.get(directCalls[i]))))?.completionHandled === true) continue;
|
|
2068
|
+
unhandledDirectCalls.push(directCalls[i]);
|
|
2069
|
+
unhandledDirectOutputs.push(directOutputs[i]);
|
|
2070
|
+
}
|
|
2071
|
+
if (unhandledDirectCalls.length > 0) {
|
|
2072
|
+
await this.handleRunToolCompletions(unhandledDirectCalls, unhandledDirectOutputs, config, resolvedArgsByCallId, errorOwnership);
|
|
2073
|
+
for (let i = 0; i < directCalls.length; i++) {
|
|
2074
|
+
const settled = settledDirectResults?.get(directReplayKey(directCalls[i], replayCallPositions.get(directCalls[i])));
|
|
2075
|
+
if (settled != null) settled.completionHandled = true;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
1894
2078
|
const eventResult = eventCalls.length > 0 ? await this.dispatchToolEvents(eventCalls, config, {
|
|
1895
2079
|
batchIndices: eventIndices,
|
|
1896
2080
|
turn,
|
|
@@ -1917,8 +2101,9 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1917
2101
|
...eventResult.injected
|
|
1918
2102
|
];
|
|
1919
2103
|
} else {
|
|
1920
|
-
const preBatchSnapshot = this.toolOutputRegistry?.snapshot(batchScopeId);
|
|
2104
|
+
const preBatchSnapshot = replayInputSnapshot ?? this.toolOutputRegistry?.snapshot(batchScopeId);
|
|
1921
2105
|
const directAdditionalContexts = [];
|
|
2106
|
+
const approvalScopeCallIds = new Set(filteredCalls.flatMap((call) => call.id == null ? [] : [call.id]));
|
|
1922
2107
|
const toolOutputs = await this.runDirectBatchInterruptSafe(filteredCalls, filteredCalls.map((_call, i) => i), config, {
|
|
1923
2108
|
turn,
|
|
1924
2109
|
batchScopeId,
|
|
@@ -1927,6 +2112,8 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1927
2112
|
preBatchSnapshot,
|
|
1928
2113
|
additionalContextsSink: directAdditionalContexts,
|
|
1929
2114
|
replayBatchKey,
|
|
2115
|
+
replayCallPositions,
|
|
2116
|
+
approvalScopeCallIds,
|
|
1930
2117
|
runInput: input
|
|
1931
2118
|
});
|
|
1932
2119
|
await this.handleRunToolCompletions(filteredCalls, toolOutputs, config, resolvedArgsByCallId, errorOwnership);
|
|
@@ -1955,7 +2142,7 @@ var ToolNode = class extends RunnableCallable {
|
|
|
1955
2142
|
}
|
|
1956
2143
|
}
|
|
1957
2144
|
if (!outputs.some(isCommand)) {
|
|
1958
|
-
if (replayBatchKey != null) this.
|
|
2145
|
+
if (replayBatchKey != null) this.settledDirectResultsByBatch.delete(replayBatchKey);
|
|
1959
2146
|
return Array.isArray(input) ? outputs : { messages: outputs };
|
|
1960
2147
|
}
|
|
1961
2148
|
if (promotedAiMessage != null) outputs = outputs.map((output) => isCommand(output) ? patchCommandUpdateForPromotedInvalidCalls(output, promotedAiMessage, invalidCallResults) : output);
|
|
@@ -2005,7 +2192,7 @@ var ToolNode = class extends RunnableCallable {
|
|
|
2005
2192
|
combinedOutputs.push(parallelCommand);
|
|
2006
2193
|
} else if (handoffCommands.length === 1) combinedOutputs.push(handoffCommands[0]);
|
|
2007
2194
|
if (parentCommand) combinedOutputs.push(parentCommand);
|
|
2008
|
-
if (replayBatchKey != null) this.
|
|
2195
|
+
if (replayBatchKey != null) this.settledDirectResultsByBatch.delete(replayBatchKey);
|
|
2009
2196
|
return combinedOutputs;
|
|
2010
2197
|
}
|
|
2011
2198
|
isSendInput(input) {
|