@librechat/agents 3.4.1 → 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 +57 -33
- 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/createWorkspacePolicyHook.cjs +1 -1
- package/dist/cjs/main.cjs +20 -11
- package/dist/cjs/run.cjs +48 -38
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/JsonlSessionStore.cjs +2 -2
- package/dist/cjs/tools/SubagentTool.cjs +4 -4
- package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +13 -9
- package/dist/cjs/tools/ToolNode.cjs.map +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 +672 -343
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentReplay.cjs +3 -1
- package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -1
- 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/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +57 -33
- 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/createWorkspacePolicyHook.mjs +1 -1
- package/dist/esm/main.mjs +12 -10
- package/dist/esm/run.mjs +48 -38
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/JsonlSessionStore.mjs +2 -2
- package/dist/esm/tools/SubagentTool.mjs +4 -4
- package/dist/esm/tools/SubagentTool.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +13 -9
- package/dist/esm/tools/ToolNode.mjs.map +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 +672 -343
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentReplay.mjs +3 -1
- package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -1
- 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/types/agents/AgentContext.d.ts +1 -1
- package/dist/types/graphs/Graph.d.ts +7 -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/run.d.ts +2 -2
- package/dist/types/tools/SubagentTool.d.ts +1 -1
- 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 +32 -37
- package/dist/types/tools/subagent/SubagentReplay.d.ts +5 -1
- 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/types/graph.d.ts +184 -19
- package/package.json +3 -1
- package/src/agents/AgentContext.ts +1 -1
- package/src/graphs/Graph.ts +111 -60
- package/src/graphs/MultiAgentGraph.ts +149 -47
- 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/run.ts +50 -38
- 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/tools/SubagentTool.ts +4 -4
- package/src/tools/ToolNode.ts +22 -10
- package/src/tools/__tests__/SubagentExecutor.test.ts +198 -9
- package/src/tools/__tests__/SubagentReplay.test.ts +29 -0
- package/src/tools/__tests__/directToolHITLResumeScope.test.ts +28 -2
- package/src/tools/__tests__/subagentHooks.test.ts +90 -3
- 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 +1245 -563
- package/src/tools/subagent/SubagentReplay.ts +10 -1
- 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/types/graph.ts +217 -19
|
@@ -1,26 +1,63 @@
|
|
|
1
1
|
const require_SubagentReplay = require("./SubagentReplay.cjs");
|
|
2
|
+
const require_misc = require("../../utils/misc.cjs");
|
|
2
3
|
require("../../common/enum.cjs");
|
|
3
4
|
require("../../common/index.cjs");
|
|
5
|
+
const require_eagerEventExecution = require("../eagerEventExecution.cjs");
|
|
4
6
|
const require_streamLimits = require("../../llm/streamLimits.cjs");
|
|
7
|
+
const require_SubagentExecutionRegistry = require("./SubagentExecutionRegistry.cjs");
|
|
8
|
+
require("./runtimeLimits.cjs");
|
|
5
9
|
const require_executeHooks = require("../../hooks/executeHooks.cjs");
|
|
6
10
|
const require_types = require("../../hooks/types.cjs");
|
|
7
11
|
require("../../hooks/index.cjs");
|
|
12
|
+
const require_childGraphConfig = require("./childGraphConfig.cjs");
|
|
8
13
|
let nanoid = require("nanoid");
|
|
9
14
|
let _langchain_core_callbacks_base = require("@langchain/core/callbacks/base");
|
|
10
15
|
let _langchain_core_messages = require("@langchain/core/messages");
|
|
11
16
|
let _langchain_langgraph = require("@langchain/langgraph");
|
|
12
17
|
let _langchain_core_singletons = require("@langchain/core/singletons");
|
|
18
|
+
let crypto = require("crypto");
|
|
13
19
|
//#region src/tools/subagent/SubagentExecutor.ts
|
|
14
|
-
const DEFAULT_MAX_TURNS = 25;
|
|
15
|
-
const RECURSION_MULTIPLIER = 3;
|
|
16
20
|
const ERROR_MESSAGE_MAX_CHARS = 200;
|
|
17
|
-
const
|
|
21
|
+
const MAX_QUEUED_SUBAGENT_UPDATES = 64;
|
|
22
|
+
const SUBAGENT_UPDATE_HANDLER_TIMEOUT_MS = 5e3;
|
|
18
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
|
+
}
|
|
19
37
|
const HOOK_FALLBACK = Object.freeze({
|
|
20
38
|
additionalContexts: [],
|
|
21
39
|
injectedMessages: [],
|
|
22
40
|
errors: []
|
|
23
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
|
+
}
|
|
24
61
|
const LANGGRAPH_RUNTIME_CONFIG_PREFIX = "__pregel_";
|
|
25
62
|
const LANGGRAPH_RESUME_MAP_CONFIG_KEY = "__pregel_resume_map";
|
|
26
63
|
const LANGGRAPH_CHECKPOINT_CONFIG_KEYS = new Set([
|
|
@@ -32,6 +69,7 @@ const SUBAGENT_CHECKPOINT_MARKER_KEY = "__librechat_subagent_checkpoint";
|
|
|
32
69
|
const SUBAGENT_HOOK_SESSION_KEY = "__librechat_subagent_hook_session";
|
|
33
70
|
const SUBAGENT_RUN_ID_KEY = "__librechat_subagent_run_id";
|
|
34
71
|
const SUBAGENT_REPLAY_NODE = "subagent-replay";
|
|
72
|
+
const DEFAULT_SUBAGENT_DESCRIPTION = "No task description provided";
|
|
35
73
|
function isCheckpointSaver(value) {
|
|
36
74
|
if (value == null || typeof value !== "object") return false;
|
|
37
75
|
const candidate = value;
|
|
@@ -118,6 +156,9 @@ function serializeToolOutput(settled) {
|
|
|
118
156
|
...settled.referenceContent == null ? {} : { referenceContent: settled.referenceContent }
|
|
119
157
|
};
|
|
120
158
|
}
|
|
159
|
+
function getSettlementFingerprint(output) {
|
|
160
|
+
return (0, crypto.createHash)("sha256").update(require_eagerEventExecution.stableStringify(output)).digest("hex");
|
|
161
|
+
}
|
|
121
162
|
function deserializeToolOutput(output) {
|
|
122
163
|
return {
|
|
123
164
|
output: new _langchain_core_messages.ToolMessage({
|
|
@@ -135,40 +176,16 @@ function deserializeToolOutput(output) {
|
|
|
135
176
|
...output.referenceContent == null ? {} : { referenceContent: output.referenceContent }
|
|
136
177
|
};
|
|
137
178
|
}
|
|
138
|
-
function getParentCheckpointFork(configurable) {
|
|
139
|
-
const checkpointId = configurable?.checkpoint_id;
|
|
140
|
-
return typeof checkpointId === "string" && checkpointId.length > 0 ? checkpointId : "root";
|
|
141
|
-
}
|
|
142
|
-
function getResumeAttemptId(configurable, fallback) {
|
|
143
|
-
const attemptId = configurable?.[require_SubagentReplay.SUBAGENT_RESUME_ATTEMPT_CONFIG_KEY];
|
|
144
|
-
return typeof attemptId === "string" && attemptId.length > 0 ? attemptId : fallback;
|
|
145
|
-
}
|
|
146
|
-
function getApprovalExecutionScope(childRunId, resumeAttemptId) {
|
|
147
|
-
return `subagent-approval:${Buffer.from(JSON.stringify([childRunId, resumeAttemptId])).toString("base64url")}`;
|
|
148
|
-
}
|
|
149
|
-
function getChildThreadId(args) {
|
|
150
|
-
const durableParentId = args.threadId ?? args.parentRunId;
|
|
151
|
-
const parentFork = getParentCheckpointFork(args.parentConfigurable);
|
|
152
|
-
const parentBatch = args.parentConfigurable?.[require_SubagentReplay.SUBAGENT_PARENT_BATCH_CONFIG_KEY];
|
|
153
|
-
const identity = [
|
|
154
|
-
durableParentId,
|
|
155
|
-
parentFork,
|
|
156
|
-
args.parentAgentId ?? "agent",
|
|
157
|
-
args.parentToolCallId,
|
|
158
|
-
typeof parentBatch === "string" && parentBatch.length > 0 ? parentBatch : "batch"
|
|
159
|
-
];
|
|
160
|
-
if (args.branchId != null) identity.push(args.branchId);
|
|
161
|
-
return `subagent:${Buffer.from(JSON.stringify(identity)).toString("base64url")}`;
|
|
162
|
-
}
|
|
163
179
|
function isToolApprovalPayload(value) {
|
|
164
180
|
return value != null && typeof value === "object" && value.type === "tool_approval";
|
|
165
181
|
}
|
|
166
182
|
function addSubagentScope(interrupts, scope, resumeManifest) {
|
|
167
183
|
return interrupts.map((childInterrupt) => {
|
|
168
|
-
|
|
184
|
+
let payload = childInterrupt.value;
|
|
185
|
+
if (isToolApprovalPayload(payload)) payload = {
|
|
169
186
|
...childInterrupt.value,
|
|
170
187
|
subagent: childInterrupt.value.subagent ?? scope
|
|
171
|
-
}
|
|
188
|
+
};
|
|
172
189
|
return {
|
|
173
190
|
...childInterrupt,
|
|
174
191
|
value: resumeManifest == null ? payload : require_SubagentReplay.attachSubagentResumeManifest(payload, resumeManifest)
|
|
@@ -215,6 +232,7 @@ var SubagentExecutor = class {
|
|
|
215
232
|
hookRegistry;
|
|
216
233
|
parentRunId;
|
|
217
234
|
parentAgentId;
|
|
235
|
+
executionContext;
|
|
218
236
|
langfuse;
|
|
219
237
|
tokenCounter;
|
|
220
238
|
streamLimits;
|
|
@@ -222,13 +240,9 @@ var SubagentExecutor = class {
|
|
|
222
240
|
checkpointer;
|
|
223
241
|
maxDepth;
|
|
224
242
|
createChildGraph;
|
|
243
|
+
createChildGraphByKind;
|
|
225
244
|
usageSink;
|
|
226
|
-
|
|
227
|
-
startedChildRuns = /* @__PURE__ */ new Set();
|
|
228
|
-
completedChildRuns = /* @__PURE__ */ new Set();
|
|
229
|
-
completedChildResults = /* @__PURE__ */ new Map();
|
|
230
|
-
childExecutionIdentities = /* @__PURE__ */ new Map();
|
|
231
|
-
activeChildRuns = /* @__PURE__ */ new Map();
|
|
245
|
+
executions;
|
|
232
246
|
replayCheckpointWorkflow;
|
|
233
247
|
resolveParentHandlerRegistry;
|
|
234
248
|
constructor(options) {
|
|
@@ -238,13 +252,25 @@ var SubagentExecutor = class {
|
|
|
238
252
|
this.hookRegistry = options.hookRegistry;
|
|
239
253
|
this.parentRunId = options.parentRunId;
|
|
240
254
|
this.parentAgentId = options.parentAgentId;
|
|
255
|
+
this.executionContext = options.executionContext ?? {
|
|
256
|
+
rootRunId: options.parentRunId,
|
|
257
|
+
hookSessionId: options.parentRunId,
|
|
258
|
+
depth: 0,
|
|
259
|
+
ancestry: []
|
|
260
|
+
};
|
|
241
261
|
this.langfuse = options.langfuse;
|
|
242
262
|
this.tokenCounter = options.tokenCounter;
|
|
243
263
|
this.streamLimits = options.streamLimits;
|
|
244
264
|
this.humanInTheLoop = options.humanInTheLoop;
|
|
245
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
|
+
});
|
|
246
271
|
this.maxDepth = options.maxDepth ?? 1;
|
|
247
272
|
this.createChildGraph = options.createChildGraph;
|
|
273
|
+
this.createChildGraphByKind = options.createChildGraphByKind;
|
|
248
274
|
this.usageSink = options.usageSink;
|
|
249
275
|
const rawRegistry = options.parentHandlerRegistry;
|
|
250
276
|
if (typeof rawRegistry === "function") this.resolveParentHandlerRegistry = rawRegistry;
|
|
@@ -258,87 +284,181 @@ var SubagentExecutor = class {
|
|
|
258
284
|
/** One signal that fires on the parent's abort or the breaker abort,
|
|
259
285
|
* collapsed to a single signal when possible (mirrors
|
|
260
286
|
* `composeAbortSignals` in Graph.ts). */
|
|
261
|
-
composeChildSignal(breaker) {
|
|
262
|
-
|
|
263
|
-
if (this.parentSignal == null || this.parentSignal === child) return child;
|
|
264
|
-
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;
|
|
265
289
|
}
|
|
266
290
|
/** Snapshot of the parent's registry at the moment a subagent is dispatched. */
|
|
267
291
|
getParentHandlerRegistry() {
|
|
268
292
|
return this.resolveParentHandlerRegistry?.();
|
|
269
293
|
}
|
|
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
|
+
}
|
|
270
329
|
/**
|
|
271
330
|
* Keeps the original child thread as an immutable resume source once a
|
|
272
331
|
* different parent Run reconstructs it. Each rebuilt parent gets a private
|
|
273
332
|
* checkpoint fork, while the persisted child run ID remains stable for
|
|
274
333
|
* activity and usage correlation across the interrupt boundary.
|
|
275
334
|
*/
|
|
276
|
-
async resolveChildExecutionIdentity(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
+
};
|
|
285
355
|
});
|
|
286
|
-
|
|
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, {
|
|
287
360
|
childRunId: currentChildRunId,
|
|
288
361
|
childThreadId: baseChildThreadId,
|
|
289
362
|
approvalExecutionScope: currentChildRunId
|
|
290
|
-
};
|
|
291
|
-
const resumeExecution = require_SubagentReplay.getSubagentResumeManifest(params.parentConfigurable)?.executions.find((execution) => execution.parentToolCallId === params.parentToolCallId);
|
|
292
|
-
const branchChildThreadId = getChildThreadId({
|
|
293
|
-
parentRunId: this.parentRunId,
|
|
294
|
-
parentAgentId: this.parentAgentId,
|
|
295
|
-
threadId: params.threadId,
|
|
296
|
-
parentToolCallId: params.parentToolCallId,
|
|
297
|
-
parentConfigurable: params.parentConfigurable,
|
|
298
|
-
branchId: resumeAttemptId
|
|
299
363
|
});
|
|
364
|
+
const { resumeExecution } = execution;
|
|
300
365
|
if (resumeExecution != null) {
|
|
301
|
-
const approvalExecutionScope =
|
|
302
|
-
|
|
303
|
-
this.hookRegistry?.restorePendingToolApprovals(approvalExecutionScope, approvalExecutionScope, resumeExecution.approvalReplays);
|
|
304
|
-
this.checkpointThreadIds.add(branchChildThreadId);
|
|
305
|
-
return {
|
|
366
|
+
const approvalExecutionScope = require_SubagentExecutionRegistry.getSubagentApprovalExecutionScope(resumeExecution.childRunId, resumeAttemptId);
|
|
367
|
+
const identity = {
|
|
306
368
|
childRunId: resumeExecution.childRunId,
|
|
307
369
|
childThreadId: branchChildThreadId,
|
|
308
|
-
approvalExecutionScope
|
|
309
|
-
|
|
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
|
+
}
|
|
310
394
|
};
|
|
311
395
|
}
|
|
312
396
|
const branchTuple = await this.checkpointer.getTuple({ configurable: { thread_id: branchChildThreadId } });
|
|
313
397
|
if (branchTuple != null) {
|
|
314
398
|
const childRunId = getSubagentRunId(getTupleMessages(branchTuple)) ?? currentChildRunId;
|
|
315
|
-
this.
|
|
316
|
-
return {
|
|
399
|
+
return this.prepareIdentity(execution, {
|
|
317
400
|
childRunId,
|
|
318
401
|
childThreadId: branchChildThreadId,
|
|
319
|
-
approvalExecutionScope:
|
|
320
|
-
};
|
|
402
|
+
approvalExecutionScope: require_SubagentExecutionRegistry.getSubagentApprovalExecutionScope(childRunId, resumeAttemptId)
|
|
403
|
+
});
|
|
321
404
|
}
|
|
322
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
|
+
});
|
|
323
411
|
const persistedChildRunId = getSubagentRunId(getTupleMessages(baseTuple));
|
|
324
|
-
if (
|
|
325
|
-
|
|
326
|
-
return {
|
|
327
|
-
childRunId
|
|
412
|
+
if (!explicitResumeAttempt && (persistedChildRunId == null || persistedChildRunId === currentChildRunId)) {
|
|
413
|
+
const childRunId = persistedChildRunId ?? currentChildRunId;
|
|
414
|
+
return this.prepareIdentity(execution, {
|
|
415
|
+
childRunId,
|
|
328
416
|
childThreadId: baseChildThreadId,
|
|
329
|
-
approvalExecutionScope:
|
|
330
|
-
};
|
|
417
|
+
approvalExecutionScope: require_SubagentExecutionRegistry.getSubagentApprovalExecutionScope(childRunId, resumeAttemptId)
|
|
418
|
+
});
|
|
331
419
|
}
|
|
332
420
|
const sourceCheckpoints = await this.getLatestCheckpointSnapshot(baseChildThreadId);
|
|
333
421
|
if (sourceCheckpoints.length === 0) throw new Error(`Cannot fork subagent checkpoint thread "${baseChildThreadId}" without a checkpoint ID.`);
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
childRunId: persistedChildRunId,
|
|
422
|
+
const childRunId = persistedChildRunId ?? currentChildRunId;
|
|
423
|
+
const identity = {
|
|
424
|
+
childRunId,
|
|
338
425
|
childThreadId: branchChildThreadId,
|
|
339
|
-
approvalExecutionScope:
|
|
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
|
|
340
433
|
};
|
|
341
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
|
+
}
|
|
342
462
|
/** Captures one exact checkpoint head per namespace for a child thread. */
|
|
343
463
|
async getLatestCheckpointSnapshot(threadId) {
|
|
344
464
|
if (this.checkpointer == null) return [];
|
|
@@ -404,19 +524,20 @@ var SubagentExecutor = class {
|
|
|
404
524
|
}
|
|
405
525
|
}
|
|
406
526
|
}
|
|
407
|
-
async createResumeManifest(
|
|
527
|
+
async createResumeManifest(records) {
|
|
408
528
|
if (this.checkpointer == null) return;
|
|
409
529
|
const executions = [];
|
|
410
|
-
for (const
|
|
411
|
-
|
|
530
|
+
for (const record of records) {
|
|
531
|
+
const { identity, binding, activeRun } = record.snapshot;
|
|
532
|
+
if (identity == null) continue;
|
|
533
|
+
const { parentToolCallId } = record.address;
|
|
412
534
|
const checkpoints = await this.getLatestCheckpointSnapshot(identity.childThreadId);
|
|
413
535
|
if (checkpoints.length === 0) continue;
|
|
414
|
-
const
|
|
415
|
-
const configuredHookSessionId = activeChildRun?.invokeConfig?.configurable?.run_id;
|
|
536
|
+
const configuredHookSessionId = activeRun?.invokeConfig?.configurable?.run_id;
|
|
416
537
|
const hookSessionId = typeof configuredHookSessionId === "string" && configuredHookSessionId.length > 0 ? configuredHookSessionId : this.parentRunId;
|
|
417
538
|
const approvalReplays = this.hookRegistry?.snapshotPendingToolApprovals(identity.approvalExecutionScope, identity.approvalExecutionScope) ?? [];
|
|
418
|
-
const descendant =
|
|
419
|
-
const graphState =
|
|
539
|
+
const descendant = activeRun?.pendingInterrupts.map((pendingInterrupt) => require_SubagentReplay.getSubagentResumeManifest(pendingInterrupt.value)).find((manifest) => manifest != null);
|
|
540
|
+
const graphState = activeRun?.graph.createSubagentResumeState(hookSessionId) ?? {
|
|
420
541
|
toolCallSteps: [],
|
|
421
542
|
toolSessions: [],
|
|
422
543
|
toolNodes: [],
|
|
@@ -426,6 +547,8 @@ var SubagentExecutor = class {
|
|
|
426
547
|
executions.push({
|
|
427
548
|
parentToolCallId,
|
|
428
549
|
childRunId: identity.childRunId,
|
|
550
|
+
...binding?.subagentType == null ? {} : { subagentType: binding.subagentType },
|
|
551
|
+
...binding?.configId == null ? {} : { configId: binding.configId },
|
|
429
552
|
approvalExecutionScope: identity.approvalExecutionScope,
|
|
430
553
|
checkpoints,
|
|
431
554
|
graphState,
|
|
@@ -439,52 +562,101 @@ var SubagentExecutor = class {
|
|
|
439
562
|
executions
|
|
440
563
|
};
|
|
441
564
|
}
|
|
442
|
-
getResumeManifest(parentToolCallIds) {
|
|
443
|
-
return this.createResumeManifest(
|
|
565
|
+
getResumeManifest(parentToolCallIds, config) {
|
|
566
|
+
return this.createResumeManifest(this.executions.selectForResume({
|
|
567
|
+
parentToolCallIds,
|
|
568
|
+
config
|
|
569
|
+
}));
|
|
444
570
|
}
|
|
445
571
|
getChildCheckpointThreadIds() {
|
|
446
|
-
|
|
447
|
-
for (const activeChildRun of this.activeChildRuns.values()) for (const threadId of this.getGraphChildCheckpointThreadIds(activeChildRun.graph)) threadIds.add(threadId);
|
|
448
|
-
return [...threadIds];
|
|
572
|
+
return this.executions.getCheckpointThreadIds((activeRun) => this.getGraphChildCheckpointThreadIds(activeRun.graph));
|
|
449
573
|
}
|
|
450
574
|
resetCheckpointThreadIds() {
|
|
451
|
-
this.
|
|
575
|
+
this.executions.resetCheckpointThreadIds();
|
|
452
576
|
}
|
|
453
577
|
getGraphChildCheckpointThreadIds(graph) {
|
|
454
578
|
return graph.getChildCheckpointThreadIds?.() ?? [];
|
|
455
579
|
}
|
|
456
580
|
clearChildGraph(graph) {
|
|
457
|
-
|
|
581
|
+
this.executions.rememberCheckpointThread(...this.getGraphChildCheckpointThreadIds(graph));
|
|
458
582
|
graph.clearHeavyState();
|
|
459
583
|
}
|
|
460
584
|
clearHeavyState() {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
this.childExecutionIdentities.clear();
|
|
466
|
-
this.startedChildRuns.clear();
|
|
467
|
-
this.completedChildRuns.clear();
|
|
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
|
+
});
|
|
468
589
|
this.replayCheckpointWorkflow = void 0;
|
|
469
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
|
+
}
|
|
470
612
|
async getSettledToolOutput(call, config) {
|
|
471
613
|
const parentToolCallId = call.id;
|
|
472
614
|
if (this.humanInTheLoop?.enabled !== true || this.checkpointer == null || parentToolCallId == null || parentToolCallId === "") return;
|
|
473
615
|
const parentConfigurable = config.configurable;
|
|
474
616
|
const threadId = parentConfigurable?.thread_id;
|
|
475
|
-
const
|
|
617
|
+
const execution = this.executions.open({
|
|
476
618
|
threadId: typeof threadId === "string" ? threadId : void 0,
|
|
477
619
|
parentToolCallId,
|
|
478
620
|
parentConfigurable
|
|
479
621
|
});
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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;
|
|
488
660
|
const persistedHookSessionId = marker?.hookSessionId ?? getSubagentHookSessionId(messages);
|
|
489
661
|
const currentHookSessionId = parentConfigurable?.run_id;
|
|
490
662
|
if (persistedHookSessionId != null && typeof currentHookSessionId === "string" && currentHookSessionId.length > 0) this.hookRegistry?.copySession(persistedHookSessionId, currentHookSessionId);
|
|
@@ -492,37 +664,64 @@ var SubagentExecutor = class {
|
|
|
492
664
|
}
|
|
493
665
|
async persistSettledToolOutput(call, config, settled) {
|
|
494
666
|
const parentToolCallId = call.id;
|
|
495
|
-
|
|
667
|
+
const checkpointer = this.checkpointer;
|
|
668
|
+
if (this.humanInTheLoop?.enabled !== true || checkpointer == null || parentToolCallId == null || parentToolCallId === "") return;
|
|
496
669
|
const parentConfigurable = config.configurable;
|
|
497
670
|
const threadId = parentConfigurable?.thread_id;
|
|
498
|
-
const
|
|
671
|
+
const execution = this.executions.open({
|
|
499
672
|
threadId: typeof threadId === "string" ? threadId : void 0,
|
|
500
673
|
parentToolCallId,
|
|
501
674
|
parentConfigurable
|
|
502
675
|
});
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
this.
|
|
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);
|
|
515
688
|
return;
|
|
516
689
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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
|
+
}
|
|
526
725
|
}
|
|
527
726
|
async persistChildCheckpointMarker(activeChildRun, parentToolCallId, settledOutput) {
|
|
528
727
|
if (this.humanInTheLoop?.enabled !== true || activeChildRun.workflow.updateState == null || activeChildRun.invokeConfig == null) return;
|
|
@@ -533,9 +732,53 @@ var SubagentExecutor = class {
|
|
|
533
732
|
hookSessionId: typeof activeChildRun.invokeConfig.configurable?.run_id === "string" ? activeChildRun.invokeConfig.configurable.run_id : this.parentRunId,
|
|
534
733
|
childRunId: activeChildRun.childRunId,
|
|
535
734
|
...settledOutput == null ? {} : { settledOutput }
|
|
536
|
-
})] }, activeChildRun.
|
|
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
|
+
}
|
|
537
780
|
}
|
|
538
|
-
async
|
|
781
|
+
async executeOnce(params, execution, executableConfig) {
|
|
539
782
|
const { description, subagentType, threadId, parentToolCallId } = params;
|
|
540
783
|
/** Captured ONCE per execution, preferring the controller the parent
|
|
541
784
|
* tool batch captured at ITS entry (before PreToolUse hooks): a failed
|
|
@@ -545,35 +788,54 @@ var SubagentExecutor = class {
|
|
|
545
788
|
* new run's breaker from an old child's stream-limit breach. Signal and
|
|
546
789
|
* trip target both bind to this capture. */
|
|
547
790
|
const childBreaker = params.breaker ?? this.resolveBreakerController();
|
|
548
|
-
const childSignal = this.composeChildSignal(childBreaker);
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
|
|
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,
|
|
560
818
|
messages: []
|
|
561
819
|
};
|
|
562
|
-
const
|
|
563
|
-
const { childRunId, childThreadId, approvalExecutionScope, resumeExecution } = await this.resolveChildExecutionIdentity({
|
|
564
|
-
threadId,
|
|
565
|
-
parentToolCallId: executionSuffix,
|
|
566
|
-
parentConfigurable: params.parentConfigurable
|
|
567
|
-
});
|
|
568
|
-
this.childExecutionIdentities.set(executionSuffix, {
|
|
569
|
-
childRunId,
|
|
570
|
-
childThreadId,
|
|
571
|
-
approvalExecutionScope
|
|
572
|
-
});
|
|
573
|
-
const childExecutionKey = childThreadId;
|
|
574
|
-
const childAgentId = config.agentInputs.agentId || `${this.parentAgentId ?? "agent"}_sub_${executionSuffix}`;
|
|
575
|
-
const completedChildResult = this.completedChildResults.get(childExecutionKey);
|
|
820
|
+
const completedChildResult = execution.completedResult;
|
|
576
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,
|
|
836
|
+
messages: []
|
|
837
|
+
};
|
|
838
|
+
}
|
|
577
839
|
const parentRegistry = this.getParentHandlerRegistry();
|
|
578
840
|
const forwardingEnabled = parentRegistry != null;
|
|
579
841
|
/**
|
|
@@ -586,18 +848,47 @@ var SubagentExecutor = class {
|
|
|
586
848
|
* recoverable "no tools" path for registry-but-no-handler configs.
|
|
587
849
|
*/
|
|
588
850
|
const hasToolExecuteHandler = parentRegistry?.getHandler("on_tool_execute") != null;
|
|
589
|
-
const
|
|
590
|
-
|
|
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);
|
|
591
876
|
const hostUsageSink = this.usageSink;
|
|
592
|
-
|
|
593
|
-
|
|
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 = {
|
|
594
884
|
runId: childRunId,
|
|
595
885
|
signal: childSignal,
|
|
596
|
-
agents:
|
|
886
|
+
agents: childPlan.agents,
|
|
597
887
|
langfuse: this.langfuse,
|
|
598
888
|
tokenCounter: this.tokenCounter,
|
|
599
889
|
streamLimits: this.streamLimits,
|
|
600
890
|
subagentScope: true,
|
|
891
|
+
subagentExecutionContext: childExecutionContext,
|
|
601
892
|
/**
|
|
602
893
|
* Forwarded so the child graph's own `SubagentExecutor` (created in
|
|
603
894
|
* its `createAgentNode` when `allowNested` keeps subagentConfigs)
|
|
@@ -605,43 +896,52 @@ var SubagentExecutor = class {
|
|
|
605
896
|
* level attaches its own capture callback — `workflow.invoke` replaces
|
|
606
897
|
* the inherited callback chain, so a single top-level handler would
|
|
607
898
|
* never see grandchild model calls.
|
|
608
|
-
*
|
|
609
|
-
* The wrapper rewrites `runId` to THIS executor's parent run: nested
|
|
610
|
-
* executors emit with their own `parentRunId` (a `*_sub_*` child id),
|
|
611
|
-
* and each wrapper layer rewrites upward, so by the time an event
|
|
612
|
-
* reaches the host sink its `runId` is the ROOT run — hosts keying
|
|
613
|
-
* billing by run id never see intermediate child run ids there
|
|
614
|
-
* (`subagentRunId` still identifies the emitting child).
|
|
615
899
|
*/
|
|
616
|
-
subagentUsageSink
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
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);
|
|
621
919
|
let forwarding;
|
|
622
920
|
if (forwardingEnabled) forwarding = this.createForwarderCallback({
|
|
623
921
|
parentRegistry,
|
|
624
922
|
subagentType,
|
|
923
|
+
subagentKind: childPlan.kind,
|
|
625
924
|
subagentAgentId: childAgentId,
|
|
626
925
|
childRunId,
|
|
627
|
-
parentToolCallId
|
|
926
|
+
parentToolCallId,
|
|
927
|
+
executionContext: childExecutionContext
|
|
628
928
|
});
|
|
629
929
|
const forwarder = forwarding?.handler;
|
|
630
|
-
let childAlreadyStarted =
|
|
631
|
-
let childAlreadyCompleted =
|
|
930
|
+
let childAlreadyStarted = execution.started;
|
|
931
|
+
let childAlreadyCompleted = execution.completed;
|
|
632
932
|
let result;
|
|
633
933
|
let recoveredComplete = false;
|
|
634
934
|
let recoveredInProgress = false;
|
|
635
935
|
try {
|
|
636
936
|
const workflow = cachedChildRun?.workflow ?? childGraph.createWorkflow();
|
|
637
|
-
const activeChildRun = cachedChildRun ?? {
|
|
937
|
+
const activeChildRun = execution.activate(cachedChildRun ?? {
|
|
638
938
|
graph: childGraph,
|
|
639
939
|
workflow,
|
|
640
940
|
pendingInterrupts: [],
|
|
641
941
|
childAgentId,
|
|
942
|
+
checkpointNodeId: childPlan.checkpointNodeId,
|
|
642
943
|
childRunId
|
|
643
|
-
};
|
|
644
|
-
if (cachedChildRun == null) this.activeChildRuns.set(childExecutionKey, activeChildRun);
|
|
944
|
+
});
|
|
645
945
|
/**
|
|
646
946
|
* When `parentHandlerRegistry` is provided (forwarding mode), attach a
|
|
647
947
|
* lightweight callback that intercepts the child's `on_custom_event`
|
|
@@ -671,11 +971,12 @@ var SubagentExecutor = class {
|
|
|
671
971
|
if (this.usageSink) callbackHandlers.push(createUsageCaptureHandler({
|
|
672
972
|
sink: this.usageSink,
|
|
673
973
|
subagentType,
|
|
974
|
+
subagentKind: childPlan.kind,
|
|
674
975
|
subagentRunId: childRunId,
|
|
675
976
|
subagentAgentId: childAgentId,
|
|
676
977
|
parentRunId: this.parentRunId,
|
|
677
|
-
|
|
678
|
-
|
|
978
|
+
executionContext: childExecutionContext,
|
|
979
|
+
memberInputs: childPlan.memberInputs
|
|
679
980
|
}));
|
|
680
981
|
const callbacks = callbackHandlers;
|
|
681
982
|
/**
|
|
@@ -692,23 +993,21 @@ var SubagentExecutor = class {
|
|
|
692
993
|
* the independently checkpointed child execution.
|
|
693
994
|
*/
|
|
694
995
|
const inheritedConfigurable = sanitizeChildConfigurable(params.parentConfigurable);
|
|
695
|
-
const resumeAttemptId =
|
|
696
|
-
const currentHookSessionId = typeof inheritedConfigurable.run_id === "string" && inheritedConfigurable.run_id.length > 0 ? inheritedConfigurable.run_id : this.parentRunId;
|
|
996
|
+
const { resumeAttemptId } = execution.address;
|
|
697
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;
|
|
698
1005
|
const childInvokeConfig = {
|
|
699
|
-
recursionLimit
|
|
1006
|
+
recursionLimit,
|
|
700
1007
|
signal: childSignal,
|
|
701
1008
|
callbacks,
|
|
702
1009
|
runName: `subagent:${subagentType}`,
|
|
703
|
-
configurable:
|
|
704
|
-
...inheritedConfigurable,
|
|
705
|
-
...this.humanInTheLoop?.enabled === true ? { [require_types.TOOL_APPROVAL_EXECUTION_SCOPE_CONFIG_KEY]: approvalExecutionScope } : {},
|
|
706
|
-
...resumeExecution?.descendant == null ? {} : {
|
|
707
|
-
[require_SubagentReplay.SUBAGENT_RESUME_MANIFEST_CONFIG_KEY]: resumeExecution.descendant,
|
|
708
|
-
[require_SubagentReplay.SUBAGENT_RESUME_ATTEMPT_CONFIG_KEY]: resumeAttemptId
|
|
709
|
-
},
|
|
710
|
-
thread_id: this.humanInTheLoop?.enabled === true ? childThreadId : inheritedConfigurable.thread_id ?? childRunId
|
|
711
|
-
}
|
|
1010
|
+
configurable: childConfigurable
|
|
712
1011
|
};
|
|
713
1012
|
activeChildRun.invokeConfig = childInvokeConfig;
|
|
714
1013
|
if (cachedChildRun == null && this.humanInTheLoop?.enabled === true) {
|
|
@@ -721,12 +1020,12 @@ var SubagentExecutor = class {
|
|
|
721
1020
|
const persistedInterrupts = getPersistedInterrupts(persistedState);
|
|
722
1021
|
if (persistedInterrupts.length > 0) {
|
|
723
1022
|
activeChildRun.pendingInterrupts = persistedInterrupts;
|
|
724
|
-
|
|
1023
|
+
execution.markStarted();
|
|
725
1024
|
childAlreadyStarted = true;
|
|
726
1025
|
} else if (persistedState.next.length > 0) {
|
|
727
1026
|
recoveredInProgress = true;
|
|
728
1027
|
childAlreadyStarted = true;
|
|
729
|
-
|
|
1028
|
+
execution.markStarted();
|
|
730
1029
|
} else if (persistedState.next.length === 0) {
|
|
731
1030
|
const persistedMessages = getPersistedMessages(persistedState);
|
|
732
1031
|
if (persistedMessages != null) {
|
|
@@ -735,7 +1034,7 @@ var SubagentExecutor = class {
|
|
|
735
1034
|
recoveredComplete = true;
|
|
736
1035
|
childAlreadyStarted = true;
|
|
737
1036
|
childAlreadyCompleted = marker?.lifecycleComplete === true;
|
|
738
|
-
|
|
1037
|
+
execution.markStarted();
|
|
739
1038
|
}
|
|
740
1039
|
}
|
|
741
1040
|
}
|
|
@@ -752,36 +1051,39 @@ var SubagentExecutor = class {
|
|
|
752
1051
|
[SUBAGENT_RUN_ID_KEY]: childRunId
|
|
753
1052
|
}
|
|
754
1053
|
})] };
|
|
755
|
-
if (!childAlreadyStarted && this.hookRegistry?.hasHookFor("SubagentStart",
|
|
1054
|
+
if (!childAlreadyStarted && this.hookRegistry?.hasHookFor("SubagentStart", currentHookSessionId) === true) {
|
|
756
1055
|
const hookResult = await require_executeHooks.executeHooks({
|
|
757
1056
|
registry: this.hookRegistry,
|
|
758
1057
|
input: {
|
|
759
1058
|
hook_event_name: "SubagentStart",
|
|
760
|
-
runId:
|
|
1059
|
+
runId: currentHookSessionId,
|
|
761
1060
|
threadId,
|
|
762
1061
|
parentAgentId: this.parentAgentId,
|
|
763
1062
|
agentId: childAgentId,
|
|
764
1063
|
agentType: subagentType,
|
|
765
1064
|
inputs: [new _langchain_core_messages.HumanMessage(description)]
|
|
766
1065
|
},
|
|
767
|
-
sessionId:
|
|
1066
|
+
sessionId: currentHookSessionId,
|
|
768
1067
|
matchQuery: subagentType
|
|
769
1068
|
}).catch(() => HOOK_FALLBACK);
|
|
770
1069
|
if (hookResult.decision === "deny" || hookResult.decision === "ask") {
|
|
771
1070
|
this.clearChildGraph(childGraph);
|
|
772
|
-
|
|
1071
|
+
execution.releaseActiveRun();
|
|
1072
|
+
this.executions.remove(execution);
|
|
773
1073
|
return {
|
|
774
1074
|
content: `Blocked: ${hookResult.reason ?? "Blocked by hook"}`,
|
|
775
1075
|
messages: []
|
|
776
1076
|
};
|
|
777
1077
|
}
|
|
778
1078
|
}
|
|
779
|
-
|
|
1079
|
+
execution.markStarted();
|
|
780
1080
|
if (forwarder && !childAlreadyStarted) await this.emitSubagentUpdate(parentRegistry, {
|
|
781
1081
|
childRunId,
|
|
782
1082
|
subagentType,
|
|
1083
|
+
subagentKind: childPlan.kind,
|
|
783
1084
|
subagentAgentId: childAgentId,
|
|
784
1085
|
parentToolCallId,
|
|
1086
|
+
executionContext: childExecutionContext,
|
|
785
1087
|
phase: "start",
|
|
786
1088
|
label: `Subagent "${subagentType}" started`
|
|
787
1089
|
});
|
|
@@ -794,13 +1096,16 @@ var SubagentExecutor = class {
|
|
|
794
1096
|
else childResult = await workflow.invoke(childInput, childInvokeConfig);
|
|
795
1097
|
const childInterrupts = (0, _langchain_langgraph.isInterrupted)(childResult) ? childResult[_langchain_langgraph.INTERRUPT] : void 0;
|
|
796
1098
|
if (childInterrupts != null && childInterrupts.length > 0) throw new _langchain_langgraph.GraphInterrupt(childInterrupts);
|
|
797
|
-
result =
|
|
1099
|
+
result = childResult;
|
|
798
1100
|
}
|
|
799
1101
|
} catch (error) {
|
|
800
1102
|
if ((0, _langchain_langgraph.isGraphInterrupt)(error)) {
|
|
801
|
-
const activeChildRun =
|
|
802
|
-
if (activeChildRun != null)
|
|
803
|
-
|
|
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]);
|
|
804
1109
|
await forwarding?.drain();
|
|
805
1110
|
throw new _langchain_langgraph.GraphInterrupt(addSubagentScope(error.interrupts, {
|
|
806
1111
|
run_id: childRunId,
|
|
@@ -823,15 +1128,17 @@ var SubagentExecutor = class {
|
|
|
823
1128
|
await this.emitSubagentUpdate(parentRegistry, {
|
|
824
1129
|
childRunId,
|
|
825
1130
|
subagentType,
|
|
1131
|
+
subagentKind: childPlan.kind,
|
|
826
1132
|
subagentAgentId: childAgentId,
|
|
827
1133
|
parentToolCallId,
|
|
1134
|
+
executionContext: childExecutionContext,
|
|
828
1135
|
phase: "error",
|
|
829
1136
|
label: `Subagent "${subagentType}" errored: ${errorMessage}`,
|
|
830
1137
|
data: { message: errorMessage }
|
|
831
1138
|
});
|
|
832
1139
|
}
|
|
833
1140
|
this.clearChildGraph(childGraph);
|
|
834
|
-
|
|
1141
|
+
execution.markFailed();
|
|
835
1142
|
/**
|
|
836
1143
|
* A tripped stream circuit breaker is a safety abort, not a recoverable
|
|
837
1144
|
* subagent failure: converting it into a tool result would let the
|
|
@@ -846,8 +1153,8 @@ var SubagentExecutor = class {
|
|
|
846
1153
|
};
|
|
847
1154
|
}
|
|
848
1155
|
if (result == null) throw new Error("Subagent completed without producing graph state.");
|
|
849
|
-
const filteredContent = filterSubagentResult(result.messages);
|
|
850
|
-
if (!childAlreadyCompleted && this.hookRegistry?.hasHookFor("SubagentStop",
|
|
1156
|
+
const filteredContent = childPlan.kind === "graph" ? filterGraphSubagentResult(result, childPlan.resultAgentId) : filterSubagentResult(result.messages);
|
|
1157
|
+
if (!childAlreadyCompleted && this.hookRegistry?.hasHookFor("SubagentStop", currentHookSessionId) === true)
|
|
851
1158
|
/**
|
|
852
1159
|
* Awaited (not fire-and-forget) for deterministic test synchronization
|
|
853
1160
|
* and consistency with PostCompact. The parent is already waiting on the
|
|
@@ -858,13 +1165,13 @@ var SubagentExecutor = class {
|
|
|
858
1165
|
registry: this.hookRegistry,
|
|
859
1166
|
input: {
|
|
860
1167
|
hook_event_name: "SubagentStop",
|
|
861
|
-
runId:
|
|
1168
|
+
runId: currentHookSessionId,
|
|
862
1169
|
threadId,
|
|
863
1170
|
agentId: childAgentId,
|
|
864
1171
|
agentType: subagentType,
|
|
865
1172
|
messages: result.messages
|
|
866
1173
|
},
|
|
867
|
-
sessionId:
|
|
1174
|
+
sessionId: currentHookSessionId,
|
|
868
1175
|
matchQuery: subagentType
|
|
869
1176
|
}).catch(() => {});
|
|
870
1177
|
if (forwarding && !childAlreadyCompleted) {
|
|
@@ -872,23 +1179,24 @@ var SubagentExecutor = class {
|
|
|
872
1179
|
await this.emitSubagentUpdate(parentRegistry, {
|
|
873
1180
|
childRunId,
|
|
874
1181
|
subagentType,
|
|
1182
|
+
subagentKind: childPlan.kind,
|
|
875
1183
|
subagentAgentId: childAgentId,
|
|
876
1184
|
parentToolCallId,
|
|
1185
|
+
executionContext: childExecutionContext,
|
|
877
1186
|
phase: "stop",
|
|
878
1187
|
label: `Subagent "${subagentType}" finished`
|
|
879
1188
|
});
|
|
880
1189
|
}
|
|
881
1190
|
if (!childAlreadyCompleted) {
|
|
882
|
-
const activeChildRun =
|
|
1191
|
+
const activeChildRun = execution.activeRun;
|
|
883
1192
|
if (activeChildRun != null && parentToolCallId != null) await this.persistChildCheckpointMarker(activeChildRun, parentToolCallId);
|
|
884
1193
|
}
|
|
885
|
-
this.completedChildRuns.add(childExecutionKey);
|
|
886
1194
|
this.clearChildGraph(childGraph);
|
|
887
1195
|
const completedResult = {
|
|
888
1196
|
content: filteredContent,
|
|
889
1197
|
messages: result.messages
|
|
890
1198
|
};
|
|
891
|
-
|
|
1199
|
+
execution.markCompleted(completedResult);
|
|
892
1200
|
return completedResult;
|
|
893
1201
|
}
|
|
894
1202
|
/**
|
|
@@ -899,21 +1207,23 @@ var SubagentExecutor = class {
|
|
|
899
1207
|
async emitSubagentUpdate(parentRegistry, args) {
|
|
900
1208
|
const handler = parentRegistry.getHandler("on_subagent_update");
|
|
901
1209
|
if (!handler) return;
|
|
902
|
-
|
|
903
|
-
runId:
|
|
1210
|
+
await dispatchObservationalSubagentUpdate(handler, {
|
|
1211
|
+
runId: args.executionContext.rootRunId,
|
|
1212
|
+
parentRunId: this.parentRunId,
|
|
904
1213
|
subagentRunId: args.childRunId,
|
|
905
1214
|
subagentType: args.subagentType,
|
|
1215
|
+
subagentKind: args.subagentKind,
|
|
906
1216
|
subagentAgentId: args.subagentAgentId,
|
|
1217
|
+
memberAgentId: args.memberAgentId,
|
|
907
1218
|
parentAgentId: this.parentAgentId,
|
|
908
1219
|
parentToolCallId: args.parentToolCallId,
|
|
1220
|
+
depth: args.executionContext.depth,
|
|
1221
|
+
ancestry: args.executionContext.ancestry,
|
|
909
1222
|
phase: args.phase,
|
|
910
1223
|
data: args.data,
|
|
911
1224
|
label: args.label,
|
|
912
1225
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
913
|
-
};
|
|
914
|
-
try {
|
|
915
|
-
await handler.handle("on_subagent_update", event);
|
|
916
|
-
} catch {}
|
|
1226
|
+
});
|
|
917
1227
|
}
|
|
918
1228
|
/**
|
|
919
1229
|
* Builds a BaseCallbackHandler that intercepts the child graph's custom
|
|
@@ -928,35 +1238,40 @@ var SubagentExecutor = class {
|
|
|
928
1238
|
* cares about; host apps can extend by registering more phases).
|
|
929
1239
|
*/
|
|
930
1240
|
createForwarderCallback(args) {
|
|
931
|
-
const { parentRegistry, subagentType, subagentAgentId, childRunId, parentToolCallId } = args;
|
|
932
|
-
const
|
|
1241
|
+
const { parentRegistry, subagentType, subagentKind, subagentAgentId, childRunId, parentToolCallId, executionContext } = args;
|
|
1242
|
+
const immediateParentRunId = this.parentRunId;
|
|
933
1243
|
const parentAgentId = this.parentAgentId;
|
|
934
|
-
const wrap = async (eventName, phase, data) => {
|
|
1244
|
+
const wrap = async (eventName, phase, data, memberAgentId) => {
|
|
935
1245
|
const handler = parentRegistry.getHandler("on_subagent_update");
|
|
936
|
-
if (!handler) return;
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
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
|
+
});
|
|
952
1264
|
};
|
|
953
1265
|
const queuedUpdates = [];
|
|
954
1266
|
let drainPromise;
|
|
1267
|
+
let queueOpen = true;
|
|
955
1268
|
const enqueue = (update) => {
|
|
956
|
-
if (
|
|
957
|
-
|
|
1269
|
+
if (!queueOpen) return;
|
|
1270
|
+
if (queuedUpdates.length >= MAX_QUEUED_SUBAGENT_UPDATES) {
|
|
1271
|
+
const dropIndex = queuedUpdates.findIndex((queued) => isLowPrioritySubagentUpdatePhase(queued.phase));
|
|
958
1272
|
if (dropIndex >= 0) queuedUpdates.splice(dropIndex, 1);
|
|
959
|
-
else if (
|
|
1273
|
+
else if (isLowPrioritySubagentUpdatePhase(update.phase)) return;
|
|
1274
|
+
else queuedUpdates.shift();
|
|
960
1275
|
}
|
|
961
1276
|
queuedUpdates.push(update);
|
|
962
1277
|
};
|
|
@@ -969,7 +1284,10 @@ var SubagentExecutor = class {
|
|
|
969
1284
|
while (queuedUpdates.length > 0) {
|
|
970
1285
|
const update = queuedUpdates.shift();
|
|
971
1286
|
if (update == null) continue;
|
|
972
|
-
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
|
+
}
|
|
973
1291
|
}
|
|
974
1292
|
})();
|
|
975
1293
|
try {
|
|
@@ -979,15 +1297,17 @@ var SubagentExecutor = class {
|
|
|
979
1297
|
if (queuedUpdates.length > 0) await drain();
|
|
980
1298
|
}
|
|
981
1299
|
};
|
|
982
|
-
const scheduleWrap = (eventName, phase, data) => {
|
|
1300
|
+
const scheduleWrap = (eventName, phase, data, memberAgentId) => {
|
|
983
1301
|
enqueue({
|
|
984
1302
|
eventName,
|
|
985
1303
|
phase,
|
|
986
|
-
data
|
|
1304
|
+
data,
|
|
1305
|
+
memberAgentId
|
|
987
1306
|
});
|
|
988
1307
|
drain();
|
|
989
1308
|
};
|
|
990
|
-
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);
|
|
991
1311
|
if (eventName === "on_tool_execute") {
|
|
992
1312
|
const toolHandler = parentRegistry.getHandler("on_tool_execute");
|
|
993
1313
|
if (toolHandler) await toolHandler.handle("on_tool_execute", data);
|
|
@@ -995,27 +1315,27 @@ var SubagentExecutor = class {
|
|
|
995
1315
|
* We also surface a short notice in the subagent-update stream so
|
|
996
1316
|
* the UI can show "calling <tool>" for each tool the child spawns.
|
|
997
1317
|
*/
|
|
998
|
-
scheduleWrap(eventName, "run_step", data);
|
|
1318
|
+
scheduleWrap(eventName, "run_step", data, memberAgentId);
|
|
999
1319
|
return;
|
|
1000
1320
|
}
|
|
1001
1321
|
if (eventName === "on_run_step") {
|
|
1002
|
-
scheduleWrap(eventName, "run_step", data);
|
|
1322
|
+
scheduleWrap(eventName, "run_step", data, memberAgentId);
|
|
1003
1323
|
return;
|
|
1004
1324
|
}
|
|
1005
1325
|
if (eventName === "on_run_step_delta") {
|
|
1006
|
-
scheduleWrap(eventName, "run_step_delta", data);
|
|
1326
|
+
scheduleWrap(eventName, "run_step_delta", data, memberAgentId);
|
|
1007
1327
|
return;
|
|
1008
1328
|
}
|
|
1009
1329
|
if (eventName === "on_run_step_completed") {
|
|
1010
|
-
scheduleWrap(eventName, "run_step_completed", data);
|
|
1330
|
+
scheduleWrap(eventName, "run_step_completed", data, memberAgentId);
|
|
1011
1331
|
return;
|
|
1012
1332
|
}
|
|
1013
1333
|
if (eventName === "on_message_delta") {
|
|
1014
|
-
scheduleWrap(eventName, "message_delta", data);
|
|
1334
|
+
scheduleWrap(eventName, "message_delta", data, memberAgentId);
|
|
1015
1335
|
return;
|
|
1016
1336
|
}
|
|
1017
1337
|
if (eventName === "on_reasoning_delta") {
|
|
1018
|
-
scheduleWrap(eventName, "reasoning_delta", data);
|
|
1338
|
+
scheduleWrap(eventName, "reasoning_delta", data, memberAgentId);
|
|
1019
1339
|
return;
|
|
1020
1340
|
}
|
|
1021
1341
|
} });
|
|
@@ -1049,7 +1369,8 @@ var SubagentExecutor = class {
|
|
|
1049
1369
|
* `subagentUsageSink` on the child graph's input.
|
|
1050
1370
|
*/
|
|
1051
1371
|
function createUsageCaptureHandler(args) {
|
|
1052
|
-
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;
|
|
1053
1374
|
/**
|
|
1054
1375
|
* Per-call attribution keyed by LangChain callback runId. `model` joins
|
|
1055
1376
|
* `ls_model_name` (provider-reported) with `INVOKED_MODEL` (stamped by
|
|
@@ -1064,16 +1385,21 @@ function createUsageCaptureHandler(args) {
|
|
|
1064
1385
|
handleChatModelStart: (_llm, _messages, runId, _parentRunId, _extraParams, _tags, metadata) => {
|
|
1065
1386
|
const callModel = asNonEmptyString(metadata?.ls_model_name) ?? asNonEmptyString(metadata?.["__invoked_model"]);
|
|
1066
1387
|
const callProvider = asNonEmptyString(metadata?.["__invoked_provider"]);
|
|
1067
|
-
|
|
1388
|
+
const memberAgentId = asNonEmptyString(metadata?.agentId);
|
|
1389
|
+
if (callModel != null || callProvider != null || memberAgentId != null) callInfoByCallId.set(runId, {
|
|
1068
1390
|
model: callModel,
|
|
1069
|
-
provider: callProvider
|
|
1391
|
+
provider: callProvider,
|
|
1392
|
+
memberAgentId
|
|
1070
1393
|
});
|
|
1071
1394
|
},
|
|
1072
1395
|
handleLLMEnd: async (output, runId) => {
|
|
1073
1396
|
const callInfo = callInfoByCallId.get(runId);
|
|
1074
1397
|
callInfoByCallId.delete(runId);
|
|
1075
|
-
const
|
|
1076
|
-
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;
|
|
1077
1403
|
for (const generationGroup of output.generations)
|
|
1078
1404
|
/**
|
|
1079
1405
|
* At most ONE event per generation group: each group is one
|
|
@@ -1098,9 +1424,14 @@ function createUsageCaptureHandler(args) {
|
|
|
1098
1424
|
model,
|
|
1099
1425
|
provider: callProvider,
|
|
1100
1426
|
subagentType,
|
|
1427
|
+
subagentKind,
|
|
1101
1428
|
subagentRunId,
|
|
1102
1429
|
subagentAgentId,
|
|
1103
|
-
|
|
1430
|
+
memberAgentId,
|
|
1431
|
+
parentRunId,
|
|
1432
|
+
depth: executionContext.depth,
|
|
1433
|
+
ancestry: executionContext.ancestry,
|
|
1434
|
+
runId: executionContext.rootRunId
|
|
1104
1435
|
});
|
|
1105
1436
|
} catch {}
|
|
1106
1437
|
break;
|
|
@@ -1121,6 +1452,11 @@ function createUsageCaptureHandler(args) {
|
|
|
1121
1452
|
function asNonEmptyString(value) {
|
|
1122
1453
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
1123
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
|
+
}
|
|
1124
1460
|
/**
|
|
1125
1461
|
* Best-effort read of the configured model from a subagent's client
|
|
1126
1462
|
* options. Providers disagree on the key (`model` vs `modelName`), and the
|
|
@@ -1131,12 +1467,50 @@ function extractConfiguredModel(agentInputs) {
|
|
|
1131
1467
|
if (typeof clientOptions?.model === "string" && clientOptions.model !== "") return clientOptions.model;
|
|
1132
1468
|
if (typeof clientOptions?.modelName === "string" && clientOptions.modelName !== "") return clientOptions.modelName;
|
|
1133
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
|
+
}
|
|
1134
1508
|
function sanitizeChildConfigurable(parentConfigurable) {
|
|
1135
1509
|
if (parentConfigurable == null) return {};
|
|
1136
1510
|
return Object.fromEntries(Object.entries(parentConfigurable).filter(([key]) => !isLangGraphRuntimeConfigKey(key)));
|
|
1137
1511
|
}
|
|
1138
1512
|
function isLangGraphRuntimeConfigKey(key) {
|
|
1139
|
-
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 === "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";
|
|
1140
1514
|
}
|
|
1141
1515
|
function sanitizeForwardedSubagentUpdateData(eventName, data) {
|
|
1142
1516
|
if (eventName === "on_tool_execute") return sanitizeToolExecuteUpdateData(data);
|
|
@@ -1146,7 +1520,7 @@ function sanitizeForwardedSubagentUpdateData(eventName, data) {
|
|
|
1146
1520
|
if (eventName === "on_message_delta") return sanitizeMessageDeltaUpdateData(data);
|
|
1147
1521
|
if (eventName === "on_reasoning_delta") return sanitizeReasoningDeltaUpdateData(data);
|
|
1148
1522
|
}
|
|
1149
|
-
function
|
|
1523
|
+
function isLowPrioritySubagentUpdatePhase(phase) {
|
|
1150
1524
|
return phase === "message_delta" || phase === "reasoning_delta" || phase === "run_step_delta";
|
|
1151
1525
|
}
|
|
1152
1526
|
function sanitizeToolExecuteUpdateData(data) {
|
|
@@ -1315,6 +1689,16 @@ function summarizeEvent(eventName, data) {
|
|
|
1315
1689
|
return eventName;
|
|
1316
1690
|
}
|
|
1317
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
|
+
/**
|
|
1318
1702
|
* Walk messages from last to first, returning the text content of the most
|
|
1319
1703
|
* recent AIMessage that has any. Non-text blocks (tool_use, thinking,
|
|
1320
1704
|
* redacted_thinking, tool_result) are stripped. If the last AIMessage is
|
|
@@ -1371,92 +1755,37 @@ function filterSubagentResult(messages) {
|
|
|
1371
1755
|
}
|
|
1372
1756
|
return "Task completed";
|
|
1373
1757
|
}
|
|
1374
|
-
/**
|
|
1375
|
-
* Resolve self-spawn configs by filling in agentInputs from the parent context.
|
|
1376
|
-
* Returns configs with agentInputs guaranteed present. Throws on duplicate
|
|
1377
|
-
* `type` values to prevent silent config shadowing.
|
|
1378
|
-
*/
|
|
1379
|
-
function resolveSubagentConfigs(configs, parentContext) {
|
|
1380
|
-
const resolved = configs.map((config) => {
|
|
1381
|
-
if (config.agentInputs != null) return config;
|
|
1382
|
-
if (config.self !== true || parentContext._sourceInputs == null) return null;
|
|
1383
|
-
return {
|
|
1384
|
-
...config,
|
|
1385
|
-
agentInputs: { ...parentContext._sourceInputs }
|
|
1386
|
-
};
|
|
1387
|
-
}).filter((c) => c != null);
|
|
1388
|
-
const seenTypes = /* @__PURE__ */ new Set();
|
|
1389
|
-
for (const config of resolved) {
|
|
1390
|
-
if (seenTypes.has(config.type)) throw new Error(`Duplicate subagent type "${config.type}". Each SubagentConfig must have a unique "type" field.`);
|
|
1391
|
-
seenTypes.add(config.type);
|
|
1392
|
-
}
|
|
1393
|
-
return resolved;
|
|
1394
|
-
}
|
|
1395
|
-
/**
|
|
1396
|
-
* Build child AgentInputs from a resolved config, stripping nesting and
|
|
1397
|
-
* (optionally) event-driven fields. When `allowNested: true`, the child's
|
|
1398
|
-
* `maxSubagentDepth` is decremented so that depth is consumed as the call
|
|
1399
|
-
* chain deepens across graph boundaries — the parent's executor-level check
|
|
1400
|
-
* alone cannot see into the child graph's separate executor.
|
|
1401
|
-
*
|
|
1402
|
-
* When `keepToolDefinitions` is `true`, the child retains the parent's
|
|
1403
|
-
* `toolDefinitions` so event-driven tools remain usable. This is only safe
|
|
1404
|
-
* when the caller has wired a forwarder for `ON_TOOL_EXECUTE` to a
|
|
1405
|
-
* registered handler — otherwise the child will hang on tool dispatch.
|
|
1406
|
-
*
|
|
1407
|
-
* @remarks Advanced utility: exported primarily for testing and by
|
|
1408
|
-
* {@link SubagentExecutor}. Host applications configuring subagents should
|
|
1409
|
-
* not need to call this directly — it is invoked internally when a subagent
|
|
1410
|
-
* tool is dispatched. The depth-countdown contract (parent's `maxDepth` in,
|
|
1411
|
-
* child's decremented `maxSubagentDepth` on the returned inputs) is the
|
|
1412
|
-
* mechanism that bounds nesting across graph boundaries; callers must
|
|
1413
|
-
* respect it.
|
|
1414
|
-
*/
|
|
1415
|
-
function buildChildInputs(config, childAgentId, parentMaxDepth, keepToolDefinitions = false) {
|
|
1416
|
-
const { agentInputs } = config;
|
|
1417
|
-
const childInputs = {
|
|
1418
|
-
...agentInputs,
|
|
1419
|
-
agentId: childAgentId,
|
|
1420
|
-
toolDefinitions: keepToolDefinitions ? agentInputs.toolDefinitions : void 0,
|
|
1421
|
-
/**
|
|
1422
|
-
* Subagents run in an isolated context by contract. Parent-run-scoped
|
|
1423
|
-
* fields that would otherwise survive the shallow-spread clone — the
|
|
1424
|
-
* cross-run conversation summary and the prior-turn tool-discovery
|
|
1425
|
-
* set — are cleared here so the child starts fresh. Host applications
|
|
1426
|
-
* that want a subagent to see parent context must thread it in
|
|
1427
|
-
* explicitly (e.g. via the `description` argument to the subagent
|
|
1428
|
-
* tool), not via inherited state.
|
|
1429
|
-
*/
|
|
1430
|
-
initialSummary: void 0,
|
|
1431
|
-
discoveredTools: void 0,
|
|
1432
|
-
/**
|
|
1433
|
-
* Host-supplied direct tools are scrubbed from INHERITED configs only.
|
|
1434
|
-
* A self-spawn config's `agentInputs` is a shallow spread of the parent's
|
|
1435
|
-
* `_sourceInputs`, so without this a parent-scoped graph tool (e.g. an
|
|
1436
|
-
* interrupt-raising ask_user_question) would silently become available to
|
|
1437
|
-
* the child. An EXPLICIT child config that lists its own `graphTools` is a
|
|
1438
|
-
* deliberate host choice and keeps them (Codex #289 P2); with HITL enabled,
|
|
1439
|
-
* those tools use the shared checkpointer and can pause and resume safely.
|
|
1440
|
-
*/
|
|
1441
|
-
graphTools: config.self === true ? void 0 : agentInputs.graphTools
|
|
1442
|
-
};
|
|
1443
|
-
if (config.allowNested === true) childInputs.maxSubagentDepth = Math.max(0, parentMaxDepth - 1);
|
|
1444
|
-
else {
|
|
1445
|
-
childInputs.subagentConfigs = void 0;
|
|
1446
|
-
childInputs.maxSubagentDepth = void 0;
|
|
1447
|
-
}
|
|
1448
|
-
return childInputs;
|
|
1449
|
-
}
|
|
1450
1758
|
function truncateErrorMessage(error) {
|
|
1451
1759
|
const message = error instanceof Error ? error.message : String(error);
|
|
1452
1760
|
if (message.length <= ERROR_MESSAGE_MAX_CHARS) return message;
|
|
1453
1761
|
return `${message.slice(0, ERROR_MESSAGE_MAX_CHARS)}...`;
|
|
1454
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
|
+
}
|
|
1455
1784
|
//#endregion
|
|
1785
|
+
exports.DEFAULT_SUBAGENT_DESCRIPTION = DEFAULT_SUBAGENT_DESCRIPTION;
|
|
1456
1786
|
exports.SubagentExecutor = SubagentExecutor;
|
|
1457
|
-
exports.
|
|
1787
|
+
exports.filterGraphSubagentResult = filterGraphSubagentResult;
|
|
1458
1788
|
exports.filterSubagentResult = filterSubagentResult;
|
|
1459
|
-
exports.resolveSubagentConfigs = resolveSubagentConfigs;
|
|
1460
1789
|
exports.summarizeEvent = summarizeEvent;
|
|
1461
1790
|
|
|
1462
1791
|
//# sourceMappingURL=SubagentExecutor.cjs.map
|