@librechat/agents 3.4.0 → 3.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +165 -57
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +81 -33
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/graphs/applyGraphRuntimeConfig.cjs +14 -0
- package/dist/cjs/graphs/applyGraphRuntimeConfig.cjs.map +1 -0
- package/dist/cjs/graphs/createGraph.cjs +15 -0
- package/dist/cjs/graphs/createGraph.cjs.map +1 -0
- package/dist/cjs/graphs/index.cjs +1 -0
- package/dist/cjs/hooks/HookRegistry.cjs +83 -0
- package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
- package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +1 -1
- package/dist/cjs/hooks/executeHooks.cjs +60 -14
- package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
- package/dist/cjs/hooks/index.cjs.map +1 -1
- package/dist/cjs/hooks/types.cjs +2 -0
- package/dist/cjs/hooks/types.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +3 -3
- package/dist/cjs/llm/invoke.cjs +2 -2
- package/dist/cjs/main.cjs +34 -24
- package/dist/cjs/messages/format.cjs +40 -23
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/run.cjs +130 -53
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +37 -2
- package/dist/cjs/session/AgentSession.cjs.map +1 -1
- package/dist/cjs/session/JsonlSessionStore.cjs +2 -2
- package/dist/cjs/stream.cjs +1 -1
- package/dist/cjs/summarization/node.cjs +6 -3
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +1 -1
- package/dist/cjs/tools/SubagentTool.cjs +4 -4
- package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +321 -95
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +1 -1
- package/dist/cjs/tools/eagerEventExecution.cjs +1 -0
- package/dist/cjs/tools/local/LocalExecutionEngine.cjs +3 -3
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +2 -2
- package/dist/cjs/tools/search/rerankers.cjs +154 -1
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +4 -1
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs +601 -0
- package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs.map +1 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1114 -233
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentReplay.cjs +177 -0
- package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -0
- package/dist/cjs/tools/subagent/childGraphConfig.cjs +278 -0
- package/dist/cjs/tools/subagent/childGraphConfig.cjs.map +1 -0
- package/dist/cjs/tools/subagent/index.cjs +1 -0
- package/dist/cjs/tools/subagent/runtimeLimits.cjs +5 -0
- package/dist/cjs/tools/subagent/runtimeLimits.cjs.map +1 -0
- package/dist/cjs/tools/toolOutputReferences.cjs +20 -0
- package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
- package/dist/cjs/utils/index.cjs +2 -2
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +165 -57
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +81 -33
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/graphs/applyGraphRuntimeConfig.mjs +14 -0
- package/dist/esm/graphs/applyGraphRuntimeConfig.mjs.map +1 -0
- package/dist/esm/graphs/createGraph.mjs +15 -0
- package/dist/esm/graphs/createGraph.mjs.map +1 -0
- package/dist/esm/graphs/index.mjs +1 -0
- package/dist/esm/hooks/HookRegistry.mjs +83 -0
- package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
- package/dist/esm/hooks/createWorkspacePolicyHook.mjs +1 -1
- package/dist/esm/hooks/executeHooks.mjs +60 -14
- package/dist/esm/hooks/executeHooks.mjs.map +1 -1
- package/dist/esm/hooks/index.mjs.map +1 -1
- package/dist/esm/hooks/types.mjs +2 -1
- package/dist/esm/hooks/types.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +1 -1
- package/dist/esm/llm/invoke.mjs +2 -2
- package/dist/esm/main.mjs +21 -19
- package/dist/esm/messages/format.mjs +40 -23
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/run.mjs +131 -54
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +37 -2
- package/dist/esm/session/AgentSession.mjs.map +1 -1
- package/dist/esm/session/JsonlSessionStore.mjs +2 -2
- package/dist/esm/stream.mjs +1 -1
- package/dist/esm/summarization/node.mjs +6 -3
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +1 -1
- package/dist/esm/tools/SubagentTool.mjs +4 -4
- package/dist/esm/tools/SubagentTool.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +322 -96
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +1 -1
- package/dist/esm/tools/eagerEventExecution.mjs +1 -1
- package/dist/esm/tools/local/LocalExecutionEngine.mjs +3 -3
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +2 -2
- package/dist/esm/tools/search/rerankers.mjs +154 -1
- package/dist/esm/tools/search/rerankers.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +4 -1
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs +597 -0
- package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs.map +1 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +1114 -233
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentReplay.mjs +170 -0
- package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -0
- package/dist/esm/tools/subagent/childGraphConfig.mjs +272 -0
- package/dist/esm/tools/subagent/childGraphConfig.mjs.map +1 -0
- package/dist/esm/tools/subagent/index.mjs +1 -0
- package/dist/esm/tools/subagent/runtimeLimits.mjs +5 -0
- package/dist/esm/tools/subagent/runtimeLimits.mjs.map +1 -0
- package/dist/esm/tools/toolOutputReferences.mjs +20 -0
- package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
- package/dist/esm/utils/index.mjs +2 -2
- package/dist/types/agents/AgentContext.d.ts +1 -1
- package/dist/types/graphs/Graph.d.ts +22 -2
- package/dist/types/graphs/MultiAgentGraph.d.ts +7 -1
- package/dist/types/graphs/applyGraphRuntimeConfig.d.ts +3 -0
- package/dist/types/graphs/createGraph.d.ts +13 -0
- package/dist/types/graphs/graphFactory.d.ts +13 -0
- package/dist/types/graphs/index.d.ts +2 -0
- package/dist/types/hooks/HookRegistry.d.ts +10 -1
- package/dist/types/hooks/executeHooks.d.ts +5 -1
- package/dist/types/hooks/index.d.ts +2 -2
- package/dist/types/hooks/types.d.ts +12 -0
- package/dist/types/run.d.ts +6 -3
- package/dist/types/session/AgentSession.d.ts +1 -0
- package/dist/types/tools/SubagentTool.d.ts +1 -1
- package/dist/types/tools/ToolNode.d.ts +23 -15
- package/dist/types/tools/search/rerankers.d.ts +28 -0
- package/dist/types/tools/search/types.d.ts +38 -1
- package/dist/types/tools/subagent/SubagentExecutionRegistry.d.ts +171 -0
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +70 -36
- package/dist/types/tools/subagent/SubagentReplay.d.ts +85 -0
- package/dist/types/tools/subagent/childGraphConfig.d.ts +37 -0
- package/dist/types/tools/subagent/index.d.ts +1 -1
- package/dist/types/tools/subagent/runtimeLimits.d.ts +4 -0
- package/dist/types/tools/toolOutputReferences.d.ts +12 -0
- package/dist/types/types/graph.d.ts +189 -24
- package/dist/types/types/hitl.d.ts +15 -0
- package/package.json +8 -2
- package/src/agents/AgentContext.ts +1 -1
- package/src/graphs/Graph.ts +349 -104
- package/src/graphs/MultiAgentGraph.ts +149 -47
- package/src/graphs/__tests__/Graph.breakerLifecycle.test.ts +48 -9
- package/src/graphs/__tests__/Graph.contextOverflow.test.ts +1 -1
- package/src/graphs/__tests__/Graph.subagentResumeState.test.ts +80 -0
- package/src/graphs/__tests__/MultiAgentGraph.test.ts +86 -0
- package/src/graphs/__tests__/composition.smoke.test.ts +152 -0
- package/src/graphs/__tests__/createGraph.test.ts +206 -0
- package/src/graphs/applyGraphRuntimeConfig.ts +25 -0
- package/src/graphs/createGraph.ts +32 -0
- package/src/graphs/graphFactory.ts +12 -0
- package/src/graphs/index.ts +2 -0
- package/src/hooks/HookRegistry.ts +151 -1
- package/src/hooks/__tests__/HookRegistry.test.ts +96 -0
- package/src/hooks/__tests__/executeHooks.test.ts +160 -0
- package/src/hooks/executeHooks.ts +133 -15
- package/src/hooks/index.ts +3 -1
- package/src/hooks/types.ts +16 -0
- package/src/messages/format.ts +54 -26
- package/src/messages/formatAgentMessages.reducer.test.ts +162 -0
- package/src/messages/formatAgentMessages.steer.test.ts +16 -11
- package/src/messages/formatAgentMessages.test.ts +7 -2
- package/src/run.ts +214 -56
- package/src/session/AgentSession.ts +52 -2
- package/src/session/__tests__/JsonlSessionStore.test.ts +53 -0
- package/src/specs/graph-subagent.live.test.ts +242 -0
- package/src/specs/graph-subagent.test.ts +322 -0
- package/src/specs/langfuse-routing.integration.test.ts +120 -1
- package/src/specs/subagent.test.ts +130 -4
- package/src/summarization/__tests__/node.test.ts +32 -14
- package/src/summarization/node.ts +15 -11
- package/src/tools/SubagentTool.ts +4 -4
- package/src/tools/ToolNode.ts +643 -157
- package/src/tools/__tests__/SubagentExecutor.test.ts +740 -15
- package/src/tools/__tests__/SubagentReplay.test.ts +329 -0
- package/src/tools/__tests__/ToolNode.breakerSignal.test.ts +208 -2
- package/src/tools/__tests__/directToolHITLResumeScope.test.ts +512 -2
- package/src/tools/__tests__/hitl.test.ts +234 -5
- package/src/tools/__tests__/subagentHooks.test.ts +1126 -61
- package/src/tools/__tests__/toolOutputReferences.test.ts +19 -2
- package/src/tools/search/rag-api-reranker.test.ts +891 -0
- package/src/tools/search/rerankers.ts +260 -0
- package/src/tools/search/tool.ts +6 -0
- package/src/tools/search/types.ts +45 -1
- package/src/tools/subagent/SubagentExecutionRegistry.ts +1159 -0
- package/src/tools/subagent/SubagentExecutor.ts +2232 -312
- package/src/tools/subagent/SubagentReplay.ts +584 -0
- package/src/tools/subagent/__tests__/SubagentExecutionRegistry.test.ts +637 -0
- package/src/tools/subagent/__tests__/SubagentExecutor.lazy.test.ts +3054 -0
- package/src/tools/subagent/__tests__/childGraphConfig.test.ts +521 -0
- package/src/tools/subagent/childGraphConfig.ts +694 -0
- package/src/tools/subagent/index.ts +6 -0
- package/src/tools/subagent/runtimeLimits.ts +7 -0
- package/src/tools/toolOutputReferences.ts +40 -1
- package/src/types/graph.ts +222 -24
- package/src/types/hitl.ts +16 -0
|
@@ -1,28 +1,223 @@
|
|
|
1
|
+
const require_SubagentReplay = require("./SubagentReplay.cjs");
|
|
2
|
+
const require_misc = require("../../utils/misc.cjs");
|
|
1
3
|
require("../../common/enum.cjs");
|
|
2
4
|
require("../../common/index.cjs");
|
|
5
|
+
const require_eagerEventExecution = require("../eagerEventExecution.cjs");
|
|
3
6
|
const require_streamLimits = require("../../llm/streamLimits.cjs");
|
|
7
|
+
const require_SubagentExecutionRegistry = require("./SubagentExecutionRegistry.cjs");
|
|
8
|
+
require("./runtimeLimits.cjs");
|
|
4
9
|
const require_executeHooks = require("../../hooks/executeHooks.cjs");
|
|
10
|
+
const require_types = require("../../hooks/types.cjs");
|
|
5
11
|
require("../../hooks/index.cjs");
|
|
12
|
+
const require_childGraphConfig = require("./childGraphConfig.cjs");
|
|
6
13
|
let nanoid = require("nanoid");
|
|
7
14
|
let _langchain_core_callbacks_base = require("@langchain/core/callbacks/base");
|
|
8
15
|
let _langchain_core_messages = require("@langchain/core/messages");
|
|
16
|
+
let _langchain_langgraph = require("@langchain/langgraph");
|
|
17
|
+
let _langchain_core_singletons = require("@langchain/core/singletons");
|
|
18
|
+
let crypto = require("crypto");
|
|
9
19
|
//#region src/tools/subagent/SubagentExecutor.ts
|
|
10
|
-
const DEFAULT_MAX_TURNS = 25;
|
|
11
|
-
const RECURSION_MULTIPLIER = 3;
|
|
12
20
|
const ERROR_MESSAGE_MAX_CHARS = 200;
|
|
13
|
-
const
|
|
21
|
+
const MAX_QUEUED_SUBAGENT_UPDATES = 64;
|
|
22
|
+
const SUBAGENT_UPDATE_HANDLER_TIMEOUT_MS = 5e3;
|
|
14
23
|
const TEXT_DELTA_CONTENT_TYPE = `text_delta`;
|
|
24
|
+
const SUBAGENT_RESOLUTION_ERROR_MESSAGE = "Subagent error: Unable to initialize the selected subagent.";
|
|
25
|
+
const SUBAGENT_CONFIG_CHANGED_MESSAGE = "Subagent error: Subagent configuration changed since this execution was paused.";
|
|
26
|
+
const SUBAGENT_INVOCATION_CHANGED_MESSAGE = "Subagent error: Subagent invocation changed for this execution.";
|
|
27
|
+
async function dispatchObservationalSubagentUpdate(handler, event) {
|
|
28
|
+
let timeout;
|
|
29
|
+
try {
|
|
30
|
+
return await Promise.race([Promise.resolve().then(() => handler.handle("on_subagent_update", event)).then(() => true, () => true), new Promise((resolve) => {
|
|
31
|
+
timeout = setTimeout(() => resolve(false), SUBAGENT_UPDATE_HANDLER_TIMEOUT_MS);
|
|
32
|
+
})]);
|
|
33
|
+
} finally {
|
|
34
|
+
if (timeout != null) clearTimeout(timeout);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
15
37
|
const HOOK_FALLBACK = Object.freeze({
|
|
16
38
|
additionalContexts: [],
|
|
17
39
|
injectedMessages: [],
|
|
18
40
|
errors: []
|
|
19
41
|
});
|
|
42
|
+
function isResumeExecutionCompatible(resumeExecution, subagentType, executableConfig, hasPersistedTypeEvidence = false) {
|
|
43
|
+
if (resumeExecution == null) return true;
|
|
44
|
+
if (subagentType == null || executableConfig == null || resumeExecution.configId !== executableConfig.configId) return false;
|
|
45
|
+
if (resumeExecution.subagentType != null) return resumeExecution.subagentType === subagentType;
|
|
46
|
+
if (hasPersistedTypeEvidence) return true;
|
|
47
|
+
return require_childGraphConfig.isGraphSubagentConfig(executableConfig) || executableConfig.agentInputs != null;
|
|
48
|
+
}
|
|
49
|
+
function getSubagentTypeFromArgs(args) {
|
|
50
|
+
if (!isObjectLike(args)) return;
|
|
51
|
+
return asNonEmptyString(args.subagent_type);
|
|
52
|
+
}
|
|
53
|
+
function getSubagentDescriptionFromArgs(args) {
|
|
54
|
+
if (!isObjectLike(args)) return;
|
|
55
|
+
const description = args.description;
|
|
56
|
+
return typeof description === "string" && description.trim().length > 0 ? description : void 0;
|
|
57
|
+
}
|
|
58
|
+
function getSubagentType(call) {
|
|
59
|
+
return getSubagentTypeFromArgs(call.args);
|
|
60
|
+
}
|
|
20
61
|
const LANGGRAPH_RUNTIME_CONFIG_PREFIX = "__pregel_";
|
|
62
|
+
const LANGGRAPH_RESUME_MAP_CONFIG_KEY = "__pregel_resume_map";
|
|
21
63
|
const LANGGRAPH_CHECKPOINT_CONFIG_KEYS = new Set([
|
|
22
64
|
"checkpoint_id",
|
|
23
65
|
"checkpoint_map",
|
|
24
66
|
"checkpoint_ns"
|
|
25
67
|
]);
|
|
68
|
+
const SUBAGENT_CHECKPOINT_MARKER_KEY = "__librechat_subagent_checkpoint";
|
|
69
|
+
const SUBAGENT_HOOK_SESSION_KEY = "__librechat_subagent_hook_session";
|
|
70
|
+
const SUBAGENT_RUN_ID_KEY = "__librechat_subagent_run_id";
|
|
71
|
+
const SUBAGENT_REPLAY_NODE = "subagent-replay";
|
|
72
|
+
const DEFAULT_SUBAGENT_DESCRIPTION = "No task description provided";
|
|
73
|
+
function isCheckpointSaver(value) {
|
|
74
|
+
if (value == null || typeof value !== "object") return false;
|
|
75
|
+
const candidate = value;
|
|
76
|
+
return typeof candidate.getTuple === "function" && typeof candidate.list === "function" && typeof candidate.put === "function" && typeof candidate.putWrites === "function" && typeof candidate.deleteThread === "function";
|
|
77
|
+
}
|
|
78
|
+
function isSubagentCheckpointMarker(value) {
|
|
79
|
+
if (value == null || typeof value !== "object") return false;
|
|
80
|
+
const marker = value;
|
|
81
|
+
return marker.version === 1 && marker.lifecycleComplete === true && typeof marker.parentToolCallId === "string" && (marker.hookSessionId == null || typeof marker.hookSessionId === "string") && (marker.childRunId == null || typeof marker.childRunId === "string") && (marker.settledOutput == null || isPersistedToolOutput(marker.settledOutput));
|
|
82
|
+
}
|
|
83
|
+
function isPersistedToolOutput(value) {
|
|
84
|
+
if (value == null || typeof value !== "object") return false;
|
|
85
|
+
const output = value;
|
|
86
|
+
const contentIsValid = typeof output.content === "string" || Array.isArray(output.content);
|
|
87
|
+
const statusIsValid = output.status == null || output.status === "success" || output.status === "error";
|
|
88
|
+
const contextsAreValid = Array.isArray(output.additionalContexts) && output.additionalContexts.every((context) => typeof context === "string");
|
|
89
|
+
const resolvedArgsAreValid = output.resolvedArgs == null || typeof output.resolvedArgs === "object" && !Array.isArray(output.resolvedArgs);
|
|
90
|
+
return contentIsValid && typeof output.toolCallId === "string" && output.additionalKwargs != null && typeof output.additionalKwargs === "object" && output.responseMetadata != null && typeof output.responseMetadata === "object" && statusIsValid && contextsAreValid && resolvedArgsAreValid && (output.referenceContent == null || typeof output.referenceContent === "string");
|
|
91
|
+
}
|
|
92
|
+
function getSubagentCheckpointMarker(messages, parentToolCallId) {
|
|
93
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
94
|
+
const marker = messages[i].additional_kwargs[SUBAGENT_CHECKPOINT_MARKER_KEY];
|
|
95
|
+
if (isSubagentCheckpointMarker(marker) && marker.parentToolCallId === parentToolCallId) return marker;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function getSubagentHookSessionId(messages) {
|
|
99
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
100
|
+
const sessionId = messages[i].additional_kwargs[SUBAGENT_HOOK_SESSION_KEY];
|
|
101
|
+
if (typeof sessionId === "string" && sessionId.length > 0) return sessionId;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function getSubagentRunId(messages) {
|
|
105
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
106
|
+
const runId = messages[i].additional_kwargs[SUBAGENT_RUN_ID_KEY];
|
|
107
|
+
if (typeof runId === "string" && runId.length > 0) return runId;
|
|
108
|
+
const marker = messages[i].additional_kwargs[SUBAGENT_CHECKPOINT_MARKER_KEY];
|
|
109
|
+
if (isSubagentCheckpointMarker(marker) && marker.childRunId != null && marker.childRunId.length > 0) return marker.childRunId;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function isSubagentCheckpointMarkerMessage(message) {
|
|
113
|
+
return isSubagentCheckpointMarker(message.additional_kwargs[SUBAGENT_CHECKPOINT_MARKER_KEY]);
|
|
114
|
+
}
|
|
115
|
+
function createSubagentCheckpointMarkerMessage(marker) {
|
|
116
|
+
return new _langchain_core_messages.AIMessage({
|
|
117
|
+
content: "",
|
|
118
|
+
additional_kwargs: { [SUBAGENT_CHECKPOINT_MARKER_KEY]: marker }
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function getCheckpointMessages(value) {
|
|
122
|
+
return Array.isArray(value) ? value.filter(_langchain_core_messages.BaseMessage.isInstance) : [];
|
|
123
|
+
}
|
|
124
|
+
function getConfigurableString(config, key) {
|
|
125
|
+
const value = config?.configurable?.[key];
|
|
126
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
127
|
+
}
|
|
128
|
+
function getTupleMessages(tuple) {
|
|
129
|
+
return getCheckpointMessages(tuple?.checkpoint.channel_values.messages);
|
|
130
|
+
}
|
|
131
|
+
function getCheckpointReference(tuple) {
|
|
132
|
+
const configurable = tuple?.config.configurable;
|
|
133
|
+
const threadId = configurable?.thread_id;
|
|
134
|
+
const checkpointId = configurable?.checkpoint_id;
|
|
135
|
+
const checkpointNs = configurable?.checkpoint_ns ?? "";
|
|
136
|
+
if (typeof threadId !== "string" || threadId.length === 0 || typeof checkpointId !== "string" || checkpointId.length === 0 || typeof checkpointNs !== "string") return;
|
|
137
|
+
return {
|
|
138
|
+
threadId,
|
|
139
|
+
checkpointId,
|
|
140
|
+
checkpointNs
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function serializeToolOutput(settled) {
|
|
144
|
+
const { output } = settled;
|
|
145
|
+
return {
|
|
146
|
+
content: output.content,
|
|
147
|
+
toolCallId: output.tool_call_id,
|
|
148
|
+
...output.id == null ? {} : { id: output.id },
|
|
149
|
+
...output.name == null ? {} : { name: output.name },
|
|
150
|
+
...output.status == null ? {} : { status: output.status },
|
|
151
|
+
additionalKwargs: output.additional_kwargs,
|
|
152
|
+
responseMetadata: output.response_metadata,
|
|
153
|
+
...output.metadata == null ? {} : { metadata: output.metadata },
|
|
154
|
+
additionalContexts: settled.additionalContexts,
|
|
155
|
+
...settled.resolvedArgs == null ? {} : { resolvedArgs: settled.resolvedArgs },
|
|
156
|
+
...settled.referenceContent == null ? {} : { referenceContent: settled.referenceContent }
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function getSettlementFingerprint(output) {
|
|
160
|
+
return (0, crypto.createHash)("sha256").update(require_eagerEventExecution.stableStringify(output)).digest("hex");
|
|
161
|
+
}
|
|
162
|
+
function deserializeToolOutput(output) {
|
|
163
|
+
return {
|
|
164
|
+
output: new _langchain_core_messages.ToolMessage({
|
|
165
|
+
content: output.content,
|
|
166
|
+
tool_call_id: output.toolCallId,
|
|
167
|
+
...output.id == null ? {} : { id: output.id },
|
|
168
|
+
...output.name == null ? {} : { name: output.name },
|
|
169
|
+
...output.status == null ? {} : { status: output.status },
|
|
170
|
+
additional_kwargs: output.additionalKwargs,
|
|
171
|
+
response_metadata: output.responseMetadata,
|
|
172
|
+
...output.metadata == null ? {} : { metadata: output.metadata }
|
|
173
|
+
}),
|
|
174
|
+
additionalContexts: output.additionalContexts,
|
|
175
|
+
...output.resolvedArgs == null ? {} : { resolvedArgs: output.resolvedArgs },
|
|
176
|
+
...output.referenceContent == null ? {} : { referenceContent: output.referenceContent }
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function isToolApprovalPayload(value) {
|
|
180
|
+
return value != null && typeof value === "object" && value.type === "tool_approval";
|
|
181
|
+
}
|
|
182
|
+
function addSubagentScope(interrupts, scope, resumeManifest) {
|
|
183
|
+
return interrupts.map((childInterrupt) => {
|
|
184
|
+
let payload = childInterrupt.value;
|
|
185
|
+
if (isToolApprovalPayload(payload)) payload = {
|
|
186
|
+
...childInterrupt.value,
|
|
187
|
+
subagent: childInterrupt.value.subagent ?? scope
|
|
188
|
+
};
|
|
189
|
+
return {
|
|
190
|
+
...childInterrupt,
|
|
191
|
+
value: resumeManifest == null ? payload : require_SubagentReplay.attachSubagentResumeManifest(payload, resumeManifest)
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
function getChildResumeMap(pendingInterrupts, parentConfigurable) {
|
|
196
|
+
const resumeMap = parentConfigurable?.[LANGGRAPH_RESUME_MAP_CONFIG_KEY];
|
|
197
|
+
if (resumeMap == null || typeof resumeMap !== "object") return;
|
|
198
|
+
const parentResumeMap = resumeMap;
|
|
199
|
+
const childResumeMap = {};
|
|
200
|
+
for (const childInterrupt of pendingInterrupts) {
|
|
201
|
+
const interruptId = childInterrupt.id;
|
|
202
|
+
if (typeof interruptId === "string" && Object.prototype.hasOwnProperty.call(parentResumeMap, interruptId)) childResumeMap[interruptId] = parentResumeMap[interruptId];
|
|
203
|
+
}
|
|
204
|
+
return Object.keys(childResumeMap).length > 0 ? childResumeMap : void 0;
|
|
205
|
+
}
|
|
206
|
+
function getPersistedInterrupts(snapshot) {
|
|
207
|
+
const interrupts = [];
|
|
208
|
+
for (const task of snapshot.tasks) for (const pendingInterrupt of task.interrupts) interrupts.push(pendingInterrupt);
|
|
209
|
+
return interrupts;
|
|
210
|
+
}
|
|
211
|
+
function getPersistedMessages(snapshot) {
|
|
212
|
+
if (snapshot.values == null || typeof snapshot.values !== "object") return;
|
|
213
|
+
const values = snapshot.values;
|
|
214
|
+
if (!Array.isArray(values.messages) || values.messages.length === 0) return;
|
|
215
|
+
const messages = values.messages.filter((message) => !isSubagentCheckpointMarkerMessage(message));
|
|
216
|
+
return messages.length > 0 ? messages : void 0;
|
|
217
|
+
}
|
|
218
|
+
function createReplayCheckpointWorkflow(checkpointer) {
|
|
219
|
+
return new _langchain_langgraph.StateGraph(_langchain_langgraph.MessagesAnnotation).addNode(SUBAGENT_REPLAY_NODE, (state) => state).addEdge(_langchain_langgraph.START, SUBAGENT_REPLAY_NODE).addEdge(SUBAGENT_REPLAY_NODE, _langchain_langgraph.END).compile({ checkpointer });
|
|
220
|
+
}
|
|
26
221
|
var SubagentExecutor = class {
|
|
27
222
|
configs;
|
|
28
223
|
parentSignal;
|
|
@@ -37,12 +232,18 @@ var SubagentExecutor = class {
|
|
|
37
232
|
hookRegistry;
|
|
38
233
|
parentRunId;
|
|
39
234
|
parentAgentId;
|
|
235
|
+
executionContext;
|
|
40
236
|
langfuse;
|
|
41
237
|
tokenCounter;
|
|
42
238
|
streamLimits;
|
|
239
|
+
humanInTheLoop;
|
|
240
|
+
checkpointer;
|
|
43
241
|
maxDepth;
|
|
44
242
|
createChildGraph;
|
|
243
|
+
createChildGraphByKind;
|
|
45
244
|
usageSink;
|
|
245
|
+
executions;
|
|
246
|
+
replayCheckpointWorkflow;
|
|
46
247
|
resolveParentHandlerRegistry;
|
|
47
248
|
constructor(options) {
|
|
48
249
|
this.configs = options.configs;
|
|
@@ -51,11 +252,25 @@ var SubagentExecutor = class {
|
|
|
51
252
|
this.hookRegistry = options.hookRegistry;
|
|
52
253
|
this.parentRunId = options.parentRunId;
|
|
53
254
|
this.parentAgentId = options.parentAgentId;
|
|
255
|
+
this.executionContext = options.executionContext ?? {
|
|
256
|
+
rootRunId: options.parentRunId,
|
|
257
|
+
hookSessionId: options.parentRunId,
|
|
258
|
+
depth: 0,
|
|
259
|
+
ancestry: []
|
|
260
|
+
};
|
|
54
261
|
this.langfuse = options.langfuse;
|
|
55
262
|
this.tokenCounter = options.tokenCounter;
|
|
56
263
|
this.streamLimits = options.streamLimits;
|
|
264
|
+
this.humanInTheLoop = options.humanInTheLoop;
|
|
265
|
+
this.checkpointer = isCheckpointSaver(options.checkpointer) ? options.checkpointer : void 0;
|
|
266
|
+
this.executions = new require_SubagentExecutionRegistry.SubagentExecutionRegistry({
|
|
267
|
+
parentRunId: options.parentRunId,
|
|
268
|
+
parentAgentId: options.parentAgentId,
|
|
269
|
+
durable: options.humanInTheLoop?.enabled === true && this.checkpointer != null
|
|
270
|
+
});
|
|
57
271
|
this.maxDepth = options.maxDepth ?? 1;
|
|
58
272
|
this.createChildGraph = options.createChildGraph;
|
|
273
|
+
this.createChildGraphByKind = options.createChildGraphByKind;
|
|
59
274
|
this.usageSink = options.usageSink;
|
|
60
275
|
const rawRegistry = options.parentHandlerRegistry;
|
|
61
276
|
if (typeof rawRegistry === "function") this.resolveParentHandlerRegistry = rawRegistry;
|
|
@@ -69,16 +284,501 @@ var SubagentExecutor = class {
|
|
|
69
284
|
/** One signal that fires on the parent's abort or the breaker abort,
|
|
70
285
|
* collapsed to a single signal when possible (mirrors
|
|
71
286
|
* `composeAbortSignals` in Graph.ts). */
|
|
72
|
-
composeChildSignal(breaker) {
|
|
73
|
-
|
|
74
|
-
if (this.parentSignal == null || this.parentSignal === child) return child;
|
|
75
|
-
return AbortSignal.any([this.parentSignal, child]);
|
|
287
|
+
composeChildSignal(breaker, executionSignal) {
|
|
288
|
+
return require_misc.composeAbortSignals(require_misc.composeAbortSignals(this.parentSignal, breaker.signal), executionSignal) ?? breaker.signal;
|
|
76
289
|
}
|
|
77
290
|
/** Snapshot of the parent's registry at the moment a subagent is dispatched. */
|
|
78
291
|
getParentHandlerRegistry() {
|
|
79
292
|
return this.resolveParentHandlerRegistry?.();
|
|
80
293
|
}
|
|
81
|
-
|
|
294
|
+
bindExecutionDefinition(execution, binding, authority) {
|
|
295
|
+
try {
|
|
296
|
+
execution.bindDefinition(binding, authority);
|
|
297
|
+
return true;
|
|
298
|
+
} catch (error) {
|
|
299
|
+
if (error instanceof require_SubagentExecutionRegistry.SubagentDefinitionBindingError) return false;
|
|
300
|
+
throw error;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/** Resolve one lazy descriptor per stable child execution. Concurrent
|
|
304
|
+
* duplicate dispatches share the same in-flight resolution; HITL re-entry
|
|
305
|
+
* on this executor reuses the resolved config until the child settles. */
|
|
306
|
+
resolveExecutionConfig(config, execution, context) {
|
|
307
|
+
if (require_childGraphConfig.isGraphSubagentConfig(config) || config.agentInputs != null) return Promise.resolve(config);
|
|
308
|
+
const resolver = config.resolveAgentInputs;
|
|
309
|
+
const configId = config.configId;
|
|
310
|
+
return execution.resolveConfig(() => resolveAgentInputsWithSignal(() => resolver({
|
|
311
|
+
descriptor: {
|
|
312
|
+
type: config.type,
|
|
313
|
+
name: config.name,
|
|
314
|
+
description: config.description,
|
|
315
|
+
configId
|
|
316
|
+
},
|
|
317
|
+
executionId: context.childRunId,
|
|
318
|
+
parentRunId: this.parentRunId,
|
|
319
|
+
parentAgentId: this.parentAgentId,
|
|
320
|
+
parentToolCallId: context.parentToolCallId,
|
|
321
|
+
threadId: context.threadId,
|
|
322
|
+
signal: context.childSignal,
|
|
323
|
+
configurable: context.parentConfigurable == null ? void 0 : sanitizeResolverConfigurable(context.parentConfigurable)
|
|
324
|
+
}), context.childSignal).then((agentInputs) => ({
|
|
325
|
+
...config,
|
|
326
|
+
agentInputs
|
|
327
|
+
})), context.childSignal);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Keeps the original child thread as an immutable resume source once a
|
|
331
|
+
* different parent Run reconstructs it. Each rebuilt parent gets a private
|
|
332
|
+
* checkpoint fork, while the persisted child run ID remains stable for
|
|
333
|
+
* activity and usage correlation across the interrupt boundary.
|
|
334
|
+
*/
|
|
335
|
+
async resolveChildExecutionIdentity(execution) {
|
|
336
|
+
return execution.resolveIdentity(async () => {
|
|
337
|
+
const preparation = await this.createChildExecutionIdentity(execution);
|
|
338
|
+
const { identity } = preparation;
|
|
339
|
+
return {
|
|
340
|
+
identity,
|
|
341
|
+
lease: preparation.lease,
|
|
342
|
+
commit: (lease) => {
|
|
343
|
+
const { resumeExecution } = execution;
|
|
344
|
+
if (resumeExecution != null) this.executions.retireResumeSources(execution, new Set(resumeExecution.checkpoints.map((checkpoint) => checkpoint.threadId)), (source) => {
|
|
345
|
+
if (source.activeRun != null) this.clearChildGraph(source.activeRun.graph);
|
|
346
|
+
if (source.identity != null) this.hookRegistry?.clearSession(source.identity.approvalExecutionScope);
|
|
347
|
+
});
|
|
348
|
+
preparation.commit?.(lease);
|
|
349
|
+
if (this.humanInTheLoop?.enabled === true && this.checkpointer != null) this.executions.rememberCheckpointThread(identity.childThreadId);
|
|
350
|
+
},
|
|
351
|
+
rollback: async (lease) => {
|
|
352
|
+
await preparation.rollback?.(lease);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
async createChildExecutionIdentity(execution) {
|
|
358
|
+
const { baseChildThreadId, branchChildThreadId, currentChildRunId, explicitResumeAttempt, resumeAttemptId } = execution.address;
|
|
359
|
+
if (this.humanInTheLoop?.enabled !== true || this.checkpointer == null) return this.prepareIdentity(execution, {
|
|
360
|
+
childRunId: currentChildRunId,
|
|
361
|
+
childThreadId: baseChildThreadId,
|
|
362
|
+
approvalExecutionScope: currentChildRunId
|
|
363
|
+
});
|
|
364
|
+
const { resumeExecution } = execution;
|
|
365
|
+
if (resumeExecution != null) {
|
|
366
|
+
const approvalExecutionScope = require_SubagentExecutionRegistry.getSubagentApprovalExecutionScope(resumeExecution.childRunId, resumeAttemptId);
|
|
367
|
+
const identity = {
|
|
368
|
+
childRunId: resumeExecution.childRunId,
|
|
369
|
+
childThreadId: branchChildThreadId,
|
|
370
|
+
approvalExecutionScope
|
|
371
|
+
};
|
|
372
|
+
const lease = this.executions.beginIdentityPreparation(execution, identity);
|
|
373
|
+
await this.prepareCheckpointFork(resumeExecution.checkpoints, branchChildThreadId, lease);
|
|
374
|
+
let previousApprovals = [];
|
|
375
|
+
let approvalsMutated = false;
|
|
376
|
+
return {
|
|
377
|
+
identity,
|
|
378
|
+
lease,
|
|
379
|
+
commit: (currentLease) => {
|
|
380
|
+
const hookRegistry = this.hookRegistry;
|
|
381
|
+
if (hookRegistry == null) return;
|
|
382
|
+
previousApprovals = hookRegistry.snapshotPendingToolApprovals(approvalExecutionScope, approvalExecutionScope);
|
|
383
|
+
const approvalReplayCreatesScope = previousApprovals.length === 0 && resumeExecution.approvalReplays.length > 0;
|
|
384
|
+
const approvalsChanged = previousApprovals.length > 0 || resumeExecution.approvalReplays.length > 0;
|
|
385
|
+
if (approvalReplayCreatesScope) currentLease.markApprovalScopeCreated(async () => {
|
|
386
|
+
hookRegistry.restorePendingToolApprovals(approvalExecutionScope, approvalExecutionScope, previousApprovals);
|
|
387
|
+
});
|
|
388
|
+
approvalsMutated = approvalsChanged && !approvalReplayCreatesScope;
|
|
389
|
+
hookRegistry.restorePendingToolApprovals(approvalExecutionScope, approvalExecutionScope, resumeExecution.approvalReplays);
|
|
390
|
+
},
|
|
391
|
+
rollback: async (currentLease) => {
|
|
392
|
+
if (approvalsMutated && currentLease.ownsApprovalScope && this.hookRegistry != null) this.hookRegistry.restorePendingToolApprovals(approvalExecutionScope, approvalExecutionScope, previousApprovals);
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
const branchTuple = await this.checkpointer.getTuple({ configurable: { thread_id: branchChildThreadId } });
|
|
397
|
+
if (branchTuple != null) {
|
|
398
|
+
const childRunId = getSubagentRunId(getTupleMessages(branchTuple)) ?? currentChildRunId;
|
|
399
|
+
return this.prepareIdentity(execution, {
|
|
400
|
+
childRunId,
|
|
401
|
+
childThreadId: branchChildThreadId,
|
|
402
|
+
approvalExecutionScope: require_SubagentExecutionRegistry.getSubagentApprovalExecutionScope(childRunId, resumeAttemptId)
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
const baseTuple = await this.checkpointer.getTuple({ configurable: { thread_id: baseChildThreadId } });
|
|
406
|
+
if (baseTuple == null) return this.prepareIdentity(execution, {
|
|
407
|
+
childRunId: currentChildRunId,
|
|
408
|
+
childThreadId: explicitResumeAttempt ? branchChildThreadId : baseChildThreadId,
|
|
409
|
+
approvalExecutionScope: require_SubagentExecutionRegistry.getSubagentApprovalExecutionScope(currentChildRunId, resumeAttemptId)
|
|
410
|
+
});
|
|
411
|
+
const persistedChildRunId = getSubagentRunId(getTupleMessages(baseTuple));
|
|
412
|
+
if (!explicitResumeAttempt && (persistedChildRunId == null || persistedChildRunId === currentChildRunId)) {
|
|
413
|
+
const childRunId = persistedChildRunId ?? currentChildRunId;
|
|
414
|
+
return this.prepareIdentity(execution, {
|
|
415
|
+
childRunId,
|
|
416
|
+
childThreadId: baseChildThreadId,
|
|
417
|
+
approvalExecutionScope: require_SubagentExecutionRegistry.getSubagentApprovalExecutionScope(childRunId, resumeAttemptId)
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
const sourceCheckpoints = await this.getLatestCheckpointSnapshot(baseChildThreadId);
|
|
421
|
+
if (sourceCheckpoints.length === 0) throw new Error(`Cannot fork subagent checkpoint thread "${baseChildThreadId}" without a checkpoint ID.`);
|
|
422
|
+
const childRunId = persistedChildRunId ?? currentChildRunId;
|
|
423
|
+
const identity = {
|
|
424
|
+
childRunId,
|
|
425
|
+
childThreadId: branchChildThreadId,
|
|
426
|
+
approvalExecutionScope: require_SubagentExecutionRegistry.getSubagentApprovalExecutionScope(childRunId, resumeAttemptId)
|
|
427
|
+
};
|
|
428
|
+
const lease = this.executions.beginIdentityPreparation(execution, identity);
|
|
429
|
+
await this.prepareCheckpointFork(sourceCheckpoints, branchChildThreadId, lease);
|
|
430
|
+
return {
|
|
431
|
+
identity,
|
|
432
|
+
lease
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
prepareIdentity(execution, identity) {
|
|
436
|
+
return {
|
|
437
|
+
identity,
|
|
438
|
+
lease: this.executions.beginIdentityPreparation(execution, identity)
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
async prepareCheckpointFork(sources, targetThreadId, lease) {
|
|
442
|
+
const checkpointer = this.checkpointer;
|
|
443
|
+
try {
|
|
444
|
+
if (checkpointer == null || sources.length === 0 || sources.every((source) => source.threadId === targetThreadId)) {
|
|
445
|
+
lease.assertOwned();
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const targetExists = await this.checkpointThreadExists(targetThreadId);
|
|
449
|
+
lease.assertOwned();
|
|
450
|
+
if (!targetExists) lease.markCheckpointCreated(() => checkpointer.deleteThread(targetThreadId));
|
|
451
|
+
await this.forkCheckpointSnapshot(sources, targetThreadId);
|
|
452
|
+
lease.assertOwned();
|
|
453
|
+
} catch (error) {
|
|
454
|
+
return lease.rollback(error);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
async checkpointThreadExists(threadId) {
|
|
458
|
+
if (this.checkpointer == null) return false;
|
|
459
|
+
for await (const _tuple of this.checkpointer.list({ configurable: { thread_id: threadId } }, { limit: 1 })) return true;
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
/** Captures one exact checkpoint head per namespace for a child thread. */
|
|
463
|
+
async getLatestCheckpointSnapshot(threadId) {
|
|
464
|
+
if (this.checkpointer == null) return [];
|
|
465
|
+
const checkpointsByNamespace = /* @__PURE__ */ new Map();
|
|
466
|
+
for await (const tuple of this.checkpointer.list({ configurable: { thread_id: threadId } })) {
|
|
467
|
+
const checkpoint = getCheckpointReference(tuple);
|
|
468
|
+
const current = checkpoint == null ? void 0 : checkpointsByNamespace.get(checkpoint.checkpointNs);
|
|
469
|
+
if (checkpoint != null && (current == null || checkpoint.checkpointId.localeCompare(current.checkpointId) > 0)) checkpointsByNamespace.set(checkpoint.checkpointNs, checkpoint);
|
|
470
|
+
}
|
|
471
|
+
return [...checkpointsByNamespace.values()].sort((left, right) => left.checkpointNs.localeCompare(right.checkpointNs));
|
|
472
|
+
}
|
|
473
|
+
/** Copies exact checkpoint lineages, including pending task writes. */
|
|
474
|
+
async forkCheckpointSnapshot(sources, targetThreadId) {
|
|
475
|
+
if (this.checkpointer == null || sources.length === 0 || sources.every((source) => source.threadId === targetThreadId)) return;
|
|
476
|
+
for (const source of sources) {
|
|
477
|
+
const tuples = [];
|
|
478
|
+
const visited = /* @__PURE__ */ new Set();
|
|
479
|
+
let tuple = await this.checkpointer.getTuple({ configurable: {
|
|
480
|
+
thread_id: source.threadId,
|
|
481
|
+
checkpoint_ns: source.checkpointNs,
|
|
482
|
+
checkpoint_id: source.checkpointId
|
|
483
|
+
} });
|
|
484
|
+
if (tuple == null) throw new Error(`Subagent checkpoint "${source.checkpointId}" is unavailable.`);
|
|
485
|
+
for (;;) {
|
|
486
|
+
const reference = getCheckpointReference(tuple);
|
|
487
|
+
if (reference == null) throw new Error("Subagent checkpoint lineage contains an invalid tuple.");
|
|
488
|
+
if (reference.threadId !== source.threadId || reference.checkpointNs !== source.checkpointNs) throw new Error("Subagent checkpoint lineage escapes its source namespace.");
|
|
489
|
+
const lineageKey = JSON.stringify([
|
|
490
|
+
reference.threadId,
|
|
491
|
+
reference.checkpointNs,
|
|
492
|
+
reference.checkpointId
|
|
493
|
+
]);
|
|
494
|
+
if (visited.has(lineageKey)) throw new Error("Subagent checkpoint lineage contains a cycle.");
|
|
495
|
+
visited.add(lineageKey);
|
|
496
|
+
tuples.push(tuple);
|
|
497
|
+
if (tuple.parentConfig == null) break;
|
|
498
|
+
tuple = await this.checkpointer.getTuple(tuple.parentConfig);
|
|
499
|
+
if (tuple == null) throw new Error("Subagent checkpoint lineage is incomplete.");
|
|
500
|
+
}
|
|
501
|
+
tuples.reverse();
|
|
502
|
+
for (const lineageTuple of tuples) {
|
|
503
|
+
const checkpointNs = getConfigurableString(lineageTuple.config, "checkpoint_ns") ?? "";
|
|
504
|
+
const parentCheckpointId = getConfigurableString(lineageTuple.parentConfig, "checkpoint_id");
|
|
505
|
+
const storedConfig = await this.checkpointer.put({ configurable: {
|
|
506
|
+
thread_id: targetThreadId,
|
|
507
|
+
checkpoint_ns: checkpointNs,
|
|
508
|
+
...parentCheckpointId == null ? {} : { checkpoint_id: parentCheckpointId }
|
|
509
|
+
} }, (0, _langchain_langgraph.copyCheckpoint)(lineageTuple.checkpoint), lineageTuple.metadata ?? {
|
|
510
|
+
source: "fork",
|
|
511
|
+
step: -1,
|
|
512
|
+
parents: {}
|
|
513
|
+
}, lineageTuple.checkpoint.channel_versions);
|
|
514
|
+
const writesByTask = /* @__PURE__ */ new Map();
|
|
515
|
+
for (const [taskId, channel, value] of lineageTuple.pendingWrites ?? []) {
|
|
516
|
+
let writes = writesByTask.get(taskId);
|
|
517
|
+
if (writes == null) {
|
|
518
|
+
writes = [];
|
|
519
|
+
writesByTask.set(taskId, writes);
|
|
520
|
+
}
|
|
521
|
+
writes.push([channel, value]);
|
|
522
|
+
}
|
|
523
|
+
for (const [taskId, writes] of writesByTask) await this.checkpointer.putWrites(storedConfig, writes, taskId);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
async createResumeManifest(records) {
|
|
528
|
+
if (this.checkpointer == null) return;
|
|
529
|
+
const executions = [];
|
|
530
|
+
for (const record of records) {
|
|
531
|
+
const { identity, binding, activeRun } = record.snapshot;
|
|
532
|
+
if (identity == null) continue;
|
|
533
|
+
const { parentToolCallId } = record.address;
|
|
534
|
+
const checkpoints = await this.getLatestCheckpointSnapshot(identity.childThreadId);
|
|
535
|
+
if (checkpoints.length === 0) continue;
|
|
536
|
+
const configuredHookSessionId = activeRun?.invokeConfig?.configurable?.run_id;
|
|
537
|
+
const hookSessionId = typeof configuredHookSessionId === "string" && configuredHookSessionId.length > 0 ? configuredHookSessionId : this.parentRunId;
|
|
538
|
+
const approvalReplays = this.hookRegistry?.snapshotPendingToolApprovals(identity.approvalExecutionScope, identity.approvalExecutionScope) ?? [];
|
|
539
|
+
const descendant = activeRun?.pendingInterrupts.map((pendingInterrupt) => require_SubagentReplay.getSubagentResumeManifest(pendingInterrupt.value)).find((manifest) => manifest != null);
|
|
540
|
+
const graphState = activeRun?.graph.createSubagentResumeState(hookSessionId) ?? {
|
|
541
|
+
toolCallSteps: [],
|
|
542
|
+
toolSessions: [],
|
|
543
|
+
toolNodes: [],
|
|
544
|
+
eagerToolUsage: [],
|
|
545
|
+
eagerToolSuppressions: []
|
|
546
|
+
};
|
|
547
|
+
executions.push({
|
|
548
|
+
parentToolCallId,
|
|
549
|
+
childRunId: identity.childRunId,
|
|
550
|
+
...binding?.subagentType == null ? {} : { subagentType: binding.subagentType },
|
|
551
|
+
...binding?.configId == null ? {} : { configId: binding.configId },
|
|
552
|
+
approvalExecutionScope: identity.approvalExecutionScope,
|
|
553
|
+
checkpoints,
|
|
554
|
+
graphState,
|
|
555
|
+
approvalReplays,
|
|
556
|
+
...descendant == null ? {} : { descendant }
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
if (executions.length === 0) return;
|
|
560
|
+
return {
|
|
561
|
+
version: 1,
|
|
562
|
+
executions
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
getResumeManifest(parentToolCallIds, config) {
|
|
566
|
+
return this.createResumeManifest(this.executions.selectForResume({
|
|
567
|
+
parentToolCallIds,
|
|
568
|
+
config
|
|
569
|
+
}));
|
|
570
|
+
}
|
|
571
|
+
getChildCheckpointThreadIds() {
|
|
572
|
+
return this.executions.getCheckpointThreadIds((activeRun) => this.getGraphChildCheckpointThreadIds(activeRun.graph));
|
|
573
|
+
}
|
|
574
|
+
resetCheckpointThreadIds() {
|
|
575
|
+
this.executions.resetCheckpointThreadIds();
|
|
576
|
+
}
|
|
577
|
+
getGraphChildCheckpointThreadIds(graph) {
|
|
578
|
+
return graph.getChildCheckpointThreadIds?.() ?? [];
|
|
579
|
+
}
|
|
580
|
+
clearChildGraph(graph) {
|
|
581
|
+
this.executions.rememberCheckpointThread(...this.getGraphChildCheckpointThreadIds(graph));
|
|
582
|
+
graph.clearHeavyState();
|
|
583
|
+
}
|
|
584
|
+
clearHeavyState() {
|
|
585
|
+
this.executions.clear((record) => {
|
|
586
|
+
if (record.activeRun != null) this.clearChildGraph(record.activeRun.graph);
|
|
587
|
+
if (record.identity != null) this.hookRegistry?.clearSession(record.identity.approvalExecutionScope);
|
|
588
|
+
});
|
|
589
|
+
this.replayCheckpointWorkflow = void 0;
|
|
590
|
+
}
|
|
591
|
+
async getResumeCheckpointMarker(resumeExecution, parentToolCallId) {
|
|
592
|
+
if (this.checkpointer == null) return;
|
|
593
|
+
let fallback;
|
|
594
|
+
for (const checkpoint of resumeExecution.checkpoints) {
|
|
595
|
+
const messages = getTupleMessages(await this.checkpointer.getTuple({ configurable: {
|
|
596
|
+
thread_id: checkpoint.threadId,
|
|
597
|
+
checkpoint_ns: checkpoint.checkpointNs,
|
|
598
|
+
checkpoint_id: checkpoint.checkpointId
|
|
599
|
+
} }));
|
|
600
|
+
const marker = getSubagentCheckpointMarker(messages, parentToolCallId);
|
|
601
|
+
if (marker?.settledOutput != null) return {
|
|
602
|
+
marker,
|
|
603
|
+
messages
|
|
604
|
+
};
|
|
605
|
+
if (marker != null && fallback == null) fallback = {
|
|
606
|
+
marker,
|
|
607
|
+
messages
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
return fallback;
|
|
611
|
+
}
|
|
612
|
+
async getSettledToolOutput(call, config) {
|
|
613
|
+
const parentToolCallId = call.id;
|
|
614
|
+
if (this.humanInTheLoop?.enabled !== true || this.checkpointer == null || parentToolCallId == null || parentToolCallId === "") return;
|
|
615
|
+
const parentConfigurable = config.configurable;
|
|
616
|
+
const threadId = parentConfigurable?.thread_id;
|
|
617
|
+
const execution = this.executions.open({
|
|
618
|
+
threadId: typeof threadId === "string" ? threadId : void 0,
|
|
619
|
+
parentToolCallId,
|
|
620
|
+
parentConfigurable
|
|
621
|
+
});
|
|
622
|
+
const { resumeExecution } = execution;
|
|
623
|
+
const inProcessSettledOutput = execution.settledOutput;
|
|
624
|
+
if (inProcessSettledOutput != null) {
|
|
625
|
+
const persistedSubagentType = getSubagentTypeFromArgs(inProcessSettledOutput.resolvedArgs);
|
|
626
|
+
const subagentType = persistedSubagentType ?? execution.binding?.subagentType ?? getSubagentType(call);
|
|
627
|
+
if (!isResumeExecutionCompatible(resumeExecution, subagentType, subagentType == null ? void 0 : this.configs.get(subagentType), persistedSubagentType != null)) return;
|
|
628
|
+
return deserializeToolOutput(inProcessSettledOutput);
|
|
629
|
+
}
|
|
630
|
+
let resumeCheckpoint;
|
|
631
|
+
if (resumeExecution != null) resumeCheckpoint = await this.getResumeCheckpointMarker(resumeExecution, parentToolCallId);
|
|
632
|
+
let marker = resumeCheckpoint?.marker;
|
|
633
|
+
let messages = resumeCheckpoint?.messages ?? [];
|
|
634
|
+
let persistedSubagentType = getSubagentTypeFromArgs(marker?.settledOutput?.resolvedArgs);
|
|
635
|
+
let subagentType = persistedSubagentType ?? resumeExecution?.subagentType ?? getSubagentType(call);
|
|
636
|
+
let executableConfig = subagentType == null ? void 0 : this.configs.get(subagentType);
|
|
637
|
+
if (!isResumeExecutionCompatible(resumeExecution, subagentType, executableConfig, persistedSubagentType != null)) {
|
|
638
|
+
this.executions.remove(execution);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
const { childThreadId } = await this.resolveChildExecutionIdentity(execution);
|
|
642
|
+
if (marker == null) {
|
|
643
|
+
messages = getCheckpointMessages((await this.checkpointer.getTuple({ configurable: { thread_id: childThreadId } }))?.checkpoint.channel_values.messages);
|
|
644
|
+
marker = getSubagentCheckpointMarker(messages, parentToolCallId);
|
|
645
|
+
persistedSubagentType = getSubagentTypeFromArgs(marker?.settledOutput?.resolvedArgs);
|
|
646
|
+
if (persistedSubagentType != null) {
|
|
647
|
+
subagentType = persistedSubagentType;
|
|
648
|
+
executableConfig = this.configs.get(subagentType);
|
|
649
|
+
}
|
|
650
|
+
if (!isResumeExecutionCompatible(resumeExecution, subagentType, executableConfig, persistedSubagentType != null)) {
|
|
651
|
+
this.executions.remove(execution);
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
const configId = executableConfig?.configId;
|
|
656
|
+
if (!this.bindExecutionDefinition(execution, {
|
|
657
|
+
...subagentType == null ? {} : { subagentType },
|
|
658
|
+
...configId == null ? {} : { configId }
|
|
659
|
+
}, persistedSubagentType != null || resumeExecution?.subagentType != null ? "effective" : "provisional")) return;
|
|
660
|
+
const persistedHookSessionId = marker?.hookSessionId ?? getSubagentHookSessionId(messages);
|
|
661
|
+
const currentHookSessionId = parentConfigurable?.run_id;
|
|
662
|
+
if (persistedHookSessionId != null && typeof currentHookSessionId === "string" && currentHookSessionId.length > 0) this.hookRegistry?.copySession(persistedHookSessionId, currentHookSessionId);
|
|
663
|
+
return marker?.settledOutput == null ? void 0 : deserializeToolOutput(marker.settledOutput);
|
|
664
|
+
}
|
|
665
|
+
async persistSettledToolOutput(call, config, settled) {
|
|
666
|
+
const parentToolCallId = call.id;
|
|
667
|
+
const checkpointer = this.checkpointer;
|
|
668
|
+
if (this.humanInTheLoop?.enabled !== true || checkpointer == null || parentToolCallId == null || parentToolCallId === "") return;
|
|
669
|
+
const parentConfigurable = config.configurable;
|
|
670
|
+
const threadId = parentConfigurable?.thread_id;
|
|
671
|
+
const execution = this.executions.open({
|
|
672
|
+
threadId: typeof threadId === "string" ? threadId : void 0,
|
|
673
|
+
parentToolCallId,
|
|
674
|
+
parentConfigurable
|
|
675
|
+
});
|
|
676
|
+
const { resumeExecution } = execution;
|
|
677
|
+
const resolvedSubagentType = getSubagentTypeFromArgs(settled.resolvedArgs);
|
|
678
|
+
const resolvedDescription = getSubagentDescriptionFromArgs(settled.resolvedArgs);
|
|
679
|
+
const boundInvocation = execution.invocation;
|
|
680
|
+
if (boundInvocation != null && resolvedDescription != null && resolvedDescription !== boundInvocation.description) return;
|
|
681
|
+
const description = boundInvocation?.description ?? resolvedDescription ?? getSubagentDescriptionFromArgs(call.args) ?? "No task description provided";
|
|
682
|
+
const subagentType = resolvedSubagentType ?? execution.binding?.subagentType ?? getSubagentType(call);
|
|
683
|
+
const executableConfig = subagentType == null ? void 0 : this.configs.get(subagentType);
|
|
684
|
+
const configId = resolvedSubagentType == null ? execution.binding?.configId ?? executableConfig?.configId ?? resumeExecution?.configId : executableConfig?.configId;
|
|
685
|
+
if (subagentType == null) return;
|
|
686
|
+
if (!isResumeExecutionCompatible(resumeExecution, subagentType, executableConfig)) {
|
|
687
|
+
this.executions.remove(execution);
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
const persistedOutput = serializeToolOutput(settled);
|
|
691
|
+
const invocation = boundInvocation ?? {
|
|
692
|
+
description,
|
|
693
|
+
subagentType,
|
|
694
|
+
...configId == null ? {} : { configId }
|
|
695
|
+
};
|
|
696
|
+
try {
|
|
697
|
+
await execution.settle({
|
|
698
|
+
definitionAuthority: resolvedSubagentType == null ? "provisional" : "effective",
|
|
699
|
+
fingerprint: getSettlementFingerprint(persistedOutput),
|
|
700
|
+
invocation,
|
|
701
|
+
subagentType,
|
|
702
|
+
...configId == null ? {} : { configId }
|
|
703
|
+
}, persistedOutput, async () => {
|
|
704
|
+
const { childRunId, childThreadId } = await this.resolveChildExecutionIdentity(execution);
|
|
705
|
+
const activeChildRun = execution.activeRun;
|
|
706
|
+
if (activeChildRun != null) {
|
|
707
|
+
await this.persistChildCheckpointMarker(activeChildRun, parentToolCallId, persistedOutput);
|
|
708
|
+
this.clearChildGraph(activeChildRun.graph);
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
this.replayCheckpointWorkflow ??= createReplayCheckpointWorkflow(checkpointer);
|
|
712
|
+
await this.replayCheckpointWorkflow.updateState({ configurable: { thread_id: childThreadId } }, { messages: [createSubagentCheckpointMarkerMessage({
|
|
713
|
+
version: 1,
|
|
714
|
+
parentToolCallId,
|
|
715
|
+
lifecycleComplete: true,
|
|
716
|
+
hookSessionId: typeof parentConfigurable?.run_id === "string" ? parentConfigurable.run_id : this.parentRunId,
|
|
717
|
+
childRunId,
|
|
718
|
+
settledOutput: persistedOutput
|
|
719
|
+
})] }, SUBAGENT_REPLAY_NODE);
|
|
720
|
+
});
|
|
721
|
+
} catch (error) {
|
|
722
|
+
if (error instanceof require_SubagentExecutionRegistry.SubagentDefinitionBindingError || error instanceof require_SubagentExecutionRegistry.SubagentInvocationBindingError || error instanceof require_SubagentExecutionRegistry.SubagentSettlementBindingError) return;
|
|
723
|
+
throw error;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
async persistChildCheckpointMarker(activeChildRun, parentToolCallId, settledOutput) {
|
|
727
|
+
if (this.humanInTheLoop?.enabled !== true || activeChildRun.workflow.updateState == null || activeChildRun.invokeConfig == null) return;
|
|
728
|
+
await activeChildRun.workflow.updateState(activeChildRun.invokeConfig, { messages: [createSubagentCheckpointMarkerMessage({
|
|
729
|
+
version: 1,
|
|
730
|
+
parentToolCallId,
|
|
731
|
+
lifecycleComplete: true,
|
|
732
|
+
hookSessionId: typeof activeChildRun.invokeConfig.configurable?.run_id === "string" ? activeChildRun.invokeConfig.configurable.run_id : this.parentRunId,
|
|
733
|
+
childRunId: activeChildRun.childRunId,
|
|
734
|
+
...settledOutput == null ? {} : { settledOutput }
|
|
735
|
+
})] }, activeChildRun.checkpointNodeId);
|
|
736
|
+
}
|
|
737
|
+
execute(params) {
|
|
738
|
+
const executableConfig = this.configs.get(params.subagentType);
|
|
739
|
+
if (executableConfig == null) {
|
|
740
|
+
const available = [...this.configs.keys()].join(", ");
|
|
741
|
+
return Promise.resolve({
|
|
742
|
+
content: `Error: Unknown subagent type "${params.subagentType}". Available types: ${available}`,
|
|
743
|
+
messages: []
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
if (this.maxDepth <= 0) return Promise.resolve({
|
|
747
|
+
content: "Error: Maximum subagent nesting depth exceeded.",
|
|
748
|
+
messages: []
|
|
749
|
+
});
|
|
750
|
+
if (require_childGraphConfig.isGraphSubagentConfig(executableConfig) && this.humanInTheLoop?.enabled === true) return Promise.resolve({
|
|
751
|
+
content: "Error: Human-in-the-loop execution is not yet supported for graph subagents.",
|
|
752
|
+
messages: []
|
|
753
|
+
});
|
|
754
|
+
if (this.humanInTheLoop?.enabled === true && (params.parentToolCallId == null || params.parentToolCallId === "")) return Promise.resolve({
|
|
755
|
+
content: "Error: Resumable subagent execution requires a parent tool call ID.",
|
|
756
|
+
messages: []
|
|
757
|
+
});
|
|
758
|
+
const execution = this.executions.open({
|
|
759
|
+
threadId: params.threadId,
|
|
760
|
+
parentToolCallId: params.parentToolCallId ?? (0, nanoid.nanoid)(8),
|
|
761
|
+
parentConfigurable: params.parentConfigurable
|
|
762
|
+
});
|
|
763
|
+
try {
|
|
764
|
+
return execution.execute({
|
|
765
|
+
description: params.description,
|
|
766
|
+
subagentType: params.subagentType,
|
|
767
|
+
...executableConfig.configId == null ? {} : { configId: executableConfig.configId }
|
|
768
|
+
}, () => this.executeOnce(params, execution, executableConfig));
|
|
769
|
+
} catch (error) {
|
|
770
|
+
if (error instanceof require_SubagentExecutionRegistry.SubagentDefinitionBindingError) return Promise.resolve({
|
|
771
|
+
content: SUBAGENT_CONFIG_CHANGED_MESSAGE,
|
|
772
|
+
messages: []
|
|
773
|
+
});
|
|
774
|
+
if (error instanceof require_SubagentExecutionRegistry.SubagentInvocationBindingError) return Promise.resolve({
|
|
775
|
+
content: SUBAGENT_INVOCATION_CHANGED_MESSAGE,
|
|
776
|
+
messages: []
|
|
777
|
+
});
|
|
778
|
+
throw error;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
async executeOnce(params, execution, executableConfig) {
|
|
82
782
|
const { description, subagentType, threadId, parentToolCallId } = params;
|
|
83
783
|
/** Captured ONCE per execution, preferring the controller the parent
|
|
84
784
|
* tool batch captured at ITS entry (before PreToolUse hooks): a failed
|
|
@@ -88,39 +788,51 @@ var SubagentExecutor = class {
|
|
|
88
788
|
* new run's breaker from an old child's stream-limit breach. Signal and
|
|
89
789
|
* trip target both bind to this capture. */
|
|
90
790
|
const childBreaker = params.breaker ?? this.resolveBreakerController();
|
|
91
|
-
const childSignal = this.composeChildSignal(childBreaker);
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
791
|
+
const childSignal = this.composeChildSignal(childBreaker, params.signal);
|
|
792
|
+
const { parentToolCallId: executionSuffix } = execution.address;
|
|
793
|
+
const { resumeExecution } = execution;
|
|
794
|
+
if (!isResumeExecutionCompatible(resumeExecution, subagentType, executableConfig)) {
|
|
795
|
+
this.executions.remove(execution);
|
|
796
|
+
return {
|
|
797
|
+
content: SUBAGENT_CONFIG_CHANGED_MESSAGE,
|
|
798
|
+
messages: []
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
let identity;
|
|
802
|
+
try {
|
|
803
|
+
identity = await this.resolveChildExecutionIdentity(execution);
|
|
804
|
+
execution.assertUsable(childSignal);
|
|
805
|
+
} catch (error) {
|
|
806
|
+
if (error instanceof require_streamLimits.StreamLimitExceededError) throw error;
|
|
807
|
+
return {
|
|
808
|
+
content: SUBAGENT_RESOLUTION_ERROR_MESSAGE,
|
|
809
|
+
messages: []
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
const { childRunId, childThreadId, approvalExecutionScope } = identity;
|
|
813
|
+
if (!this.bindExecutionDefinition(execution, {
|
|
814
|
+
subagentType,
|
|
815
|
+
...executableConfig.configId == null ? {} : { configId: executableConfig.configId }
|
|
816
|
+
}, "effective")) return {
|
|
817
|
+
content: SUBAGENT_CONFIG_CHANGED_MESSAGE,
|
|
99
818
|
messages: []
|
|
100
819
|
};
|
|
101
|
-
const
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* `ask` is treated identically to `deny` in the subagent context:
|
|
119
|
-
* subagents are non-interactive, so there is no prompt path for `ask`.
|
|
120
|
-
* Both decisions block execution and return a "Blocked" tool result.
|
|
121
|
-
*/
|
|
122
|
-
if (hookResult.decision === "deny" || hookResult.decision === "ask") return {
|
|
123
|
-
content: `Blocked: ${hookResult.reason ?? "Blocked by hook"}`,
|
|
820
|
+
const completedChildResult = execution.completedResult;
|
|
821
|
+
if (completedChildResult != null) return completedChildResult;
|
|
822
|
+
let config;
|
|
823
|
+
try {
|
|
824
|
+
config = await this.resolveExecutionConfig(executableConfig, execution, {
|
|
825
|
+
childRunId,
|
|
826
|
+
childSignal,
|
|
827
|
+
threadId,
|
|
828
|
+
parentToolCallId,
|
|
829
|
+
parentConfigurable: params.parentConfigurable
|
|
830
|
+
});
|
|
831
|
+
execution.assertUsable(childSignal);
|
|
832
|
+
} catch (error) {
|
|
833
|
+
if (error instanceof require_streamLimits.StreamLimitExceededError) throw error;
|
|
834
|
+
return {
|
|
835
|
+
content: SUBAGENT_RESOLUTION_ERROR_MESSAGE,
|
|
124
836
|
messages: []
|
|
125
837
|
};
|
|
126
838
|
}
|
|
@@ -136,18 +848,47 @@ var SubagentExecutor = class {
|
|
|
136
848
|
* recoverable "no tools" path for registry-but-no-handler configs.
|
|
137
849
|
*/
|
|
138
850
|
const hasToolExecuteHandler = parentRegistry?.getHandler("on_tool_execute") != null;
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
851
|
+
const childPlan = require_childGraphConfig.createChildGraphPlan({
|
|
852
|
+
config,
|
|
853
|
+
executionSuffix,
|
|
854
|
+
parentAgentId: this.parentAgentId,
|
|
855
|
+
parentMaxDepth: this.maxDepth,
|
|
856
|
+
keepToolDefinitions: hasToolExecuteHandler
|
|
857
|
+
});
|
|
858
|
+
const childAgentId = childPlan.subjectAgentId;
|
|
859
|
+
const currentHookSessionId = asNonEmptyString(params.parentConfigurable?.run_id) ?? this.executionContext.hookSessionId;
|
|
860
|
+
const childExecutionContext = {
|
|
861
|
+
rootRunId: this.executionContext.rootRunId,
|
|
862
|
+
hookSessionId: currentHookSessionId,
|
|
863
|
+
depth: this.executionContext.depth + 1,
|
|
864
|
+
ancestry: [...this.executionContext.ancestry, {
|
|
865
|
+
subagentRunId: childRunId,
|
|
866
|
+
subagentType,
|
|
867
|
+
subagentKind: childPlan.kind,
|
|
868
|
+
subagentAgentId: childAgentId,
|
|
869
|
+
parentRunId: this.parentRunId,
|
|
870
|
+
parentAgentId: this.parentAgentId,
|
|
871
|
+
parentToolCallId
|
|
872
|
+
}]
|
|
873
|
+
};
|
|
874
|
+
const memberRecursionLimit = (config.maxTurns ?? 25) * 3;
|
|
875
|
+
const recursionLimit = memberRecursionLimit * (childPlan.kind === "graph" ? childPlan.agents.length : 1);
|
|
142
876
|
const hostUsageSink = this.usageSink;
|
|
143
|
-
|
|
877
|
+
let subagentUsageSink;
|
|
878
|
+
if (hostUsageSink != null) subagentUsageSink = (event) => hostUsageSink({
|
|
879
|
+
...event,
|
|
880
|
+
runId: this.executionContext.rootRunId
|
|
881
|
+
});
|
|
882
|
+
const cachedChildRun = execution.activeRun;
|
|
883
|
+
const childGraphInput = {
|
|
144
884
|
runId: childRunId,
|
|
145
885
|
signal: childSignal,
|
|
146
|
-
agents:
|
|
886
|
+
agents: childPlan.agents,
|
|
147
887
|
langfuse: this.langfuse,
|
|
148
888
|
tokenCounter: this.tokenCounter,
|
|
149
889
|
streamLimits: this.streamLimits,
|
|
150
890
|
subagentScope: true,
|
|
891
|
+
subagentExecutionContext: childExecutionContext,
|
|
151
892
|
/**
|
|
152
893
|
* Forwarded so the child graph's own `SubagentExecutor` (created in
|
|
153
894
|
* its `createAgentNode` when `allowNested` keeps subagentConfigs)
|
|
@@ -155,39 +896,52 @@ var SubagentExecutor = class {
|
|
|
155
896
|
* level attaches its own capture callback — `workflow.invoke` replaces
|
|
156
897
|
* the inherited callback chain, so a single top-level handler would
|
|
157
898
|
* never see grandchild model calls.
|
|
158
|
-
*
|
|
159
|
-
* The wrapper rewrites `runId` to THIS executor's parent run: nested
|
|
160
|
-
* executors emit with their own `parentRunId` (a `*_sub_*` child id),
|
|
161
|
-
* and each wrapper layer rewrites upward, so by the time an event
|
|
162
|
-
* reaches the host sink its `runId` is the ROOT run — hosts keying
|
|
163
|
-
* billing by run id never see intermediate child run ids there
|
|
164
|
-
* (`subagentRunId` still identifies the emitting child).
|
|
165
899
|
*/
|
|
166
|
-
subagentUsageSink
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
900
|
+
subagentUsageSink
|
|
901
|
+
};
|
|
902
|
+
let childGraph = cachedChildRun?.graph;
|
|
903
|
+
if (childGraph == null && childPlan.kind === "graph") {
|
|
904
|
+
if (this.createChildGraphByKind == null) return {
|
|
905
|
+
content: "Error: Graph subagent execution requires a polymorphic child graph factory.",
|
|
906
|
+
messages: []
|
|
907
|
+
};
|
|
908
|
+
childGraph = this.createChildGraphByKind({
|
|
909
|
+
kind: "multi-agent",
|
|
910
|
+
input: {
|
|
911
|
+
...childGraphInput,
|
|
912
|
+
edges: childPlan.edges,
|
|
913
|
+
resultAgentId: childPlan.resultAgentId,
|
|
914
|
+
memberRecursionLimit
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
childGraph ??= this.createChildGraph(childGraphInput);
|
|
171
919
|
let forwarding;
|
|
172
920
|
if (forwardingEnabled) forwarding = this.createForwarderCallback({
|
|
173
921
|
parentRegistry,
|
|
174
922
|
subagentType,
|
|
923
|
+
subagentKind: childPlan.kind,
|
|
175
924
|
subagentAgentId: childAgentId,
|
|
176
925
|
childRunId,
|
|
177
|
-
parentToolCallId
|
|
178
|
-
});
|
|
179
|
-
const forwarder = forwarding?.handler;
|
|
180
|
-
if (forwarder) await this.emitSubagentUpdate(parentRegistry, {
|
|
181
|
-
childRunId,
|
|
182
|
-
subagentType,
|
|
183
|
-
subagentAgentId: childAgentId,
|
|
184
926
|
parentToolCallId,
|
|
185
|
-
|
|
186
|
-
label: `Subagent "${subagentType}" started`
|
|
927
|
+
executionContext: childExecutionContext
|
|
187
928
|
});
|
|
929
|
+
const forwarder = forwarding?.handler;
|
|
930
|
+
let childAlreadyStarted = execution.started;
|
|
931
|
+
let childAlreadyCompleted = execution.completed;
|
|
188
932
|
let result;
|
|
933
|
+
let recoveredComplete = false;
|
|
934
|
+
let recoveredInProgress = false;
|
|
189
935
|
try {
|
|
190
|
-
const workflow = childGraph.createWorkflow();
|
|
936
|
+
const workflow = cachedChildRun?.workflow ?? childGraph.createWorkflow();
|
|
937
|
+
const activeChildRun = execution.activate(cachedChildRun ?? {
|
|
938
|
+
graph: childGraph,
|
|
939
|
+
workflow,
|
|
940
|
+
pendingInterrupts: [],
|
|
941
|
+
childAgentId,
|
|
942
|
+
checkpointNodeId: childPlan.checkpointNodeId,
|
|
943
|
+
childRunId
|
|
944
|
+
});
|
|
191
945
|
/**
|
|
192
946
|
* When `parentHandlerRegistry` is provided (forwarding mode), attach a
|
|
193
947
|
* lightweight callback that intercepts the child's `on_custom_event`
|
|
@@ -217,54 +971,149 @@ var SubagentExecutor = class {
|
|
|
217
971
|
if (this.usageSink) callbackHandlers.push(createUsageCaptureHandler({
|
|
218
972
|
sink: this.usageSink,
|
|
219
973
|
subagentType,
|
|
974
|
+
subagentKind: childPlan.kind,
|
|
220
975
|
subagentRunId: childRunId,
|
|
221
976
|
subagentAgentId: childAgentId,
|
|
222
977
|
parentRunId: this.parentRunId,
|
|
223
|
-
|
|
224
|
-
|
|
978
|
+
executionContext: childExecutionContext,
|
|
979
|
+
memberInputs: childPlan.memberInputs
|
|
225
980
|
}));
|
|
226
981
|
const callbacks = callbackHandlers;
|
|
227
982
|
/**
|
|
228
|
-
* Inherit the parent's host `configurable`
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
* Run-identity propagation is intentional and matches the
|
|
235
|
-
* convention this executor itself already uses for `SubagentStart`
|
|
236
|
-
* / `SubagentStop` hooks (`sessionId: this.parentRunId`): the
|
|
237
|
-
* subagent runs under the parent's session scope, not its own.
|
|
238
|
-
* Forwarding `run_id` / `parent_run_id` / `thread_id` makes
|
|
239
|
-
* `ToolNode`'s hook lookups (`hasHookFor(eventName, runId)`),
|
|
240
|
-
* `ToolOutputReferenceRegistry` keying, and trace lineage all
|
|
241
|
-
* resolve to the parent's session for tools dispatched from the
|
|
242
|
-
* subagent — so `PreToolUse` / `PostToolUse` hooks the host
|
|
243
|
-
* registered against the parent's run fire for subagent tool
|
|
244
|
-
* calls too. "Same run" matches the user-perceptual mental model.
|
|
983
|
+
* Inherit the parent's host `configurable` while binding LangGraph's
|
|
984
|
+
* checkpoint identity to a stable child id derived from the durable
|
|
985
|
+
* parent thread and checkpoint fork. The parent thread id cannot be
|
|
986
|
+
* reused here: parent and child share one checkpointer when nested HITL
|
|
987
|
+
* is enabled, and root checkpoint namespaces are normalized by
|
|
988
|
+
* LangGraph, so a shared `thread_id` would collide with the parent.
|
|
245
989
|
*
|
|
246
|
-
* `
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
* NOTE: a future revision will likely make this configurable per
|
|
251
|
-
* spawn type — e.g. a background / async subagent that runs after
|
|
252
|
-
* the parent's run completes wants isolation, not inheritance.
|
|
253
|
-
* For now the inheritance path matches LibreChat's primary use
|
|
254
|
-
* case (synchronous subagents within a single user turn).
|
|
990
|
+
* `run_id` still propagates as the parent run id, which is the key used
|
|
991
|
+
* for session-scoped hook lookup. Child hook inputs therefore retain
|
|
992
|
+
* the parent policy scope while their `threadId` truthfully identifies
|
|
993
|
+
* the independently checkpointed child execution.
|
|
255
994
|
*/
|
|
256
995
|
const inheritedConfigurable = sanitizeChildConfigurable(params.parentConfigurable);
|
|
257
|
-
|
|
258
|
-
|
|
996
|
+
const { resumeAttemptId } = execution.address;
|
|
997
|
+
if (cachedChildRun == null && resumeExecution != null) childGraph.restoreSubagentResumeState(resumeExecution.graphState, currentHookSessionId);
|
|
998
|
+
const childConfigurable = { ...inheritedConfigurable };
|
|
999
|
+
if (this.humanInTheLoop?.enabled === true) childConfigurable[require_types.TOOL_APPROVAL_EXECUTION_SCOPE_CONFIG_KEY] = approvalExecutionScope;
|
|
1000
|
+
if (resumeExecution?.descendant != null) {
|
|
1001
|
+
childConfigurable[require_SubagentReplay.SUBAGENT_RESUME_MANIFEST_CONFIG_KEY] = resumeExecution.descendant;
|
|
1002
|
+
childConfigurable[require_SubagentReplay.SUBAGENT_RESUME_ATTEMPT_CONFIG_KEY] = resumeAttemptId;
|
|
1003
|
+
}
|
|
1004
|
+
childConfigurable.thread_id = this.humanInTheLoop?.enabled === true ? childThreadId : inheritedConfigurable.thread_id ?? childRunId;
|
|
1005
|
+
const childInvokeConfig = {
|
|
1006
|
+
recursionLimit,
|
|
259
1007
|
signal: childSignal,
|
|
260
1008
|
callbacks,
|
|
261
1009
|
runName: `subagent:${subagentType}`,
|
|
262
|
-
configurable:
|
|
263
|
-
|
|
264
|
-
|
|
1010
|
+
configurable: childConfigurable
|
|
1011
|
+
};
|
|
1012
|
+
activeChildRun.invokeConfig = childInvokeConfig;
|
|
1013
|
+
if (cachedChildRun == null && this.humanInTheLoop?.enabled === true) {
|
|
1014
|
+
/** Rehydrate child-owned interrupt state when a host rebuilds Run
|
|
1015
|
+
* around the same durable checkpointer after a process boundary. */
|
|
1016
|
+
const persistedState = await workflow.getState(childInvokeConfig);
|
|
1017
|
+
const checkpointMessages = getCheckpointMessages(persistedState.values?.messages);
|
|
1018
|
+
const persistedHookSessionId = getSubagentHookSessionId(checkpointMessages);
|
|
1019
|
+
if (persistedHookSessionId != null) this.hookRegistry?.copySession(persistedHookSessionId, currentHookSessionId);
|
|
1020
|
+
const persistedInterrupts = getPersistedInterrupts(persistedState);
|
|
1021
|
+
if (persistedInterrupts.length > 0) {
|
|
1022
|
+
activeChildRun.pendingInterrupts = persistedInterrupts;
|
|
1023
|
+
execution.markStarted();
|
|
1024
|
+
childAlreadyStarted = true;
|
|
1025
|
+
} else if (persistedState.next.length > 0) {
|
|
1026
|
+
recoveredInProgress = true;
|
|
1027
|
+
childAlreadyStarted = true;
|
|
1028
|
+
execution.markStarted();
|
|
1029
|
+
} else if (persistedState.next.length === 0) {
|
|
1030
|
+
const persistedMessages = getPersistedMessages(persistedState);
|
|
1031
|
+
if (persistedMessages != null) {
|
|
1032
|
+
const marker = getSubagentCheckpointMarker(checkpointMessages, executionSuffix);
|
|
1033
|
+
result = { messages: persistedMessages };
|
|
1034
|
+
recoveredComplete = true;
|
|
1035
|
+
childAlreadyStarted = true;
|
|
1036
|
+
childAlreadyCompleted = marker?.lifecycleComplete === true;
|
|
1037
|
+
execution.markStarted();
|
|
1038
|
+
}
|
|
265
1039
|
}
|
|
266
|
-
}
|
|
1040
|
+
}
|
|
1041
|
+
if (!recoveredComplete) {
|
|
1042
|
+
const childResumeMap = getChildResumeMap(activeChildRun.pendingInterrupts, params.parentConfigurable);
|
|
1043
|
+
if (activeChildRun.pendingInterrupts.length > 0 && childResumeMap == null) throw new _langchain_langgraph.GraphInterrupt(activeChildRun.pendingInterrupts);
|
|
1044
|
+
let childInput;
|
|
1045
|
+
if (childResumeMap != null) childInput = new _langchain_langgraph.Command({ resume: childResumeMap });
|
|
1046
|
+
else if (recoveredInProgress) childInput = null;
|
|
1047
|
+
else childInput = { messages: [new _langchain_core_messages.HumanMessage({
|
|
1048
|
+
content: description,
|
|
1049
|
+
additional_kwargs: {
|
|
1050
|
+
[SUBAGENT_HOOK_SESSION_KEY]: currentHookSessionId,
|
|
1051
|
+
[SUBAGENT_RUN_ID_KEY]: childRunId
|
|
1052
|
+
}
|
|
1053
|
+
})] };
|
|
1054
|
+
if (!childAlreadyStarted && this.hookRegistry?.hasHookFor("SubagentStart", currentHookSessionId) === true) {
|
|
1055
|
+
const hookResult = await require_executeHooks.executeHooks({
|
|
1056
|
+
registry: this.hookRegistry,
|
|
1057
|
+
input: {
|
|
1058
|
+
hook_event_name: "SubagentStart",
|
|
1059
|
+
runId: currentHookSessionId,
|
|
1060
|
+
threadId,
|
|
1061
|
+
parentAgentId: this.parentAgentId,
|
|
1062
|
+
agentId: childAgentId,
|
|
1063
|
+
agentType: subagentType,
|
|
1064
|
+
inputs: [new _langchain_core_messages.HumanMessage(description)]
|
|
1065
|
+
},
|
|
1066
|
+
sessionId: currentHookSessionId,
|
|
1067
|
+
matchQuery: subagentType
|
|
1068
|
+
}).catch(() => HOOK_FALLBACK);
|
|
1069
|
+
if (hookResult.decision === "deny" || hookResult.decision === "ask") {
|
|
1070
|
+
this.clearChildGraph(childGraph);
|
|
1071
|
+
execution.releaseActiveRun();
|
|
1072
|
+
this.executions.remove(execution);
|
|
1073
|
+
return {
|
|
1074
|
+
content: `Blocked: ${hookResult.reason ?? "Blocked by hook"}`,
|
|
1075
|
+
messages: []
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
execution.markStarted();
|
|
1080
|
+
if (forwarder && !childAlreadyStarted) await this.emitSubagentUpdate(parentRegistry, {
|
|
1081
|
+
childRunId,
|
|
1082
|
+
subagentType,
|
|
1083
|
+
subagentKind: childPlan.kind,
|
|
1084
|
+
subagentAgentId: childAgentId,
|
|
1085
|
+
parentToolCallId,
|
|
1086
|
+
executionContext: childExecutionContext,
|
|
1087
|
+
phase: "start",
|
|
1088
|
+
label: `Subagent "${subagentType}" started`
|
|
1089
|
+
});
|
|
1090
|
+
let childResult;
|
|
1091
|
+
if (this.humanInTheLoop?.enabled === true)
|
|
1092
|
+
/** Execute as an independently checkpointed root instead of inheriting
|
|
1093
|
+
* the parent's Pregel namespace. Parent decisions are routed explicitly
|
|
1094
|
+
* by interrupt id, so concurrent children keep isolated resume state. */
|
|
1095
|
+
childResult = await _langchain_core_singletons.AsyncLocalStorageProviderSingleton.runWithConfig(childInvokeConfig, () => workflow.invoke(childInput, childInvokeConfig));
|
|
1096
|
+
else childResult = await workflow.invoke(childInput, childInvokeConfig);
|
|
1097
|
+
const childInterrupts = (0, _langchain_langgraph.isInterrupted)(childResult) ? childResult[_langchain_langgraph.INTERRUPT] : void 0;
|
|
1098
|
+
if (childInterrupts != null && childInterrupts.length > 0) throw new _langchain_langgraph.GraphInterrupt(childInterrupts);
|
|
1099
|
+
result = childResult;
|
|
1100
|
+
}
|
|
267
1101
|
} catch (error) {
|
|
1102
|
+
if ((0, _langchain_langgraph.isGraphInterrupt)(error)) {
|
|
1103
|
+
const activeChildRun = execution.activeRun;
|
|
1104
|
+
if (activeChildRun != null) {
|
|
1105
|
+
activeChildRun.pendingInterrupts = error.interrupts;
|
|
1106
|
+
execution.markInterrupted();
|
|
1107
|
+
}
|
|
1108
|
+
const resumeManifest = activeChildRun == null || parentToolCallId == null ? void 0 : await this.createResumeManifest([execution]);
|
|
1109
|
+
await forwarding?.drain();
|
|
1110
|
+
throw new _langchain_langgraph.GraphInterrupt(addSubagentScope(error.interrupts, {
|
|
1111
|
+
run_id: childRunId,
|
|
1112
|
+
agent_id: childAgentId,
|
|
1113
|
+
subagent_type: subagentType,
|
|
1114
|
+
parent_tool_call_id: parentToolCallId
|
|
1115
|
+
}, resumeManifest));
|
|
1116
|
+
}
|
|
268
1117
|
/** Aborted before any observational work below: parallel siblings — in
|
|
269
1118
|
* this executor and, via the graph-scoped breaker, under other
|
|
270
1119
|
* parallel agent nodes — stream on the composed child signal, and
|
|
@@ -279,14 +1128,17 @@ var SubagentExecutor = class {
|
|
|
279
1128
|
await this.emitSubagentUpdate(parentRegistry, {
|
|
280
1129
|
childRunId,
|
|
281
1130
|
subagentType,
|
|
1131
|
+
subagentKind: childPlan.kind,
|
|
282
1132
|
subagentAgentId: childAgentId,
|
|
283
1133
|
parentToolCallId,
|
|
1134
|
+
executionContext: childExecutionContext,
|
|
284
1135
|
phase: "error",
|
|
285
1136
|
label: `Subagent "${subagentType}" errored: ${errorMessage}`,
|
|
286
1137
|
data: { message: errorMessage }
|
|
287
1138
|
});
|
|
288
1139
|
}
|
|
289
|
-
|
|
1140
|
+
this.clearChildGraph(childGraph);
|
|
1141
|
+
execution.markFailed();
|
|
290
1142
|
/**
|
|
291
1143
|
* A tripped stream circuit breaker is a safety abort, not a recoverable
|
|
292
1144
|
* subagent failure: converting it into a tool result would let the
|
|
@@ -300,8 +1152,9 @@ var SubagentExecutor = class {
|
|
|
300
1152
|
messages: []
|
|
301
1153
|
};
|
|
302
1154
|
}
|
|
303
|
-
|
|
304
|
-
|
|
1155
|
+
if (result == null) throw new Error("Subagent completed without producing graph state.");
|
|
1156
|
+
const filteredContent = childPlan.kind === "graph" ? filterGraphSubagentResult(result, childPlan.resultAgentId) : filterSubagentResult(result.messages);
|
|
1157
|
+
if (!childAlreadyCompleted && this.hookRegistry?.hasHookFor("SubagentStop", currentHookSessionId) === true)
|
|
305
1158
|
/**
|
|
306
1159
|
* Awaited (not fire-and-forget) for deterministic test synchronization
|
|
307
1160
|
* and consistency with PostCompact. The parent is already waiting on the
|
|
@@ -312,31 +1165,39 @@ var SubagentExecutor = class {
|
|
|
312
1165
|
registry: this.hookRegistry,
|
|
313
1166
|
input: {
|
|
314
1167
|
hook_event_name: "SubagentStop",
|
|
315
|
-
runId:
|
|
1168
|
+
runId: currentHookSessionId,
|
|
316
1169
|
threadId,
|
|
317
1170
|
agentId: childAgentId,
|
|
318
1171
|
agentType: subagentType,
|
|
319
1172
|
messages: result.messages
|
|
320
1173
|
},
|
|
321
|
-
sessionId:
|
|
1174
|
+
sessionId: currentHookSessionId,
|
|
322
1175
|
matchQuery: subagentType
|
|
323
1176
|
}).catch(() => {});
|
|
324
|
-
if (forwarding) {
|
|
1177
|
+
if (forwarding && !childAlreadyCompleted) {
|
|
325
1178
|
await forwarding.drain();
|
|
326
1179
|
await this.emitSubagentUpdate(parentRegistry, {
|
|
327
1180
|
childRunId,
|
|
328
1181
|
subagentType,
|
|
1182
|
+
subagentKind: childPlan.kind,
|
|
329
1183
|
subagentAgentId: childAgentId,
|
|
330
1184
|
parentToolCallId,
|
|
1185
|
+
executionContext: childExecutionContext,
|
|
331
1186
|
phase: "stop",
|
|
332
1187
|
label: `Subagent "${subagentType}" finished`
|
|
333
1188
|
});
|
|
334
1189
|
}
|
|
335
|
-
|
|
336
|
-
|
|
1190
|
+
if (!childAlreadyCompleted) {
|
|
1191
|
+
const activeChildRun = execution.activeRun;
|
|
1192
|
+
if (activeChildRun != null && parentToolCallId != null) await this.persistChildCheckpointMarker(activeChildRun, parentToolCallId);
|
|
1193
|
+
}
|
|
1194
|
+
this.clearChildGraph(childGraph);
|
|
1195
|
+
const completedResult = {
|
|
337
1196
|
content: filteredContent,
|
|
338
1197
|
messages: result.messages
|
|
339
1198
|
};
|
|
1199
|
+
execution.markCompleted(completedResult);
|
|
1200
|
+
return completedResult;
|
|
340
1201
|
}
|
|
341
1202
|
/**
|
|
342
1203
|
* Emits a single {@link GraphEvents.ON_SUBAGENT_UPDATE} envelope through the
|
|
@@ -346,21 +1207,23 @@ var SubagentExecutor = class {
|
|
|
346
1207
|
async emitSubagentUpdate(parentRegistry, args) {
|
|
347
1208
|
const handler = parentRegistry.getHandler("on_subagent_update");
|
|
348
1209
|
if (!handler) return;
|
|
349
|
-
|
|
350
|
-
runId:
|
|
1210
|
+
await dispatchObservationalSubagentUpdate(handler, {
|
|
1211
|
+
runId: args.executionContext.rootRunId,
|
|
1212
|
+
parentRunId: this.parentRunId,
|
|
351
1213
|
subagentRunId: args.childRunId,
|
|
352
1214
|
subagentType: args.subagentType,
|
|
1215
|
+
subagentKind: args.subagentKind,
|
|
353
1216
|
subagentAgentId: args.subagentAgentId,
|
|
1217
|
+
memberAgentId: args.memberAgentId,
|
|
354
1218
|
parentAgentId: this.parentAgentId,
|
|
355
1219
|
parentToolCallId: args.parentToolCallId,
|
|
1220
|
+
depth: args.executionContext.depth,
|
|
1221
|
+
ancestry: args.executionContext.ancestry,
|
|
356
1222
|
phase: args.phase,
|
|
357
1223
|
data: args.data,
|
|
358
1224
|
label: args.label,
|
|
359
1225
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
360
|
-
};
|
|
361
|
-
try {
|
|
362
|
-
await handler.handle("on_subagent_update", event);
|
|
363
|
-
} catch {}
|
|
1226
|
+
});
|
|
364
1227
|
}
|
|
365
1228
|
/**
|
|
366
1229
|
* Builds a BaseCallbackHandler that intercepts the child graph's custom
|
|
@@ -375,35 +1238,40 @@ var SubagentExecutor = class {
|
|
|
375
1238
|
* cares about; host apps can extend by registering more phases).
|
|
376
1239
|
*/
|
|
377
1240
|
createForwarderCallback(args) {
|
|
378
|
-
const { parentRegistry, subagentType, subagentAgentId, childRunId, parentToolCallId } = args;
|
|
379
|
-
const
|
|
1241
|
+
const { parentRegistry, subagentType, subagentKind, subagentAgentId, childRunId, parentToolCallId, executionContext } = args;
|
|
1242
|
+
const immediateParentRunId = this.parentRunId;
|
|
380
1243
|
const parentAgentId = this.parentAgentId;
|
|
381
|
-
const wrap = async (eventName, phase, data) => {
|
|
1244
|
+
const wrap = async (eventName, phase, data, memberAgentId) => {
|
|
382
1245
|
const handler = parentRegistry.getHandler("on_subagent_update");
|
|
383
|
-
if (!handler) return;
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
1246
|
+
if (!handler) return true;
|
|
1247
|
+
return dispatchObservationalSubagentUpdate(handler, {
|
|
1248
|
+
runId: executionContext.rootRunId,
|
|
1249
|
+
parentRunId: immediateParentRunId,
|
|
1250
|
+
subagentRunId: childRunId,
|
|
1251
|
+
subagentType,
|
|
1252
|
+
subagentKind,
|
|
1253
|
+
subagentAgentId,
|
|
1254
|
+
memberAgentId,
|
|
1255
|
+
parentAgentId,
|
|
1256
|
+
parentToolCallId,
|
|
1257
|
+
depth: executionContext.depth,
|
|
1258
|
+
ancestry: executionContext.ancestry,
|
|
1259
|
+
phase,
|
|
1260
|
+
data: sanitizeForwardedSubagentUpdateData(eventName, data),
|
|
1261
|
+
label: summarizeEvent(eventName, data),
|
|
1262
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1263
|
+
});
|
|
399
1264
|
};
|
|
400
1265
|
const queuedUpdates = [];
|
|
401
1266
|
let drainPromise;
|
|
1267
|
+
let queueOpen = true;
|
|
402
1268
|
const enqueue = (update) => {
|
|
403
|
-
if (
|
|
404
|
-
|
|
1269
|
+
if (!queueOpen) return;
|
|
1270
|
+
if (queuedUpdates.length >= MAX_QUEUED_SUBAGENT_UPDATES) {
|
|
1271
|
+
const dropIndex = queuedUpdates.findIndex((queued) => isLowPrioritySubagentUpdatePhase(queued.phase));
|
|
405
1272
|
if (dropIndex >= 0) queuedUpdates.splice(dropIndex, 1);
|
|
406
|
-
else if (
|
|
1273
|
+
else if (isLowPrioritySubagentUpdatePhase(update.phase)) return;
|
|
1274
|
+
else queuedUpdates.shift();
|
|
407
1275
|
}
|
|
408
1276
|
queuedUpdates.push(update);
|
|
409
1277
|
};
|
|
@@ -416,7 +1284,10 @@ var SubagentExecutor = class {
|
|
|
416
1284
|
while (queuedUpdates.length > 0) {
|
|
417
1285
|
const update = queuedUpdates.shift();
|
|
418
1286
|
if (update == null) continue;
|
|
419
|
-
await wrap(update.eventName, update.phase, update.data)
|
|
1287
|
+
if (!await wrap(update.eventName, update.phase, update.data, update.memberAgentId)) {
|
|
1288
|
+
queueOpen = false;
|
|
1289
|
+
queuedUpdates.length = 0;
|
|
1290
|
+
}
|
|
420
1291
|
}
|
|
421
1292
|
})();
|
|
422
1293
|
try {
|
|
@@ -426,15 +1297,17 @@ var SubagentExecutor = class {
|
|
|
426
1297
|
if (queuedUpdates.length > 0) await drain();
|
|
427
1298
|
}
|
|
428
1299
|
};
|
|
429
|
-
const scheduleWrap = (eventName, phase, data) => {
|
|
1300
|
+
const scheduleWrap = (eventName, phase, data, memberAgentId) => {
|
|
430
1301
|
enqueue({
|
|
431
1302
|
eventName,
|
|
432
1303
|
phase,
|
|
433
|
-
data
|
|
1304
|
+
data,
|
|
1305
|
+
memberAgentId
|
|
434
1306
|
});
|
|
435
1307
|
drain();
|
|
436
1308
|
};
|
|
437
|
-
const handler = _langchain_core_callbacks_base.BaseCallbackHandler.fromMethods({ ["handleCustomEvent"]: async (eventName, data) => {
|
|
1309
|
+
const handler = _langchain_core_callbacks_base.BaseCallbackHandler.fromMethods({ ["handleCustomEvent"]: async (eventName, data, _runId, _tags, metadata) => {
|
|
1310
|
+
const memberAgentId = asNonEmptyString(metadata?.agentId) ?? getEventAgentId(data);
|
|
438
1311
|
if (eventName === "on_tool_execute") {
|
|
439
1312
|
const toolHandler = parentRegistry.getHandler("on_tool_execute");
|
|
440
1313
|
if (toolHandler) await toolHandler.handle("on_tool_execute", data);
|
|
@@ -442,27 +1315,27 @@ var SubagentExecutor = class {
|
|
|
442
1315
|
* We also surface a short notice in the subagent-update stream so
|
|
443
1316
|
* the UI can show "calling <tool>" for each tool the child spawns.
|
|
444
1317
|
*/
|
|
445
|
-
scheduleWrap(eventName, "run_step", data);
|
|
1318
|
+
scheduleWrap(eventName, "run_step", data, memberAgentId);
|
|
446
1319
|
return;
|
|
447
1320
|
}
|
|
448
1321
|
if (eventName === "on_run_step") {
|
|
449
|
-
scheduleWrap(eventName, "run_step", data);
|
|
1322
|
+
scheduleWrap(eventName, "run_step", data, memberAgentId);
|
|
450
1323
|
return;
|
|
451
1324
|
}
|
|
452
1325
|
if (eventName === "on_run_step_delta") {
|
|
453
|
-
scheduleWrap(eventName, "run_step_delta", data);
|
|
1326
|
+
scheduleWrap(eventName, "run_step_delta", data, memberAgentId);
|
|
454
1327
|
return;
|
|
455
1328
|
}
|
|
456
1329
|
if (eventName === "on_run_step_completed") {
|
|
457
|
-
scheduleWrap(eventName, "run_step_completed", data);
|
|
1330
|
+
scheduleWrap(eventName, "run_step_completed", data, memberAgentId);
|
|
458
1331
|
return;
|
|
459
1332
|
}
|
|
460
1333
|
if (eventName === "on_message_delta") {
|
|
461
|
-
scheduleWrap(eventName, "message_delta", data);
|
|
1334
|
+
scheduleWrap(eventName, "message_delta", data, memberAgentId);
|
|
462
1335
|
return;
|
|
463
1336
|
}
|
|
464
1337
|
if (eventName === "on_reasoning_delta") {
|
|
465
|
-
scheduleWrap(eventName, "reasoning_delta", data);
|
|
1338
|
+
scheduleWrap(eventName, "reasoning_delta", data, memberAgentId);
|
|
466
1339
|
return;
|
|
467
1340
|
}
|
|
468
1341
|
} });
|
|
@@ -496,7 +1369,8 @@ var SubagentExecutor = class {
|
|
|
496
1369
|
* `subagentUsageSink` on the child graph's input.
|
|
497
1370
|
*/
|
|
498
1371
|
function createUsageCaptureHandler(args) {
|
|
499
|
-
const { sink, subagentType, subagentRunId, subagentAgentId, parentRunId,
|
|
1372
|
+
const { sink, subagentType, subagentKind, subagentRunId, subagentAgentId, parentRunId, executionContext, memberInputs } = args;
|
|
1373
|
+
const defaultMember = memberInputs.size === 1 ? memberInputs.entries().next().value : void 0;
|
|
500
1374
|
/**
|
|
501
1375
|
* Per-call attribution keyed by LangChain callback runId. `model` joins
|
|
502
1376
|
* `ls_model_name` (provider-reported) with `INVOKED_MODEL` (stamped by
|
|
@@ -511,16 +1385,21 @@ function createUsageCaptureHandler(args) {
|
|
|
511
1385
|
handleChatModelStart: (_llm, _messages, runId, _parentRunId, _extraParams, _tags, metadata) => {
|
|
512
1386
|
const callModel = asNonEmptyString(metadata?.ls_model_name) ?? asNonEmptyString(metadata?.["__invoked_model"]);
|
|
513
1387
|
const callProvider = asNonEmptyString(metadata?.["__invoked_provider"]);
|
|
514
|
-
|
|
1388
|
+
const memberAgentId = asNonEmptyString(metadata?.agentId);
|
|
1389
|
+
if (callModel != null || callProvider != null || memberAgentId != null) callInfoByCallId.set(runId, {
|
|
515
1390
|
model: callModel,
|
|
516
|
-
provider: callProvider
|
|
1391
|
+
provider: callProvider,
|
|
1392
|
+
memberAgentId
|
|
517
1393
|
});
|
|
518
1394
|
},
|
|
519
1395
|
handleLLMEnd: async (output, runId) => {
|
|
520
1396
|
const callInfo = callInfoByCallId.get(runId);
|
|
521
1397
|
callInfoByCallId.delete(runId);
|
|
522
|
-
const
|
|
523
|
-
const
|
|
1398
|
+
const observedMemberAgentId = callInfo?.memberAgentId;
|
|
1399
|
+
const memberAgentId = observedMemberAgentId != null && memberInputs.has(observedMemberAgentId) ? observedMemberAgentId : defaultMember?.[0];
|
|
1400
|
+
const memberInput = (memberAgentId == null ? void 0 : memberInputs.get(memberAgentId)) ?? defaultMember?.[1];
|
|
1401
|
+
const model = callInfo?.model ?? (memberInput == null ? void 0 : extractConfiguredModel(memberInput));
|
|
1402
|
+
const callProvider = callInfo?.provider ?? memberInput?.provider;
|
|
524
1403
|
for (const generationGroup of output.generations)
|
|
525
1404
|
/**
|
|
526
1405
|
* At most ONE event per generation group: each group is one
|
|
@@ -545,9 +1424,14 @@ function createUsageCaptureHandler(args) {
|
|
|
545
1424
|
model,
|
|
546
1425
|
provider: callProvider,
|
|
547
1426
|
subagentType,
|
|
1427
|
+
subagentKind,
|
|
548
1428
|
subagentRunId,
|
|
549
1429
|
subagentAgentId,
|
|
550
|
-
|
|
1430
|
+
memberAgentId,
|
|
1431
|
+
parentRunId,
|
|
1432
|
+
depth: executionContext.depth,
|
|
1433
|
+
ancestry: executionContext.ancestry,
|
|
1434
|
+
runId: executionContext.rootRunId
|
|
551
1435
|
});
|
|
552
1436
|
} catch {}
|
|
553
1437
|
break;
|
|
@@ -568,6 +1452,11 @@ function createUsageCaptureHandler(args) {
|
|
|
568
1452
|
function asNonEmptyString(value) {
|
|
569
1453
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
570
1454
|
}
|
|
1455
|
+
function getEventAgentId(data) {
|
|
1456
|
+
if (data == null || typeof data !== "object") return;
|
|
1457
|
+
const event = data;
|
|
1458
|
+
return asNonEmptyString(event.agentId) ?? asNonEmptyString(event.result?.agentId);
|
|
1459
|
+
}
|
|
571
1460
|
/**
|
|
572
1461
|
* Best-effort read of the configured model from a subagent's client
|
|
573
1462
|
* options. Providers disagree on the key (`model` vs `modelName`), and the
|
|
@@ -578,12 +1467,50 @@ function extractConfiguredModel(agentInputs) {
|
|
|
578
1467
|
if (typeof clientOptions?.model === "string" && clientOptions.model !== "") return clientOptions.model;
|
|
579
1468
|
if (typeof clientOptions?.modelName === "string" && clientOptions.modelName !== "") return clientOptions.modelName;
|
|
580
1469
|
}
|
|
1470
|
+
function sanitizeResolverConfigurable(parentConfigurable) {
|
|
1471
|
+
const configurable = sanitizeChildConfigurable(parentConfigurable);
|
|
1472
|
+
const requestBody = createResolveRequestContext(configurable.requestBody);
|
|
1473
|
+
const user = createResolveUserContext(configurable.user);
|
|
1474
|
+
const userId = asNonEmptyString(configurable.user_id) ?? user?.id;
|
|
1475
|
+
if (requestBody == null && user == null && userId == null) return;
|
|
1476
|
+
return {
|
|
1477
|
+
...requestBody == null ? {} : { requestBody },
|
|
1478
|
+
...user == null ? {} : { user },
|
|
1479
|
+
...userId == null ? {} : { user_id: userId }
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
function createResolveRequestContext(value) {
|
|
1483
|
+
if (!isObjectLike(value)) return;
|
|
1484
|
+
const source = value;
|
|
1485
|
+
const conversationId = asNonEmptyString(source.conversationId);
|
|
1486
|
+
const messageId = asNonEmptyString(source.messageId);
|
|
1487
|
+
const parentMessageId = asNonEmptyString(source.parentMessageId);
|
|
1488
|
+
if (conversationId == null && messageId == null && parentMessageId == null) return;
|
|
1489
|
+
return {
|
|
1490
|
+
...conversationId == null ? {} : { conversationId },
|
|
1491
|
+
...messageId == null ? {} : { messageId },
|
|
1492
|
+
...parentMessageId == null ? {} : { parentMessageId }
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
function createResolveUserContext(value) {
|
|
1496
|
+
if (!isObjectLike(value)) return;
|
|
1497
|
+
const source = value;
|
|
1498
|
+
const id = asNonEmptyString(source.id);
|
|
1499
|
+
const role = asNonEmptyString(source.role);
|
|
1500
|
+
const tenantId = asNonEmptyString(source.tenantId);
|
|
1501
|
+
if (id == null && role == null && tenantId == null) return;
|
|
1502
|
+
return {
|
|
1503
|
+
...id == null ? {} : { id },
|
|
1504
|
+
...role == null ? {} : { role },
|
|
1505
|
+
...tenantId == null ? {} : { tenantId }
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
581
1508
|
function sanitizeChildConfigurable(parentConfigurable) {
|
|
582
1509
|
if (parentConfigurable == null) return {};
|
|
583
1510
|
return Object.fromEntries(Object.entries(parentConfigurable).filter(([key]) => !isLangGraphRuntimeConfigKey(key)));
|
|
584
1511
|
}
|
|
585
1512
|
function isLangGraphRuntimeConfigKey(key) {
|
|
586
|
-
return key.startsWith(LANGGRAPH_RUNTIME_CONFIG_PREFIX) || LANGGRAPH_CHECKPOINT_CONFIG_KEYS.has(key) || key === "lc_run_breaker_scope";
|
|
1513
|
+
return key.startsWith(LANGGRAPH_RUNTIME_CONFIG_PREFIX) || LANGGRAPH_CHECKPOINT_CONFIG_KEYS.has(key) || key === "__librechat_subagent_resume_attempt" || key === "__librechat_subagent_resume_manifest" || key === "__librechat_subagent_parent_batch" || key === "__librechat_tool_approval_execution_scope" || key === "lc_run_breaker_scope";
|
|
587
1514
|
}
|
|
588
1515
|
function sanitizeForwardedSubagentUpdateData(eventName, data) {
|
|
589
1516
|
if (eventName === "on_tool_execute") return sanitizeToolExecuteUpdateData(data);
|
|
@@ -593,7 +1520,7 @@ function sanitizeForwardedSubagentUpdateData(eventName, data) {
|
|
|
593
1520
|
if (eventName === "on_message_delta") return sanitizeMessageDeltaUpdateData(data);
|
|
594
1521
|
if (eventName === "on_reasoning_delta") return sanitizeReasoningDeltaUpdateData(data);
|
|
595
1522
|
}
|
|
596
|
-
function
|
|
1523
|
+
function isLowPrioritySubagentUpdatePhase(phase) {
|
|
597
1524
|
return phase === "message_delta" || phase === "reasoning_delta" || phase === "run_step_delta";
|
|
598
1525
|
}
|
|
599
1526
|
function sanitizeToolExecuteUpdateData(data) {
|
|
@@ -762,6 +1689,16 @@ function summarizeEvent(eventName, data) {
|
|
|
762
1689
|
return eventName;
|
|
763
1690
|
}
|
|
764
1691
|
/**
|
|
1692
|
+
* Reads only the result member captured by MultiAgentGraph. Worker output is
|
|
1693
|
+
* never used as a fallback when the designated result member is textless.
|
|
1694
|
+
*/
|
|
1695
|
+
function filterGraphSubagentResult(state, resultAgentId) {
|
|
1696
|
+
const result = state.subagentResult;
|
|
1697
|
+
if (result == null || result.agentId !== resultAgentId) return `Error: Graph subagent result agent "${resultAgentId}" did not produce a result.`;
|
|
1698
|
+
if (result.message == null) return "Task completed";
|
|
1699
|
+
return filterSubagentResult([result.message]);
|
|
1700
|
+
}
|
|
1701
|
+
/**
|
|
765
1702
|
* Walk messages from last to first, returning the text content of the most
|
|
766
1703
|
* recent AIMessage that has any. Non-text blocks (tool_use, thinking,
|
|
767
1704
|
* redacted_thinking, tool_result) are stripped. If the last AIMessage is
|
|
@@ -818,93 +1755,37 @@ function filterSubagentResult(messages) {
|
|
|
818
1755
|
}
|
|
819
1756
|
return "Task completed";
|
|
820
1757
|
}
|
|
821
|
-
/**
|
|
822
|
-
* Resolve self-spawn configs by filling in agentInputs from the parent context.
|
|
823
|
-
* Returns configs with agentInputs guaranteed present. Throws on duplicate
|
|
824
|
-
* `type` values to prevent silent config shadowing.
|
|
825
|
-
*/
|
|
826
|
-
function resolveSubagentConfigs(configs, parentContext) {
|
|
827
|
-
const resolved = configs.map((config) => {
|
|
828
|
-
if (config.agentInputs != null) return config;
|
|
829
|
-
if (config.self !== true || parentContext._sourceInputs == null) return null;
|
|
830
|
-
return {
|
|
831
|
-
...config,
|
|
832
|
-
agentInputs: { ...parentContext._sourceInputs }
|
|
833
|
-
};
|
|
834
|
-
}).filter((c) => c != null);
|
|
835
|
-
const seenTypes = /* @__PURE__ */ new Set();
|
|
836
|
-
for (const config of resolved) {
|
|
837
|
-
if (seenTypes.has(config.type)) throw new Error(`Duplicate subagent type "${config.type}". Each SubagentConfig must have a unique "type" field.`);
|
|
838
|
-
seenTypes.add(config.type);
|
|
839
|
-
}
|
|
840
|
-
return resolved;
|
|
841
|
-
}
|
|
842
|
-
/**
|
|
843
|
-
* Build child AgentInputs from a resolved config, stripping nesting and
|
|
844
|
-
* (optionally) event-driven fields. When `allowNested: true`, the child's
|
|
845
|
-
* `maxSubagentDepth` is decremented so that depth is consumed as the call
|
|
846
|
-
* chain deepens across graph boundaries — the parent's executor-level check
|
|
847
|
-
* alone cannot see into the child graph's separate executor.
|
|
848
|
-
*
|
|
849
|
-
* When `keepToolDefinitions` is `true`, the child retains the parent's
|
|
850
|
-
* `toolDefinitions` so event-driven tools remain usable. This is only safe
|
|
851
|
-
* when the caller has wired a forwarder for `ON_TOOL_EXECUTE` to a
|
|
852
|
-
* registered handler — otherwise the child will hang on tool dispatch.
|
|
853
|
-
*
|
|
854
|
-
* @remarks Advanced utility: exported primarily for testing and by
|
|
855
|
-
* {@link SubagentExecutor}. Host applications configuring subagents should
|
|
856
|
-
* not need to call this directly — it is invoked internally when a subagent
|
|
857
|
-
* tool is dispatched. The depth-countdown contract (parent's `maxDepth` in,
|
|
858
|
-
* child's decremented `maxSubagentDepth` on the returned inputs) is the
|
|
859
|
-
* mechanism that bounds nesting across graph boundaries; callers must
|
|
860
|
-
* respect it.
|
|
861
|
-
*/
|
|
862
|
-
function buildChildInputs(config, childAgentId, parentMaxDepth, keepToolDefinitions = false) {
|
|
863
|
-
const { agentInputs } = config;
|
|
864
|
-
const childInputs = {
|
|
865
|
-
...agentInputs,
|
|
866
|
-
agentId: childAgentId,
|
|
867
|
-
toolDefinitions: keepToolDefinitions ? agentInputs.toolDefinitions : void 0,
|
|
868
|
-
/**
|
|
869
|
-
* Subagents run in an isolated context by contract. Parent-run-scoped
|
|
870
|
-
* fields that would otherwise survive the shallow-spread clone — the
|
|
871
|
-
* cross-run conversation summary and the prior-turn tool-discovery
|
|
872
|
-
* set — are cleared here so the child starts fresh. Host applications
|
|
873
|
-
* that want a subagent to see parent context must thread it in
|
|
874
|
-
* explicitly (e.g. via the `description` argument to the subagent
|
|
875
|
-
* tool), not via inherited state.
|
|
876
|
-
*/
|
|
877
|
-
initialSummary: void 0,
|
|
878
|
-
discoveredTools: void 0,
|
|
879
|
-
/**
|
|
880
|
-
* Host-supplied direct tools are scrubbed from INHERITED configs only.
|
|
881
|
-
* A self-spawn config's `agentInputs` is a shallow spread of the parent's
|
|
882
|
-
* `_sourceInputs`, so without this a parent-scoped graph tool (e.g. an
|
|
883
|
-
* interrupt-raising ask_user_question, which needs the parent's
|
|
884
|
-
* checkpointer — child graphs compile without one) would silently leak
|
|
885
|
-
* into the child and deterministically throw `No checkpointer set`. An
|
|
886
|
-
* EXPLICIT child config that lists its own `graphTools` is a deliberate
|
|
887
|
-
* host choice and keeps them (Codex #289 P2).
|
|
888
|
-
*/
|
|
889
|
-
graphTools: config.self === true ? void 0 : agentInputs.graphTools
|
|
890
|
-
};
|
|
891
|
-
if (config.allowNested === true) childInputs.maxSubagentDepth = Math.max(0, parentMaxDepth - 1);
|
|
892
|
-
else {
|
|
893
|
-
childInputs.subagentConfigs = void 0;
|
|
894
|
-
childInputs.maxSubagentDepth = void 0;
|
|
895
|
-
}
|
|
896
|
-
return childInputs;
|
|
897
|
-
}
|
|
898
1758
|
function truncateErrorMessage(error) {
|
|
899
1759
|
const message = error instanceof Error ? error.message : String(error);
|
|
900
1760
|
if (message.length <= ERROR_MESSAGE_MAX_CHARS) return message;
|
|
901
1761
|
return `${message.slice(0, ERROR_MESSAGE_MAX_CHARS)}...`;
|
|
902
1762
|
}
|
|
1763
|
+
function resolveAgentInputsWithSignal(resolve, signal) {
|
|
1764
|
+
if (signal.aborted) return Promise.reject(getAbortReason(signal));
|
|
1765
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
1766
|
+
const cleanup = () => signal.removeEventListener("abort", onAbort);
|
|
1767
|
+
const onAbort = () => {
|
|
1768
|
+
cleanup();
|
|
1769
|
+
rejectPromise(getAbortReason(signal));
|
|
1770
|
+
};
|
|
1771
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1772
|
+
Promise.resolve().then(resolve).then((agentInputs) => {
|
|
1773
|
+
cleanup();
|
|
1774
|
+
resolvePromise(agentInputs);
|
|
1775
|
+
}, (error) => {
|
|
1776
|
+
cleanup();
|
|
1777
|
+
rejectPromise(error);
|
|
1778
|
+
});
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1781
|
+
function getAbortReason(signal) {
|
|
1782
|
+
return signal.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("Subagent resolution aborted.");
|
|
1783
|
+
}
|
|
903
1784
|
//#endregion
|
|
1785
|
+
exports.DEFAULT_SUBAGENT_DESCRIPTION = DEFAULT_SUBAGENT_DESCRIPTION;
|
|
904
1786
|
exports.SubagentExecutor = SubagentExecutor;
|
|
905
|
-
exports.
|
|
1787
|
+
exports.filterGraphSubagentResult = filterGraphSubagentResult;
|
|
906
1788
|
exports.filterSubagentResult = filterSubagentResult;
|
|
907
|
-
exports.resolveSubagentConfigs = resolveSubagentConfigs;
|
|
908
1789
|
exports.summarizeEvent = summarizeEvent;
|
|
909
1790
|
|
|
910
1791
|
//# sourceMappingURL=SubagentExecutor.cjs.map
|