@librechat/agents 4.0.0 → 4.0.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/decisions/deadline.cjs +52 -0
- package/dist/cjs/decisions/deadline.cjs.map +1 -0
- package/dist/cjs/decisions/dialect.cjs +88 -0
- package/dist/cjs/decisions/dialect.cjs.map +1 -0
- package/dist/cjs/decisions/http.cjs +77 -0
- package/dist/cjs/decisions/http.cjs.map +1 -0
- package/dist/cjs/decisions/index.cjs +7 -0
- package/dist/cjs/decisions/presets.cjs +82 -0
- package/dist/cjs/decisions/presets.cjs.map +1 -0
- package/dist/cjs/decisions/questions.cjs +70 -0
- package/dist/cjs/decisions/questions.cjs.map +1 -0
- package/dist/cjs/decisions/structuredChat.cjs +178 -0
- package/dist/cjs/decisions/structuredChat.cjs.map +1 -0
- package/dist/cjs/decisions/traceMarker.cjs +6 -0
- package/dist/cjs/decisions/traceMarker.cjs.map +1 -0
- package/dist/cjs/decisions/transport.cjs +166 -0
- package/dist/cjs/decisions/transport.cjs.map +1 -0
- package/dist/cjs/decisions/types.cjs +44 -0
- package/dist/cjs/decisions/types.cjs.map +1 -0
- package/dist/cjs/graphs/Graph.cjs +4 -1
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/langfuseToolOutputTracing.cjs +6 -1
- package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
- package/dist/cjs/main.cjs +39 -0
- package/dist/cjs/rerank/index.cjs +2 -0
- package/dist/cjs/rerank/search.cjs +45 -0
- package/dist/cjs/rerank/search.cjs.map +1 -0
- package/dist/cjs/rerank/systemone.cjs +70 -0
- package/dist/cjs/rerank/systemone.cjs.map +1 -0
- package/dist/cjs/run.cjs +4 -0
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +1 -1
- package/dist/cjs/tools/search/rerankers.cjs +1 -0
- package/dist/cjs/tools/search/search.cjs +3 -3
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +4 -3
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs +12 -0
- package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +87 -11
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/subagent/diagnostics.cjs +73 -0
- package/dist/cjs/tools/subagent/diagnostics.cjs.map +1 -0
- package/dist/cjs/tools/subagent/index.cjs +1 -0
- package/dist/esm/decisions/deadline.mjs +52 -0
- package/dist/esm/decisions/deadline.mjs.map +1 -0
- package/dist/esm/decisions/dialect.mjs +87 -0
- package/dist/esm/decisions/dialect.mjs.map +1 -0
- package/dist/esm/decisions/http.mjs +75 -0
- package/dist/esm/decisions/http.mjs.map +1 -0
- package/dist/esm/decisions/index.mjs +8 -0
- package/dist/esm/decisions/presets.mjs +77 -0
- package/dist/esm/decisions/presets.mjs.map +1 -0
- package/dist/esm/decisions/questions.mjs +67 -0
- package/dist/esm/decisions/questions.mjs.map +1 -0
- package/dist/esm/decisions/structuredChat.mjs +178 -0
- package/dist/esm/decisions/structuredChat.mjs.map +1 -0
- package/dist/esm/decisions/traceMarker.mjs +6 -0
- package/dist/esm/decisions/traceMarker.mjs.map +1 -0
- package/dist/esm/decisions/transport.mjs +164 -0
- package/dist/esm/decisions/transport.mjs.map +1 -0
- package/dist/esm/decisions/types.mjs +39 -0
- package/dist/esm/decisions/types.mjs.map +1 -0
- package/dist/esm/graphs/Graph.mjs +4 -1
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/langfuseToolOutputTracing.mjs +6 -1
- package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
- package/dist/esm/main.mjs +13 -1
- package/dist/esm/rerank/index.mjs +3 -0
- package/dist/esm/rerank/search.mjs +45 -0
- package/dist/esm/rerank/search.mjs.map +1 -0
- package/dist/esm/rerank/systemone.mjs +70 -0
- package/dist/esm/rerank/systemone.mjs.map +1 -0
- package/dist/esm/run.mjs +4 -0
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +1 -1
- package/dist/esm/tools/search/rerankers.mjs +1 -1
- package/dist/esm/tools/search/search.mjs +3 -3
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +4 -3
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs +12 -1
- package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +88 -12
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/subagent/diagnostics.mjs +71 -0
- package/dist/esm/tools/subagent/diagnostics.mjs.map +1 -0
- package/dist/esm/tools/subagent/index.mjs +1 -0
- package/dist/types/decisions/deadline.d.ts +3 -0
- package/dist/types/decisions/dialect.d.ts +11 -0
- package/dist/types/decisions/http.d.ts +22 -0
- package/dist/types/decisions/index.d.ts +10 -0
- package/dist/types/decisions/presets.d.ts +17 -0
- package/dist/types/decisions/questions.d.ts +8 -0
- package/dist/types/decisions/structuredChat.d.ts +16 -0
- package/dist/types/decisions/traceMarker.d.ts +2 -0
- package/dist/types/decisions/transport.d.ts +29 -0
- package/dist/types/decisions/types.d.ts +119 -0
- package/dist/types/graphs/Graph.d.ts +2 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/rerank/index.d.ts +4 -0
- package/dist/types/rerank/search.d.ts +7 -0
- package/dist/types/rerank/systemone.d.ts +13 -0
- package/dist/types/rerank/types.d.ts +24 -0
- package/dist/types/run.d.ts +1 -0
- package/dist/types/tools/search/types.d.ts +3 -1
- package/dist/types/tools/subagent/SubagentExecutionRegistry.d.ts +4 -0
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +13 -0
- package/dist/types/tools/subagent/diagnostics.d.ts +44 -0
- package/dist/types/tools/subagent/index.d.ts +2 -0
- package/dist/types/types/graph.d.ts +3 -0
- package/dist/types/types/run.d.ts +2 -0
- package/package.json +3 -3
- package/src/decisions/deadline.ts +92 -0
- package/src/decisions/dialect.ts +200 -0
- package/src/decisions/http.ts +175 -0
- package/src/decisions/index.ts +14 -0
- package/src/decisions/presets.ts +111 -0
- package/src/decisions/questions.ts +128 -0
- package/src/decisions/structuredChat.ts +364 -0
- package/src/decisions/traceMarker.ts +2 -0
- package/src/decisions/transport.ts +335 -0
- package/src/decisions/types.ts +193 -0
- package/src/graphs/Graph.ts +4 -0
- package/src/index.ts +2 -0
- package/src/langfuseToolOutputTracing.ts +8 -1
- package/src/rerank/index.ts +4 -0
- package/src/rerank/search.ts +85 -0
- package/src/rerank/systemone.ts +140 -0
- package/src/rerank/types.ts +28 -0
- package/src/run.ts +4 -0
- package/src/tools/search/search.ts +4 -4
- package/src/tools/search/tool.ts +27 -25
- package/src/tools/search/types.ts +4 -1
- package/src/tools/subagent/SubagentExecutionRegistry.ts +21 -0
- package/src/tools/subagent/SubagentExecutor.ts +224 -36
- package/src/tools/subagent/diagnostics.ts +142 -0
- package/src/tools/subagent/index.ts +11 -0
- package/src/types/graph.ts +3 -0
- package/src/types/run.ts +2 -0
|
@@ -425,6 +425,7 @@ export class SubagentExecutionRecord<
|
|
|
425
425
|
TSettledOutput = never,
|
|
426
426
|
> {
|
|
427
427
|
private identityValue?: SubagentExecutionIdentity;
|
|
428
|
+
private attemptedIdentityValue?: SubagentExecutionIdentity;
|
|
428
429
|
private bindingValue?: SubagentDefinitionBinding;
|
|
429
430
|
private bindingAuthority?: 'provisional' | 'effective';
|
|
430
431
|
private invocationValue?: SubagentInvocationBinding;
|
|
@@ -488,6 +489,24 @@ export class SubagentExecutionRecord<
|
|
|
488
489
|
return this.identityValue;
|
|
489
490
|
}
|
|
490
491
|
|
|
492
|
+
/** Correlation only; never committed identity or resume authority. */
|
|
493
|
+
get attemptedIdentity(): SubagentExecutionIdentity | undefined {
|
|
494
|
+
return this.attemptedIdentityValue;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
recordIdentityAttempt(identity: SubagentExecutionIdentity): void {
|
|
498
|
+
const current = this.attemptedIdentityValue;
|
|
499
|
+
if (
|
|
500
|
+
current != null &&
|
|
501
|
+
current.childRunId === identity.childRunId &&
|
|
502
|
+
current.childThreadId === identity.childThreadId &&
|
|
503
|
+
current.approvalExecutionScope === identity.approvalExecutionScope
|
|
504
|
+
) {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
this.attemptedIdentityValue = Object.freeze({ ...identity });
|
|
508
|
+
}
|
|
509
|
+
|
|
491
510
|
get binding(): SubagentDefinitionBinding | undefined {
|
|
492
511
|
return this.bindingValue;
|
|
493
512
|
}
|
|
@@ -570,6 +589,7 @@ export class SubagentExecutionRecord<
|
|
|
570
589
|
if (this.pendingIdentityResolution != null) {
|
|
571
590
|
return this.pendingIdentityResolution;
|
|
572
591
|
}
|
|
592
|
+
this.attemptedIdentityValue = undefined;
|
|
573
593
|
const pending = Promise.resolve()
|
|
574
594
|
.then(() => {
|
|
575
595
|
this.assertCurrentIdentityResolution(pending);
|
|
@@ -929,6 +949,7 @@ export class SubagentExecutionRegistry<
|
|
|
929
949
|
if (this.recordsByAddress.get(record.address.key) !== record) {
|
|
930
950
|
throw new SubagentExecutionInvalidatedError();
|
|
931
951
|
}
|
|
952
|
+
record.recordIdentityAttempt(identity);
|
|
932
953
|
const checkpointKey = getPreparationResourceKey(
|
|
933
954
|
'checkpoint',
|
|
934
955
|
identity.childThreadId
|
|
@@ -92,6 +92,12 @@ import type {
|
|
|
92
92
|
PreemptBoundaryHookOutput,
|
|
93
93
|
ToolApprovalReplaySnapshot,
|
|
94
94
|
} from '@/hooks';
|
|
95
|
+
import type {
|
|
96
|
+
SubagentResolutionPhase,
|
|
97
|
+
SubagentResolutionCause,
|
|
98
|
+
SubagentResolutionFailureHandler,
|
|
99
|
+
} from './diagnostics';
|
|
100
|
+
import type { RunBreakerScope } from '@/llm/streamLimits';
|
|
95
101
|
import type { GraphFactory } from '@/graphs/graphFactory';
|
|
96
102
|
import type { StandardGraph } from '@/graphs/Graph';
|
|
97
103
|
import {
|
|
@@ -126,6 +132,10 @@ import {
|
|
|
126
132
|
DEFAULT_SUBAGENT_MAX_TURNS,
|
|
127
133
|
SUBAGENT_RECURSION_MULTIPLIER,
|
|
128
134
|
} from './runtimeLimits';
|
|
135
|
+
import {
|
|
136
|
+
logSubagentResolutionFailure,
|
|
137
|
+
SubagentResolutionError,
|
|
138
|
+
} from './diagnostics';
|
|
129
139
|
import {
|
|
130
140
|
ContentTypes,
|
|
131
141
|
Constants,
|
|
@@ -161,13 +171,19 @@ const MAX_QUEUED_SUBAGENT_UPDATES = 64;
|
|
|
161
171
|
const MAX_BACKGROUND_SUBAGENT_SEALS = 32;
|
|
162
172
|
const SUBAGENT_UPDATE_HANDLER_TIMEOUT_MS = 5_000;
|
|
163
173
|
const TEXT_DELTA_CONTENT_TYPE = `${ContentTypes.TEXT}_delta`;
|
|
164
|
-
const SUBAGENT_RESOLUTION_ERROR_MESSAGE =
|
|
165
|
-
'Subagent error: Unable to initialize the selected subagent.';
|
|
166
174
|
const SUBAGENT_CONFIG_CHANGED_MESSAGE =
|
|
167
175
|
'Subagent error: Subagent configuration changed since this execution was paused.';
|
|
168
176
|
const SUBAGENT_INVOCATION_CHANGED_MESSAGE =
|
|
169
177
|
'Subagent error: Subagent invocation changed for this execution.';
|
|
170
178
|
|
|
179
|
+
function isSubagentResolutionControlFlow(error: unknown): boolean {
|
|
180
|
+
try {
|
|
181
|
+
return error instanceof StreamLimitExceededError || isGraphInterrupt(error);
|
|
182
|
+
} catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
171
187
|
function seedChildGraphSessions(
|
|
172
188
|
childGraph: StandardGraph,
|
|
173
189
|
agents: AgentInputs[]
|
|
@@ -866,6 +882,11 @@ export type SubagentExecuteParams = {
|
|
|
866
882
|
export type SubagentExecuteResult = SubagentContextResult & {
|
|
867
883
|
/** Tagged internal failure; foreground callers retain the legacy content. */
|
|
868
884
|
error?: string;
|
|
885
|
+
/** Safe startup classification retained for detached host delivery. */
|
|
886
|
+
resolutionFailure?: {
|
|
887
|
+
phase: SubagentResolutionPhase;
|
|
888
|
+
cause: SubagentResolutionCause;
|
|
889
|
+
};
|
|
869
890
|
/** Completed child work whose host projection must be retried without re-execution. */
|
|
870
891
|
retryableDelivery?: true;
|
|
871
892
|
};
|
|
@@ -985,6 +1006,8 @@ export type SubagentExecutorOptions = {
|
|
|
985
1006
|
/** Host-owned process-local task namespace for detached execution. */
|
|
986
1007
|
taskConfig?: SubagentTaskConfig;
|
|
987
1008
|
subagentContext?: SubagentContextAdapter;
|
|
1009
|
+
/** Host diagnostic sink and safe cause classifier for startup failures. */
|
|
1010
|
+
onResolutionFailure?: SubagentResolutionFailureHandler;
|
|
988
1011
|
};
|
|
989
1012
|
|
|
990
1013
|
type DurableExecutionRecord = SubagentExecutionRecord<
|
|
@@ -1023,6 +1046,7 @@ export class SubagentExecutor {
|
|
|
1023
1046
|
private readonly usageSink?: SubagentUsageSink;
|
|
1024
1047
|
private readonly taskConfig?: SubagentTaskConfig;
|
|
1025
1048
|
private readonly subagentContext?: SubagentContextAdapter;
|
|
1049
|
+
private readonly onResolutionFailure?: SubagentResolutionFailureHandler;
|
|
1026
1050
|
private readonly executions: SubagentExecutionRegistry<
|
|
1027
1051
|
SubagentExecuteResult,
|
|
1028
1052
|
ResolvedSubagentConfig,
|
|
@@ -1068,6 +1092,7 @@ export class SubagentExecutor {
|
|
|
1068
1092
|
this.usageSink = options.usageSink;
|
|
1069
1093
|
this.taskConfig = options.taskConfig;
|
|
1070
1094
|
this.subagentContext = options.subagentContext;
|
|
1095
|
+
this.onResolutionFailure = options.onResolutionFailure;
|
|
1071
1096
|
const rawRegistry = options.parentHandlerRegistry;
|
|
1072
1097
|
if (typeof rawRegistry === 'function') {
|
|
1073
1098
|
this.resolveParentHandlerRegistry = rawRegistry;
|
|
@@ -1317,6 +1342,7 @@ export class SubagentExecutor {
|
|
|
1317
1342
|
tokenCounter: this.tokenCounter,
|
|
1318
1343
|
usageSink: this.usageSink,
|
|
1319
1344
|
subagentContext: this.subagentContext,
|
|
1345
|
+
onResolutionFailure: this.onResolutionFailure,
|
|
1320
1346
|
streamLimits: this.streamLimits,
|
|
1321
1347
|
humanInTheLoop:
|
|
1322
1348
|
this.humanInTheLoop?.enabled === true ||
|
|
@@ -1358,10 +1384,7 @@ export class SubagentExecutor {
|
|
|
1358
1384
|
};
|
|
1359
1385
|
let deliveryAttempts = 1;
|
|
1360
1386
|
let result = await executeAttempt();
|
|
1361
|
-
while (
|
|
1362
|
-
result.retryableDelivery === true &&
|
|
1363
|
-
deliveryAttempts < 3
|
|
1364
|
-
) {
|
|
1387
|
+
while (result.retryableDelivery === true && deliveryAttempts < 3) {
|
|
1365
1388
|
if (deliveryAttempts > 1) {
|
|
1366
1389
|
await sleep(
|
|
1367
1390
|
Math.min(100 * 2 ** Math.min(deliveryAttempts - 2, 6), 5_000)
|
|
@@ -1371,6 +1394,12 @@ export class SubagentExecutor {
|
|
|
1371
1394
|
result = await executeAttempt();
|
|
1372
1395
|
}
|
|
1373
1396
|
if (result.error != null) {
|
|
1397
|
+
if (result.resolutionFailure != null) {
|
|
1398
|
+
throw new SubagentResolutionError(
|
|
1399
|
+
result.resolutionFailure.phase,
|
|
1400
|
+
result.resolutionFailure.cause
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1374
1403
|
throw new Error(result.error);
|
|
1375
1404
|
}
|
|
1376
1405
|
if (this.humanInTheLoop?.enabled === true) {
|
|
@@ -1664,13 +1693,6 @@ export class SubagentExecutor {
|
|
|
1664
1693
|
),
|
|
1665
1694
|
});
|
|
1666
1695
|
}
|
|
1667
|
-
const sourceCheckpoints =
|
|
1668
|
-
await this.getLatestCheckpointSnapshot(baseChildThreadId);
|
|
1669
|
-
if (sourceCheckpoints.length === 0) {
|
|
1670
|
-
throw new Error(
|
|
1671
|
-
`Cannot fork subagent checkpoint thread "${baseChildThreadId}" without a checkpoint ID.`
|
|
1672
|
-
);
|
|
1673
|
-
}
|
|
1674
1696
|
const childRunId = persistedChildRunId ?? currentChildRunId;
|
|
1675
1697
|
const identity = {
|
|
1676
1698
|
childRunId,
|
|
@@ -1680,6 +1702,14 @@ export class SubagentExecutor {
|
|
|
1680
1702
|
resumeAttemptId
|
|
1681
1703
|
),
|
|
1682
1704
|
};
|
|
1705
|
+
execution.recordIdentityAttempt(identity);
|
|
1706
|
+
const sourceCheckpoints =
|
|
1707
|
+
await this.getLatestCheckpointSnapshot(baseChildThreadId);
|
|
1708
|
+
if (sourceCheckpoints.length === 0) {
|
|
1709
|
+
throw new Error(
|
|
1710
|
+
`Cannot fork subagent checkpoint thread "${baseChildThreadId}" without a checkpoint ID.`
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1683
1713
|
const lease = this.executions.beginIdentityPreparation(execution, identity);
|
|
1684
1714
|
await this.prepareCheckpointFork(
|
|
1685
1715
|
sourceCheckpoints,
|
|
@@ -2085,9 +2115,10 @@ export class SubagentExecutor {
|
|
|
2085
2115
|
config: RunnableConfig
|
|
2086
2116
|
): Promise<SettledSubagentToolOutput | undefined> {
|
|
2087
2117
|
const parentToolCallId = call.id;
|
|
2118
|
+
const checkpointer = this.checkpointer;
|
|
2088
2119
|
if (
|
|
2089
2120
|
this.humanInTheLoop?.enabled !== true ||
|
|
2090
|
-
|
|
2121
|
+
checkpointer == null ||
|
|
2091
2122
|
parentToolCallId == null ||
|
|
2092
2123
|
parentToolCallId === ''
|
|
2093
2124
|
) {
|
|
@@ -2102,6 +2133,44 @@ export class SubagentExecutor {
|
|
|
2102
2133
|
parentToolCallId,
|
|
2103
2134
|
parentConfigurable,
|
|
2104
2135
|
});
|
|
2136
|
+
const signal = this.getReplaySignal(config);
|
|
2137
|
+
try {
|
|
2138
|
+
return await this.restoreSettledToolOutput(
|
|
2139
|
+
call,
|
|
2140
|
+
parentConfigurable,
|
|
2141
|
+
parentToolCallId,
|
|
2142
|
+
execution,
|
|
2143
|
+
checkpointer
|
|
2144
|
+
);
|
|
2145
|
+
} catch (error) {
|
|
2146
|
+
if (isSubagentResolutionControlFlow(error)) throw error;
|
|
2147
|
+
const failure = this.createReplayResolutionError(
|
|
2148
|
+
call,
|
|
2149
|
+
config,
|
|
2150
|
+
execution,
|
|
2151
|
+
signal,
|
|
2152
|
+
error
|
|
2153
|
+
);
|
|
2154
|
+
if (signal.aborted) throw failure;
|
|
2155
|
+
return {
|
|
2156
|
+
output: new ToolMessage({
|
|
2157
|
+
content: failure.message,
|
|
2158
|
+
name: call.name,
|
|
2159
|
+
tool_call_id: parentToolCallId,
|
|
2160
|
+
status: 'error',
|
|
2161
|
+
}),
|
|
2162
|
+
additionalContexts: [],
|
|
2163
|
+
};
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
private async restoreSettledToolOutput(
|
|
2168
|
+
call: ToolCall,
|
|
2169
|
+
parentConfigurable: Record<string, unknown> | undefined,
|
|
2170
|
+
parentToolCallId: string,
|
|
2171
|
+
execution: DurableExecutionRecord,
|
|
2172
|
+
checkpointer: BaseCheckpointSaver
|
|
2173
|
+
): Promise<SettledSubagentToolOutput | undefined> {
|
|
2105
2174
|
const { resumeExecution } = execution;
|
|
2106
2175
|
const inProcessSettledOutput = execution.settledOutput;
|
|
2107
2176
|
if (inProcessSettledOutput != null) {
|
|
@@ -2160,7 +2229,7 @@ export class SubagentExecutor {
|
|
|
2160
2229
|
const { childThreadId } =
|
|
2161
2230
|
await this.resolveChildExecutionIdentity(execution);
|
|
2162
2231
|
if (marker == null) {
|
|
2163
|
-
const checkpoint = await
|
|
2232
|
+
const checkpoint = await checkpointer.getTuple({
|
|
2164
2233
|
configurable: { thread_id: childThreadId },
|
|
2165
2234
|
});
|
|
2166
2235
|
messages = getCheckpointMessages(
|
|
@@ -2245,7 +2314,10 @@ export class SubagentExecutor {
|
|
|
2245
2314
|
parentToolCallId,
|
|
2246
2315
|
parentConfigurable,
|
|
2247
2316
|
});
|
|
2248
|
-
if (
|
|
2317
|
+
if (
|
|
2318
|
+
execution.completedResult != null &&
|
|
2319
|
+
settled.output.status === 'error'
|
|
2320
|
+
) {
|
|
2249
2321
|
return;
|
|
2250
2322
|
}
|
|
2251
2323
|
const { resumeExecution } = execution;
|
|
@@ -2309,8 +2381,20 @@ export class SubagentExecutor {
|
|
|
2309
2381
|
},
|
|
2310
2382
|
persistedOutput,
|
|
2311
2383
|
async (): Promise<void> => {
|
|
2312
|
-
|
|
2313
|
-
|
|
2384
|
+
let identity: SubagentExecutionIdentity;
|
|
2385
|
+
try {
|
|
2386
|
+
identity = await this.resolveChildExecutionIdentity(execution);
|
|
2387
|
+
} catch (error) {
|
|
2388
|
+
if (isSubagentResolutionControlFlow(error)) throw error;
|
|
2389
|
+
throw this.createReplayResolutionError(
|
|
2390
|
+
call,
|
|
2391
|
+
config,
|
|
2392
|
+
execution,
|
|
2393
|
+
this.getReplaySignal(config),
|
|
2394
|
+
error
|
|
2395
|
+
);
|
|
2396
|
+
}
|
|
2397
|
+
const { childRunId, childThreadId } = identity;
|
|
2314
2398
|
const activeChildRun = execution.activeRun;
|
|
2315
2399
|
if (activeChildRun != null) {
|
|
2316
2400
|
await this.persistChildCheckpointMarker(
|
|
@@ -2456,6 +2540,90 @@ export class SubagentExecutor {
|
|
|
2456
2540
|
}
|
|
2457
2541
|
}
|
|
2458
2542
|
|
|
2543
|
+
private getReplaySignal(config: RunnableConfig): AbortSignal {
|
|
2544
|
+
const scope = config.configurable?.[RUN_BREAKER_SCOPE_CONFIG_KEY] as
|
|
2545
|
+
| RunBreakerScope
|
|
2546
|
+
| undefined;
|
|
2547
|
+
return this.composeChildSignal(
|
|
2548
|
+
scope?.controller ?? this.resolveBreakerController(),
|
|
2549
|
+
config.signal
|
|
2550
|
+
);
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
private createReplayResolutionError(
|
|
2554
|
+
call: ToolCall,
|
|
2555
|
+
config: RunnableConfig,
|
|
2556
|
+
execution: DurableExecutionRecord,
|
|
2557
|
+
signal: AbortSignal,
|
|
2558
|
+
error: unknown
|
|
2559
|
+
): SubagentResolutionError {
|
|
2560
|
+
const selectedType =
|
|
2561
|
+
execution.binding?.subagentType ??
|
|
2562
|
+
execution.resumeExecution?.subagentType ??
|
|
2563
|
+
getSubagentType(call);
|
|
2564
|
+
const threadId = config.configurable?.thread_id;
|
|
2565
|
+
const failure = this.createResolutionFailure(
|
|
2566
|
+
'identity',
|
|
2567
|
+
{
|
|
2568
|
+
subagentType:
|
|
2569
|
+
selectedType == null
|
|
2570
|
+
? 'unknown'
|
|
2571
|
+
: (this.configs.get(selectedType)?.type ?? 'unknown'),
|
|
2572
|
+
description: DEFAULT_SUBAGENT_DESCRIPTION,
|
|
2573
|
+
parentToolCallId: call.id,
|
|
2574
|
+
threadId: typeof threadId === 'string' ? threadId : undefined,
|
|
2575
|
+
},
|
|
2576
|
+
execution,
|
|
2577
|
+
signal,
|
|
2578
|
+
error
|
|
2579
|
+
);
|
|
2580
|
+
return new SubagentResolutionError(
|
|
2581
|
+
'identity',
|
|
2582
|
+
failure.resolutionFailure?.cause ?? 'unknown'
|
|
2583
|
+
);
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2586
|
+
private createResolutionFailure(
|
|
2587
|
+
phase: SubagentResolutionPhase,
|
|
2588
|
+
params: SubagentExecuteParams,
|
|
2589
|
+
execution: DurableExecutionRecord,
|
|
2590
|
+
signal: AbortSignal,
|
|
2591
|
+
error: unknown
|
|
2592
|
+
): SubagentExecuteResult {
|
|
2593
|
+
const identity = execution.identity ?? execution.attemptedIdentity;
|
|
2594
|
+
const resumeExecution =
|
|
2595
|
+
this.humanInTheLoop?.enabled === true && this.checkpointer != null
|
|
2596
|
+
? execution.resumeExecution
|
|
2597
|
+
: undefined;
|
|
2598
|
+
const detail = logSubagentResolutionFailure(
|
|
2599
|
+
phase,
|
|
2600
|
+
params.subagentType,
|
|
2601
|
+
signal,
|
|
2602
|
+
error,
|
|
2603
|
+
{
|
|
2604
|
+
parentRunId: this.parentRunId,
|
|
2605
|
+
parentAgentId: this.parentAgentId,
|
|
2606
|
+
parentToolCallId: params.parentToolCallId,
|
|
2607
|
+
threadId: params.threadId,
|
|
2608
|
+
childRunId:
|
|
2609
|
+
identity?.childRunId ??
|
|
2610
|
+
resumeExecution?.childRunId ??
|
|
2611
|
+
execution.address.currentChildRunId,
|
|
2612
|
+
childThreadId:
|
|
2613
|
+
identity?.childThreadId ??
|
|
2614
|
+
(resumeExecution == null
|
|
2615
|
+
? execution.address.baseChildThreadId
|
|
2616
|
+
: execution.address.branchChildThreadId),
|
|
2617
|
+
taskId: params.taskRuntime?.taskId,
|
|
2618
|
+
},
|
|
2619
|
+
this.onResolutionFailure
|
|
2620
|
+
);
|
|
2621
|
+
return {
|
|
2622
|
+
...createSubagentFailure(detail.message),
|
|
2623
|
+
resolutionFailure: { phase, cause: detail.cause },
|
|
2624
|
+
};
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2459
2627
|
private async executeOnce(
|
|
2460
2628
|
params: SubagentExecuteParams,
|
|
2461
2629
|
execution: DurableExecutionRecord,
|
|
@@ -2489,10 +2657,16 @@ export class SubagentExecutor {
|
|
|
2489
2657
|
identity = await this.resolveChildExecutionIdentity(execution);
|
|
2490
2658
|
execution.assertUsable(childSignal);
|
|
2491
2659
|
} catch (error) {
|
|
2492
|
-
if (error
|
|
2660
|
+
if (isSubagentResolutionControlFlow(error)) {
|
|
2493
2661
|
throw error;
|
|
2494
2662
|
}
|
|
2495
|
-
return
|
|
2663
|
+
return this.createResolutionFailure(
|
|
2664
|
+
'identity',
|
|
2665
|
+
params,
|
|
2666
|
+
execution,
|
|
2667
|
+
childSignal,
|
|
2668
|
+
error
|
|
2669
|
+
);
|
|
2496
2670
|
}
|
|
2497
2671
|
const { childRunId, childThreadId, approvalExecutionScope } = identity;
|
|
2498
2672
|
const bound = this.bindExecutionDefinition(
|
|
@@ -2519,10 +2693,16 @@ export class SubagentExecutor {
|
|
|
2519
2693
|
});
|
|
2520
2694
|
execution.assertUsable(childSignal);
|
|
2521
2695
|
} catch (error) {
|
|
2522
|
-
if (error
|
|
2696
|
+
if (isSubagentResolutionControlFlow(error)) {
|
|
2523
2697
|
throw error;
|
|
2524
2698
|
}
|
|
2525
|
-
return
|
|
2699
|
+
return this.createResolutionFailure(
|
|
2700
|
+
'config',
|
|
2701
|
+
params,
|
|
2702
|
+
execution,
|
|
2703
|
+
childSignal,
|
|
2704
|
+
error
|
|
2705
|
+
);
|
|
2526
2706
|
}
|
|
2527
2707
|
|
|
2528
2708
|
const parentRegistry = this.getParentHandlerRegistry();
|
|
@@ -2609,9 +2789,7 @@ export class SubagentExecutor {
|
|
|
2609
2789
|
}
|
|
2610
2790
|
} catch (error) {
|
|
2611
2791
|
if (childSignal.aborted) {
|
|
2612
|
-
throw childSignal.reason instanceof Error
|
|
2613
|
-
? childSignal.reason
|
|
2614
|
-
: error;
|
|
2792
|
+
throw childSignal.reason instanceof Error ? childSignal.reason : error;
|
|
2615
2793
|
}
|
|
2616
2794
|
if (execution.completedResult != null) {
|
|
2617
2795
|
return {
|
|
@@ -2656,6 +2834,7 @@ export class SubagentExecutor {
|
|
|
2656
2834
|
subagentScope: true,
|
|
2657
2835
|
subagentExecutionContext: childExecutionContext,
|
|
2658
2836
|
subagentContext: this.subagentContext,
|
|
2837
|
+
onSubagentResolutionFailure: this.onResolutionFailure,
|
|
2659
2838
|
...(resumeExecution?.graphState.fadingTier == null
|
|
2660
2839
|
? {}
|
|
2661
2840
|
: { fadingTier: resumeExecution.graphState.fadingTier }),
|
|
@@ -2958,10 +3137,11 @@ export class SubagentExecutor {
|
|
|
2958
3137
|
},
|
|
2959
3138
|
sessionId: currentHookSessionId,
|
|
2960
3139
|
matchQuery: subagentType,
|
|
2961
|
-
}).catch(
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
3140
|
+
}).catch(
|
|
3141
|
+
(): AggregatedHookResult =>
|
|
3142
|
+
isBackgroundDenyMode(this.humanInTheLoop)
|
|
3143
|
+
? { ...HOOK_FALLBACK, hasHookFailures: true }
|
|
3144
|
+
: HOOK_FALLBACK
|
|
2965
3145
|
);
|
|
2966
3146
|
|
|
2967
3147
|
const policyFailed =
|
|
@@ -3823,7 +4003,10 @@ export function sanitizeForwardedSubagentUpdateData(
|
|
|
3823
4003
|
if (eventName === GraphEvents.ON_TOOL_CALLS_DISPATCHED) {
|
|
3824
4004
|
if (!isObjectLike(data)) return undefined;
|
|
3825
4005
|
const event = data as { dispatched_at?: number; toolCalls?: unknown };
|
|
3826
|
-
if (
|
|
4006
|
+
if (
|
|
4007
|
+
!Array.isArray(event.toolCalls) ||
|
|
4008
|
+
typeof event.dispatched_at !== 'number'
|
|
4009
|
+
) {
|
|
3827
4010
|
return undefined;
|
|
3828
4011
|
}
|
|
3829
4012
|
return {
|
|
@@ -3831,12 +4014,17 @@ export function sanitizeForwardedSubagentUpdateData(
|
|
|
3831
4014
|
toolCalls: (event.toolCalls as unknown[]).flatMap((call) => {
|
|
3832
4015
|
if (!isObjectLike(call)) return [];
|
|
3833
4016
|
const entry = call as Record<string, unknown>;
|
|
3834
|
-
if (typeof entry.id !== 'string' || typeof entry.name !== 'string')
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
4017
|
+
if (typeof entry.id !== 'string' || typeof entry.name !== 'string')
|
|
4018
|
+
return [];
|
|
4019
|
+
return [
|
|
4020
|
+
{
|
|
4021
|
+
id: entry.id,
|
|
4022
|
+
name: entry.name,
|
|
4023
|
+
...(typeof entry.stepId === 'string'
|
|
4024
|
+
? { stepId: entry.stepId }
|
|
4025
|
+
: {}),
|
|
4026
|
+
},
|
|
4027
|
+
];
|
|
3840
4028
|
}),
|
|
3841
4029
|
};
|
|
3842
4030
|
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SubagentSettlementBindingError,
|
|
3
|
+
SubagentDefinitionBindingError,
|
|
4
|
+
SubagentInvocationBindingError,
|
|
5
|
+
SubagentExecutionInvalidatedError,
|
|
6
|
+
} from './SubagentExecutionRegistry';
|
|
7
|
+
import { describeCodeApiError } from '@/tools/diagnostics';
|
|
8
|
+
|
|
9
|
+
/** Which step of child start-up failed: execution identity, then host config resolution. */
|
|
10
|
+
export type SubagentResolutionPhase = 'identity' | 'config';
|
|
11
|
+
|
|
12
|
+
const SUBAGENT_ERROR_TYPES = [
|
|
13
|
+
['SubagentExecutionInvalidatedError', SubagentExecutionInvalidatedError],
|
|
14
|
+
['SubagentDefinitionBindingError', SubagentDefinitionBindingError],
|
|
15
|
+
['SubagentInvocationBindingError', SubagentInvocationBindingError],
|
|
16
|
+
['SubagentSettlementBindingError', SubagentSettlementBindingError],
|
|
17
|
+
] as const;
|
|
18
|
+
|
|
19
|
+
type SubagentErrorLabel =
|
|
20
|
+
| (typeof SUBAGENT_ERROR_TYPES)[number][0]
|
|
21
|
+
| ReturnType<typeof describeCodeApiError>['type'];
|
|
22
|
+
|
|
23
|
+
const SUBAGENT_RESOLUTION_MESSAGES = {
|
|
24
|
+
workspace_unavailable:
|
|
25
|
+
'Subagent error: Workspace unavailable. Retry later or choose another workspace.',
|
|
26
|
+
agent_unavailable: 'Subagent error: Agent not found or not accessible.',
|
|
27
|
+
model_unavailable: 'Subagent error: Model or provider unavailable.',
|
|
28
|
+
configuration_changed:
|
|
29
|
+
'Subagent error: Subagent configuration changed. Start a new execution.',
|
|
30
|
+
unknown: 'Subagent error: Unable to initialize the selected subagent.',
|
|
31
|
+
} as const;
|
|
32
|
+
|
|
33
|
+
/** Host classifications accepted at the public failure boundary. */
|
|
34
|
+
export type SubagentResolutionCause = keyof typeof SUBAGENT_RESOLUTION_MESSAGES;
|
|
35
|
+
|
|
36
|
+
export type SubagentResolutionContext = {
|
|
37
|
+
parentRunId: string;
|
|
38
|
+
parentAgentId?: string;
|
|
39
|
+
parentToolCallId?: string;
|
|
40
|
+
threadId?: string;
|
|
41
|
+
childRunId: string;
|
|
42
|
+
childThreadId: string;
|
|
43
|
+
taskId?: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** No received error text, writable error names, or stacks enter this payload. */
|
|
47
|
+
export type SubagentResolutionDiagnostic = {
|
|
48
|
+
phase: SubagentResolutionPhase;
|
|
49
|
+
subagentType: string;
|
|
50
|
+
aborted: boolean;
|
|
51
|
+
type: SubagentErrorLabel;
|
|
52
|
+
cause: SubagentResolutionCause;
|
|
53
|
+
message: (typeof SUBAGENT_RESOLUTION_MESSAGES)[SubagentResolutionCause];
|
|
54
|
+
} & Partial<SubagentResolutionContext>;
|
|
55
|
+
|
|
56
|
+
/** The original error is private. Return a classification, never error text. */
|
|
57
|
+
export type SubagentResolutionFailureHandler = (
|
|
58
|
+
detail: Readonly<SubagentResolutionDiagnostic>,
|
|
59
|
+
error: unknown
|
|
60
|
+
) => SubagentResolutionCause | void;
|
|
61
|
+
|
|
62
|
+
function normalizeResolutionCause(
|
|
63
|
+
cause: SubagentResolutionCause | void
|
|
64
|
+
): SubagentResolutionCause {
|
|
65
|
+
switch (cause) {
|
|
66
|
+
case 'workspace_unavailable':
|
|
67
|
+
case 'agent_unavailable':
|
|
68
|
+
case 'model_unavailable':
|
|
69
|
+
case 'configuration_changed':
|
|
70
|
+
return cause;
|
|
71
|
+
default:
|
|
72
|
+
return 'unknown';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function getSubagentResolutionFailureMessage(
|
|
77
|
+
cause: SubagentResolutionCause
|
|
78
|
+
): SubagentResolutionDiagnostic['message'] {
|
|
79
|
+
return SUBAGENT_RESOLUTION_MESSAGES[normalizeResolutionCause(cause)];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Safe typed failure retained when detached execution crosses the host boundary. */
|
|
83
|
+
export class SubagentResolutionError extends Error {
|
|
84
|
+
readonly phase: SubagentResolutionPhase;
|
|
85
|
+
readonly resolutionCause: SubagentResolutionCause;
|
|
86
|
+
|
|
87
|
+
constructor(phase: SubagentResolutionPhase, cause: SubagentResolutionCause) {
|
|
88
|
+
super(getSubagentResolutionFailureMessage(cause));
|
|
89
|
+
this.name = 'SubagentResolutionError';
|
|
90
|
+
this.phase = phase;
|
|
91
|
+
this.resolutionCause = normalizeResolutionCause(cause);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function describeSubagentError(error: unknown): SubagentErrorLabel {
|
|
96
|
+
try {
|
|
97
|
+
for (const [label, constructor] of SUBAGENT_ERROR_TYPES) {
|
|
98
|
+
if (error instanceof constructor) {
|
|
99
|
+
return label;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
return 'UndescribableError';
|
|
104
|
+
}
|
|
105
|
+
return describeCodeApiError(error).type;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function logSubagentResolutionFailure(
|
|
109
|
+
phase: SubagentResolutionPhase,
|
|
110
|
+
subagentType: string,
|
|
111
|
+
signal: AbortSignal,
|
|
112
|
+
error: unknown,
|
|
113
|
+
context?: SubagentResolutionContext,
|
|
114
|
+
onResolutionFailure?: SubagentResolutionFailureHandler
|
|
115
|
+
): SubagentResolutionDiagnostic {
|
|
116
|
+
const detail: SubagentResolutionDiagnostic = {
|
|
117
|
+
...context,
|
|
118
|
+
phase,
|
|
119
|
+
subagentType,
|
|
120
|
+
aborted: signal.aborted,
|
|
121
|
+
type: describeSubagentError(error),
|
|
122
|
+
cause: 'unknown',
|
|
123
|
+
message: SUBAGENT_RESOLUTION_MESSAGES.unknown,
|
|
124
|
+
};
|
|
125
|
+
if (onResolutionFailure != null) {
|
|
126
|
+
try {
|
|
127
|
+
const cause = normalizeResolutionCause(
|
|
128
|
+
onResolutionFailure(Object.freeze(detail), error)
|
|
129
|
+
);
|
|
130
|
+
return {
|
|
131
|
+
...detail,
|
|
132
|
+
cause,
|
|
133
|
+
message: getSubagentResolutionFailureMessage(cause),
|
|
134
|
+
};
|
|
135
|
+
} catch {
|
|
136
|
+
// A broken diagnostic sink must not replace or expose the original failure.
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// eslint-disable-next-line no-console
|
|
140
|
+
console.warn('[SubagentExecutor] Subagent resolution failed', detail);
|
|
141
|
+
return detail;
|
|
142
|
+
}
|
|
@@ -19,3 +19,14 @@ export type {
|
|
|
19
19
|
} from './SubagentExecutor';
|
|
20
20
|
export { InMemorySubagentTaskStore } from './InMemorySubagentTaskStore';
|
|
21
21
|
export type { InMemorySubagentTaskStoreOptions } from './InMemorySubagentTaskStore';
|
|
22
|
+
export {
|
|
23
|
+
SubagentResolutionError,
|
|
24
|
+
getSubagentResolutionFailureMessage,
|
|
25
|
+
} from './diagnostics';
|
|
26
|
+
export type {
|
|
27
|
+
SubagentResolutionPhase,
|
|
28
|
+
SubagentResolutionCause,
|
|
29
|
+
SubagentResolutionContext,
|
|
30
|
+
SubagentResolutionDiagnostic,
|
|
31
|
+
SubagentResolutionFailureHandler,
|
|
32
|
+
} from './diagnostics';
|
package/src/types/graph.ts
CHANGED
|
@@ -44,6 +44,7 @@ import type {
|
|
|
44
44
|
StreamPreemption,
|
|
45
45
|
TokenBudgetBreakdown,
|
|
46
46
|
} from '@/types/run';
|
|
47
|
+
import type { SubagentResolutionFailureHandler } from '@/tools/subagent/diagnostics';
|
|
47
48
|
import type { SubagentTaskConfig } from '@/types/subagentTasks';
|
|
48
49
|
import type { StandardGraph, MultiAgentGraph } from '@/graphs';
|
|
49
50
|
import type { ProviderClientOptionsConfig } from '@/types/llm';
|
|
@@ -441,6 +442,8 @@ export type StandardGraphInput = {
|
|
|
441
442
|
subagentTasks?: SubagentTaskConfig;
|
|
442
443
|
/** Host-owned context and result projection for each isolated child execution. */
|
|
443
444
|
subagentContext?: SubagentContextAdapter;
|
|
445
|
+
/** Receives private startup errors; returns only an SDK-owned public cause. */
|
|
446
|
+
onSubagentResolutionFailure?: SubagentResolutionFailureHandler;
|
|
444
447
|
/**
|
|
445
448
|
* True when this graph IS a subagent child run (set by `SubagentExecutor`
|
|
446
449
|
* when it constructs the child graph). Drives the hook-input `agentId`
|
package/src/types/run.ts
CHANGED
|
@@ -300,6 +300,8 @@ export type RunConfig = {
|
|
|
300
300
|
subagentTasks?: SubagentTaskConfig;
|
|
301
301
|
/** Authorizes and supplies host-owned context to isolated child runs. */
|
|
302
302
|
subagentContext?: g.SubagentContextAdapter;
|
|
303
|
+
/** Host diagnostic sink for foreground, detached, and nested startup failures. */
|
|
304
|
+
onSubagentResolutionFailure?: g.StandardGraphInput['onSubagentResolutionFailure'];
|
|
303
305
|
/**
|
|
304
306
|
* Pre-constructed hook registry for this run. Hooks fire at lifecycle
|
|
305
307
|
* points in `processStream` (RunStart, UserPromptSubmit, Stop,
|