@librechat/agents 3.7.21 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/graphs/Graph.cjs +21 -4
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +6 -2
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +21 -6
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/main.cjs +2 -0
- package/dist/cjs/stream.cjs +38 -1
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/ArtifactDelivery.cjs +27 -0
- package/dist/cjs/tools/ArtifactDelivery.cjs.map +1 -0
- package/dist/cjs/tools/BashExecutor.cjs +6 -1
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +6 -1
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +5 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +95 -18
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +130 -24
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +1 -1
- package/dist/cjs/tools/preparedSubagents.cjs +129 -0
- package/dist/cjs/tools/preparedSubagents.cjs.map +1 -0
- package/dist/esm/graphs/Graph.mjs +21 -4
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +6 -2
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +21 -6
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/main.mjs +2 -2
- package/dist/esm/stream.mjs +39 -2
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/ArtifactDelivery.mjs +26 -0
- package/dist/esm/tools/ArtifactDelivery.mjs.map +1 -0
- package/dist/esm/tools/BashExecutor.mjs +6 -1
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +6 -1
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +5 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +95 -18
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +129 -25
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +1 -1
- package/dist/esm/tools/preparedSubagents.mjs +128 -0
- package/dist/esm/tools/preparedSubagents.mjs.map +1 -0
- package/dist/types/graphs/Graph.d.ts +6 -1
- package/dist/types/langfuse.d.ts +5 -0
- package/dist/types/tools/ArtifactDelivery.d.ts +4 -0
- package/dist/types/tools/ToolNode.d.ts +13 -1
- package/dist/types/tools/ToolSearch.d.ts +46 -2
- package/dist/types/tools/preparedSubagents.d.ts +26 -0
- package/dist/types/types/graph.d.ts +8 -0
- package/dist/types/types/tools.d.ts +28 -0
- package/package.json +1 -1
- package/src/graphs/Graph.ts +63 -5
- package/src/langfuse.ts +17 -2
- package/src/llm/invoke.ts +35 -8
- package/src/stream.ts +70 -1
- package/src/tools/ArtifactDelivery.ts +50 -0
- package/src/tools/BashExecutor.ts +18 -1
- package/src/tools/CodeExecutor.ts +18 -1
- package/src/tools/ProgrammaticToolCalling.ts +15 -1
- package/src/tools/ToolNode.ts +189 -43
- package/src/tools/ToolSearch.ts +278 -28
- package/src/tools/preparedSubagents.ts +228 -0
- package/src/types/graph.ts +8 -0
- package/src/types/tools.ts +29 -0
|
@@ -33,6 +33,10 @@ import {
|
|
|
33
33
|
createCodeApiRunTimeoutSchema,
|
|
34
34
|
resolveCodeApiRunTimeoutMs,
|
|
35
35
|
} from './ptcTimeout';
|
|
36
|
+
import {
|
|
37
|
+
appendArtifactDeliveryWarning,
|
|
38
|
+
normalizeArtifactDeliveryFailure,
|
|
39
|
+
} from '@/tools/ArtifactDelivery';
|
|
36
40
|
import {
|
|
37
41
|
describeCodeApiError,
|
|
38
42
|
logCodeApiDiagnostic,
|
|
@@ -871,12 +875,22 @@ export function formatCompletedResponse(
|
|
|
871
875
|
}
|
|
872
876
|
|
|
873
877
|
const outputWithReminder = appendTmpScratchReminder(formatted, sourceCode);
|
|
878
|
+
const artifactDelivery = normalizeArtifactDeliveryFailure(
|
|
879
|
+
response.artifact_delivery
|
|
880
|
+
);
|
|
881
|
+
const outputWithDeliveryWarning = appendArtifactDeliveryWarning(
|
|
882
|
+
outputWithReminder,
|
|
883
|
+
artifactDelivery
|
|
884
|
+
);
|
|
874
885
|
|
|
875
886
|
return [
|
|
876
|
-
appendCodeSessionFileSummary(
|
|
887
|
+
appendCodeSessionFileSummary(outputWithDeliveryWarning, response.files),
|
|
877
888
|
{
|
|
878
889
|
session_id: response.session_id,
|
|
879
890
|
files: response.files,
|
|
891
|
+
...(artifactDelivery != null
|
|
892
|
+
? { artifact_delivery: artifactDelivery }
|
|
893
|
+
: {}),
|
|
880
894
|
...(response.runtime_session_id != null
|
|
881
895
|
? {
|
|
882
896
|
runtime_session_id: response.runtime_session_id,
|
package/src/tools/ToolNode.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { nanoid } from 'nanoid';
|
|
2
2
|
import { ToolCall } from '@langchain/core/messages/tool';
|
|
3
|
+
import { RunnableLambda } from '@langchain/core/runnables';
|
|
3
4
|
import { AsyncLocalStorageProviderSingleton } from '@langchain/core/singletons';
|
|
4
5
|
import {
|
|
5
6
|
AIMessage,
|
|
@@ -46,8 +47,17 @@ import type {
|
|
|
46
47
|
PostToolBatchEntry,
|
|
47
48
|
ToolApprovalReplayKey,
|
|
48
49
|
} from '@/hooks';
|
|
50
|
+
import type { PreparedSubagents } from '@/tools/preparedSubagents';
|
|
49
51
|
import type { RunBreakerScope } from '@/llm/streamLimits';
|
|
50
52
|
import type * as t from '@/types';
|
|
53
|
+
import {
|
|
54
|
+
type CallerCapabilityProjection,
|
|
55
|
+
createCallerCapabilityProjectionSnapshot,
|
|
56
|
+
isToolDefinitionActive,
|
|
57
|
+
isProgrammaticControlTool,
|
|
58
|
+
mergeCallerCapabilityDefinitions,
|
|
59
|
+
resolveCallerCapabilityProjection,
|
|
60
|
+
} from '@/tools/CallerCapabilities';
|
|
51
61
|
import {
|
|
52
62
|
cloneToolMessageWithContent,
|
|
53
63
|
compactToolContent,
|
|
@@ -57,6 +67,13 @@ import {
|
|
|
57
67
|
serializeStructuredValueBounded,
|
|
58
68
|
serializeToolContentBounded,
|
|
59
69
|
} from '@/utils/toolContent';
|
|
70
|
+
import {
|
|
71
|
+
buildToolExecutionRequestPlan,
|
|
72
|
+
coerceArgsForSchema,
|
|
73
|
+
coerceRecordArgs,
|
|
74
|
+
resolveRuntimeSessionHint,
|
|
75
|
+
recordArgsEqual,
|
|
76
|
+
} from '@/tools/eagerEventExecution';
|
|
60
77
|
import {
|
|
61
78
|
attachSubagentResumeManifest,
|
|
62
79
|
SUBAGENT_PARENT_BATCH_CONFIG_KEY,
|
|
@@ -69,13 +86,6 @@ import {
|
|
|
69
86
|
isIntentLabelProperty,
|
|
70
87
|
outcomeFieldsFromResult,
|
|
71
88
|
} from '@/tools/intentArg';
|
|
72
|
-
import {
|
|
73
|
-
buildToolExecutionRequestPlan,
|
|
74
|
-
coerceArgsForSchema,
|
|
75
|
-
coerceRecordArgs,
|
|
76
|
-
resolveRuntimeSessionHint,
|
|
77
|
-
recordArgsEqual,
|
|
78
|
-
} from '@/tools/eagerEventExecution';
|
|
79
89
|
import {
|
|
80
90
|
resolveLangfuseRuntimeScope,
|
|
81
91
|
withLangfuseRuntimeScope,
|
|
@@ -96,16 +106,9 @@ import {
|
|
|
96
106
|
resolveLocalToolRegistry,
|
|
97
107
|
resolveLocalExecutionTools,
|
|
98
108
|
} from '@/tools/local';
|
|
99
|
-
import {
|
|
100
|
-
type CallerCapabilityProjection,
|
|
101
|
-
createCallerCapabilityProjectionSnapshot,
|
|
102
|
-
isToolDefinitionActive,
|
|
103
|
-
isProgrammaticControlTool,
|
|
104
|
-
mergeCallerCapabilityDefinitions,
|
|
105
|
-
resolveCallerCapabilityProjection,
|
|
106
|
-
} from '@/tools/CallerCapabilities';
|
|
107
109
|
import { stripCodeSessionFileSummary } from '@/tools/CodeSessionFileSummary';
|
|
108
110
|
import { Constants, GraphEvents, CODE_EXECUTION_TOOLS } from '@/common';
|
|
111
|
+
import { PreparedSubagentError } from '@/tools/preparedSubagents';
|
|
109
112
|
import { attachRunStepResumeState } from '@/tools/runStepResume';
|
|
110
113
|
|
|
111
114
|
/** Host-facing batch requests must not carry the batch's breaker scope —
|
|
@@ -793,6 +796,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
793
796
|
private getBreakerSignal?: () => AbortSignal | undefined;
|
|
794
797
|
/** Owning graph's immutable run scope; captured once per batch. */
|
|
795
798
|
private getRunScope?: () => RunBreakerScope;
|
|
799
|
+
private preparedSubagents?: PreparedSubagents;
|
|
796
800
|
/**
|
|
797
801
|
* Monotonic counter used to mint a unique scope id for anonymous
|
|
798
802
|
* batches (ones invoked without a `run_id` in
|
|
@@ -842,6 +846,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
842
846
|
fileCheckpointer,
|
|
843
847
|
getBreakerSignal,
|
|
844
848
|
getRunScope,
|
|
849
|
+
preparedSubagents,
|
|
845
850
|
restoreRunStepResumeState,
|
|
846
851
|
createRunStepResumeState,
|
|
847
852
|
}: t.ToolNodeConstructorParams) {
|
|
@@ -927,6 +932,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
927
932
|
this.toolExecution = toolExecution;
|
|
928
933
|
this.getBreakerSignal = getBreakerSignal;
|
|
929
934
|
this.getRunScope = getRunScope;
|
|
935
|
+
this.preparedSubagents = preparedSubagents;
|
|
930
936
|
// Caller-provided checkpointer wins. Graphs use this to share a
|
|
931
937
|
// single per-Run instance across every ToolNode they compile so
|
|
932
938
|
// `Run.rewindFiles()` reaches the same snapshot store regardless
|
|
@@ -961,6 +967,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
961
967
|
options?: Partial<RunnableConfig>
|
|
962
968
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
963
969
|
): Promise<any> {
|
|
970
|
+
return this.withToolScope(options, (scoped) => super.invoke(input, scoped));
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
private withToolScope<R>(
|
|
974
|
+
options: Partial<RunnableConfig> | undefined,
|
|
975
|
+
invoke: (config: Partial<RunnableConfig> | undefined) => Promise<R>
|
|
976
|
+
): Promise<R> {
|
|
964
977
|
// Explicit agent identity for tool callbacks: node-name parsing is
|
|
965
978
|
// ambiguous when agent ids themselves embed node prefixes, so the
|
|
966
979
|
// handler prefers this metadata (see `isForeignScope`).
|
|
@@ -995,7 +1008,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
995
1008
|
// `LangfuseRuntimeContext.agentId`).
|
|
996
1009
|
agentId: this.executingAgentId,
|
|
997
1010
|
}),
|
|
998
|
-
() =>
|
|
1011
|
+
() => invoke(scopedOptions)
|
|
999
1012
|
);
|
|
1000
1013
|
}
|
|
1001
1014
|
|
|
@@ -1279,6 +1292,146 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1279
1292
|
}
|
|
1280
1293
|
}
|
|
1281
1294
|
|
|
1295
|
+
/**
|
|
1296
|
+
* Forward the graph state as langgraph 1.4's `runtime.state` so
|
|
1297
|
+
* tools can read it off their second argument instead of the
|
|
1298
|
+
* deprecated `getCurrentTaskInput()` (which relies on
|
|
1299
|
+
* `node:async_hooks` and is browser-incompatible). Shape mirrors
|
|
1300
|
+
* langgraph's prebuilt ToolNode runtime exactly.
|
|
1301
|
+
*/
|
|
1302
|
+
private invokeWithRuntime(
|
|
1303
|
+
tool: t.GenericTool,
|
|
1304
|
+
invokeParams: Record<string, unknown>,
|
|
1305
|
+
call: ToolCall,
|
|
1306
|
+
config: RunnableConfig,
|
|
1307
|
+
runInput?: RunToolBatchContext<T>['runInput']
|
|
1308
|
+
): Promise<unknown> {
|
|
1309
|
+
const lgConfig = config as LangGraphRunnableConfig;
|
|
1310
|
+
const runtime: ToolRuntime<T> = {
|
|
1311
|
+
...config,
|
|
1312
|
+
state: runInput as ToolRuntime<T>['state'],
|
|
1313
|
+
toolCallId: call.id ?? '',
|
|
1314
|
+
config,
|
|
1315
|
+
context: lgConfig.context as ToolRuntime<T>['context'],
|
|
1316
|
+
store: (lgConfig.store ?? null) as ToolRuntime<T>['store'],
|
|
1317
|
+
writer:
|
|
1318
|
+
lgConfig.writer ??
|
|
1319
|
+
(config.configurable?.writer as ToolRuntime<T>['writer']) ??
|
|
1320
|
+
null,
|
|
1321
|
+
};
|
|
1322
|
+
this.throwIfBreakerTripped(config);
|
|
1323
|
+
config.signal?.throwIfAborted();
|
|
1324
|
+
return tool.invoke(invokeParams, runtime);
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
/** Only Graph's built-in subagent binding is allowed to call this seam. */
|
|
1328
|
+
prestartSubagent(
|
|
1329
|
+
call: ToolCall,
|
|
1330
|
+
attempt: string,
|
|
1331
|
+
config: RunnableConfig,
|
|
1332
|
+
capacity: number
|
|
1333
|
+
): void {
|
|
1334
|
+
const runId = (config.configurable?.run_id as string | undefined) ?? '';
|
|
1335
|
+
if (
|
|
1336
|
+
call.name !== Constants.SUBAGENT ||
|
|
1337
|
+
call.id == null ||
|
|
1338
|
+
call.args.run_in_background === true ||
|
|
1339
|
+
call.args.subagent_thread_id != null ||
|
|
1340
|
+
this.humanInTheLoop?.enabled === true ||
|
|
1341
|
+
this.hookRegistry?.hasResultAlteringHooks(runId) === true ||
|
|
1342
|
+
this.eagerEventToolExecution?.excludeToolNames?.includes(call.name) ===
|
|
1343
|
+
true
|
|
1344
|
+
) {
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
const tool = this.toolMap.get(call.name);
|
|
1348
|
+
if (tool == null || this.preparedSubagents == null) {
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
const scope = this.getRunScope?.();
|
|
1352
|
+
const capturedConfig = {
|
|
1353
|
+
...config,
|
|
1354
|
+
configurable: {
|
|
1355
|
+
...config.configurable,
|
|
1356
|
+
[RUN_BREAKER_SCOPE_CONFIG_KEY]: scope,
|
|
1357
|
+
[SUBAGENT_PARENT_BATCH_CONFIG_KEY]: attempt,
|
|
1358
|
+
},
|
|
1359
|
+
};
|
|
1360
|
+
const args = JSON.parse(JSON.stringify(call.args)) as ToolCall['args'];
|
|
1361
|
+
const preparedCall = { ...call, args };
|
|
1362
|
+
this.preparedSubagents.start(
|
|
1363
|
+
attempt,
|
|
1364
|
+
this.executingAgentId ?? '',
|
|
1365
|
+
preparedCall,
|
|
1366
|
+
capacity,
|
|
1367
|
+
(signal) => {
|
|
1368
|
+
const turn = this.toolUsageCount.get(call.name) ?? 0;
|
|
1369
|
+
this.toolUsageCount.set(call.name, turn + 1);
|
|
1370
|
+
this.directPathTurns.set(call.id!, turn);
|
|
1371
|
+
// An early call is its own dispatch, before the completed batch exists.
|
|
1372
|
+
// Give it a real chain parent instead of borrowing the model's callback
|
|
1373
|
+
// parent or attempting to reparent already-exported observations later.
|
|
1374
|
+
return this.withToolScope(capturedConfig, async (scopedConfig) => {
|
|
1375
|
+
let output: unknown;
|
|
1376
|
+
let failure: { error: unknown } | undefined;
|
|
1377
|
+
const dispatchOptions = {
|
|
1378
|
+
...scopedConfig,
|
|
1379
|
+
runName: `tools=${this.executingAgentId ?? ''}`,
|
|
1380
|
+
};
|
|
1381
|
+
delete dispatchOptions.runId;
|
|
1382
|
+
delete dispatchOptions.signal;
|
|
1383
|
+
delete dispatchOptions.timeout;
|
|
1384
|
+
// Isolate ambient cancellation too: RunnableLambda races its signal,
|
|
1385
|
+
// but capacity must remain owned until the actual tool settles.
|
|
1386
|
+
await AsyncLocalStorageProviderSingleton.runWithConfig(
|
|
1387
|
+
dispatchOptions,
|
|
1388
|
+
() =>
|
|
1389
|
+
RunnableLambda.from(async (_input, dispatchConfig) => {
|
|
1390
|
+
try {
|
|
1391
|
+
output = await this.invokeWithRuntime(
|
|
1392
|
+
tool,
|
|
1393
|
+
{
|
|
1394
|
+
...preparedCall,
|
|
1395
|
+
type: 'tool_call',
|
|
1396
|
+
turn,
|
|
1397
|
+
stepId: this.toolCallStepIds?.get(call.id!),
|
|
1398
|
+
},
|
|
1399
|
+
preparedCall,
|
|
1400
|
+
{
|
|
1401
|
+
...dispatchConfig,
|
|
1402
|
+
signal: composeAbortSignals(
|
|
1403
|
+
signal,
|
|
1404
|
+
composeAbortSignals(
|
|
1405
|
+
config.signal,
|
|
1406
|
+
scope?.controller.signal
|
|
1407
|
+
)
|
|
1408
|
+
),
|
|
1409
|
+
}
|
|
1410
|
+
);
|
|
1411
|
+
// Raw output belongs to the tool observation, whose redaction policy
|
|
1412
|
+
// knows the tool name. A chain's scalar output has no such identity.
|
|
1413
|
+
return { status: 'completed' };
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
failure = { error };
|
|
1416
|
+
return { status: 'failed' };
|
|
1417
|
+
}
|
|
1418
|
+
}).invoke(
|
|
1419
|
+
{
|
|
1420
|
+
messages: [
|
|
1421
|
+
new AIMessage({ content: '', tool_calls: [preparedCall] }),
|
|
1422
|
+
],
|
|
1423
|
+
},
|
|
1424
|
+
dispatchOptions
|
|
1425
|
+
)
|
|
1426
|
+
);
|
|
1427
|
+
if (failure != null) {
|
|
1428
|
+
throw failure.error;
|
|
1429
|
+
}
|
|
1430
|
+
return output;
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1282
1435
|
/**
|
|
1283
1436
|
* Runs a single tool call with error handling.
|
|
1284
1437
|
*
|
|
@@ -1371,6 +1524,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1371
1524
|
*/
|
|
1372
1525
|
const usageCount =
|
|
1373
1526
|
batchContext.usageCount ??
|
|
1527
|
+
this.directPathTurns.get(call.id ?? '') ??
|
|
1374
1528
|
((): number => {
|
|
1375
1529
|
const next = this.toolUsageCount.get(call.name) ?? 0;
|
|
1376
1530
|
this.toolUsageCount.set(call.name, next + 1);
|
|
@@ -1493,31 +1647,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1493
1647
|
}
|
|
1494
1648
|
}
|
|
1495
1649
|
|
|
1496
|
-
/**
|
|
1497
|
-
* Forward the graph state as langgraph 1.4's `runtime.state` so
|
|
1498
|
-
* tools can read it off their second argument instead of the
|
|
1499
|
-
* deprecated `getCurrentTaskInput()` (which relies on
|
|
1500
|
-
* `node:async_hooks` and is browser-incompatible). Shape mirrors
|
|
1501
|
-
* langgraph's prebuilt ToolNode runtime exactly.
|
|
1502
|
-
*/
|
|
1503
|
-
const lgConfig = config as LangGraphRunnableConfig;
|
|
1504
|
-
const runtime: ToolRuntime<T> = {
|
|
1505
|
-
...config,
|
|
1506
|
-
state: runInput as ToolRuntime<T>['state'],
|
|
1507
|
-
toolCallId: call.id ?? '',
|
|
1508
|
-
config,
|
|
1509
|
-
context: lgConfig.context as ToolRuntime<T>['context'],
|
|
1510
|
-
store: (lgConfig.store ?? null) as ToolRuntime<T>['store'],
|
|
1511
|
-
writer:
|
|
1512
|
-
lgConfig.writer ??
|
|
1513
|
-
(config.configurable?.writer as ToolRuntime<T>['writer']) ??
|
|
1514
|
-
null,
|
|
1515
|
-
};
|
|
1516
|
-
/** A sibling can trip the breaker while the PreToolUse hooks above
|
|
1517
|
-
* are awaited; a tool that never inspects its runtime signal would
|
|
1518
|
-
* still run. Recheck at the last moment before execution. */
|
|
1519
1650
|
this.throwIfBreakerTripped(config);
|
|
1520
|
-
const output = await
|
|
1651
|
+
const output = await (this.preparedSubagents?.take(
|
|
1652
|
+
this.executingAgentId ?? '',
|
|
1653
|
+
{ ...call, args }
|
|
1654
|
+
) ?? this.invokeWithRuntime(tool, invokeParams, call, config, runInput));
|
|
1521
1655
|
if (isCommand(output)) {
|
|
1522
1656
|
return output;
|
|
1523
1657
|
}
|
|
@@ -1633,7 +1767,10 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1633
1767
|
* safety abort, not a tool failure to report back to the model. It
|
|
1634
1768
|
* passes through like an interrupt so the whole run rejects.
|
|
1635
1769
|
*/
|
|
1636
|
-
if (
|
|
1770
|
+
if (
|
|
1771
|
+
e instanceof StreamLimitExceededError ||
|
|
1772
|
+
e instanceof PreparedSubagentError
|
|
1773
|
+
) {
|
|
1637
1774
|
throw e;
|
|
1638
1775
|
}
|
|
1639
1776
|
if (this.errorHandler) {
|
|
@@ -1777,6 +1914,15 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1777
1914
|
config: RunnableConfig,
|
|
1778
1915
|
batchContext: RunToolBatchContext<T> = {}
|
|
1779
1916
|
): Promise<BaseMessage | Command> {
|
|
1917
|
+
if (
|
|
1918
|
+
this.preparedSubagents?.owns(this.executingAgentId ?? '', call) === true &&
|
|
1919
|
+
this.hookRegistry?.hasResultAlteringHooks(
|
|
1920
|
+
(config.configurable?.run_id as string | undefined) ?? ''
|
|
1921
|
+
) === true
|
|
1922
|
+
) {
|
|
1923
|
+
this.preparedSubagents.clear();
|
|
1924
|
+
throw new PreparedSubagentError('Tool hooks changed after a subagent started.');
|
|
1925
|
+
}
|
|
1780
1926
|
const replayController = (
|
|
1781
1927
|
this.toolMap.get(call.name) as ReplayableSubagentTool | undefined
|
|
1782
1928
|
)?.[SUBAGENT_REPLAY_CONTROLLER];
|
|
@@ -2735,7 +2881,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2735
2881
|
const signal = config.signal;
|
|
2736
2882
|
if (
|
|
2737
2883
|
signal?.aborted === true &&
|
|
2738
|
-
signal.reason instanceof StreamLimitExceededError
|
|
2884
|
+
(signal.reason instanceof StreamLimitExceededError || signal.reason instanceof PreparedSubagentError)
|
|
2739
2885
|
) {
|
|
2740
2886
|
throw signal.reason;
|
|
2741
2887
|
}
|
|
@@ -4435,7 +4581,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4435
4581
|
* otherwise perform side effects on a failed run. */
|
|
4436
4582
|
if (
|
|
4437
4583
|
composedSignal?.aborted === true &&
|
|
4438
|
-
composedSignal.reason instanceof StreamLimitExceededError
|
|
4584
|
+
(composedSignal.reason instanceof StreamLimitExceededError || composedSignal.reason instanceof PreparedSubagentError)
|
|
4439
4585
|
) {
|
|
4440
4586
|
throw composedSignal.reason;
|
|
4441
4587
|
}
|