@librechat/agents 4.0.1 → 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/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 +11 -0
- package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +87 -14
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/subagent/diagnostics.cjs +45 -2
- package/dist/cjs/tools/subagent/diagnostics.cjs.map +1 -1
- 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/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 +11 -0
- package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +88 -15
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/subagent/diagnostics.mjs +44 -3
- package/dist/esm/tools/subagent/diagnostics.mjs.map +1 -1
- 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 +32 -8
- 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 +1 -1
- 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 +190 -22
- package/src/tools/subagent/diagnostics.ts +88 -9
- 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,
|
|
@@ -141,7 +151,6 @@ import { stripRunStepResumeState } from '@/tools/runStepResume';
|
|
|
141
151
|
import { seedAgentInitialSessions } from '@/utils/toolSessions';
|
|
142
152
|
import { stableStringify } from '@/tools/eagerEventExecution';
|
|
143
153
|
import { convertInjectedMessages } from '@/messages/injected';
|
|
144
|
-
import { logSubagentResolutionFailure } from './diagnostics';
|
|
145
154
|
import { resolveClientOptionsModel } from '@/llm/request';
|
|
146
155
|
import { isBackgroundDenyMode } from '@/types/hitl';
|
|
147
156
|
import { composeAbortSignals } from '@/utils/misc';
|
|
@@ -162,13 +171,19 @@ const MAX_QUEUED_SUBAGENT_UPDATES = 64;
|
|
|
162
171
|
const MAX_BACKGROUND_SUBAGENT_SEALS = 32;
|
|
163
172
|
const SUBAGENT_UPDATE_HANDLER_TIMEOUT_MS = 5_000;
|
|
164
173
|
const TEXT_DELTA_CONTENT_TYPE = `${ContentTypes.TEXT}_delta`;
|
|
165
|
-
const SUBAGENT_RESOLUTION_ERROR_MESSAGE =
|
|
166
|
-
'Subagent error: Unable to initialize the selected subagent.';
|
|
167
174
|
const SUBAGENT_CONFIG_CHANGED_MESSAGE =
|
|
168
175
|
'Subagent error: Subagent configuration changed since this execution was paused.';
|
|
169
176
|
const SUBAGENT_INVOCATION_CHANGED_MESSAGE =
|
|
170
177
|
'Subagent error: Subagent invocation changed for this execution.';
|
|
171
178
|
|
|
179
|
+
function isSubagentResolutionControlFlow(error: unknown): boolean {
|
|
180
|
+
try {
|
|
181
|
+
return error instanceof StreamLimitExceededError || isGraphInterrupt(error);
|
|
182
|
+
} catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
172
187
|
function seedChildGraphSessions(
|
|
173
188
|
childGraph: StandardGraph,
|
|
174
189
|
agents: AgentInputs[]
|
|
@@ -867,6 +882,11 @@ export type SubagentExecuteParams = {
|
|
|
867
882
|
export type SubagentExecuteResult = SubagentContextResult & {
|
|
868
883
|
/** Tagged internal failure; foreground callers retain the legacy content. */
|
|
869
884
|
error?: string;
|
|
885
|
+
/** Safe startup classification retained for detached host delivery. */
|
|
886
|
+
resolutionFailure?: {
|
|
887
|
+
phase: SubagentResolutionPhase;
|
|
888
|
+
cause: SubagentResolutionCause;
|
|
889
|
+
};
|
|
870
890
|
/** Completed child work whose host projection must be retried without re-execution. */
|
|
871
891
|
retryableDelivery?: true;
|
|
872
892
|
};
|
|
@@ -986,6 +1006,8 @@ export type SubagentExecutorOptions = {
|
|
|
986
1006
|
/** Host-owned process-local task namespace for detached execution. */
|
|
987
1007
|
taskConfig?: SubagentTaskConfig;
|
|
988
1008
|
subagentContext?: SubagentContextAdapter;
|
|
1009
|
+
/** Host diagnostic sink and safe cause classifier for startup failures. */
|
|
1010
|
+
onResolutionFailure?: SubagentResolutionFailureHandler;
|
|
989
1011
|
};
|
|
990
1012
|
|
|
991
1013
|
type DurableExecutionRecord = SubagentExecutionRecord<
|
|
@@ -1024,6 +1046,7 @@ export class SubagentExecutor {
|
|
|
1024
1046
|
private readonly usageSink?: SubagentUsageSink;
|
|
1025
1047
|
private readonly taskConfig?: SubagentTaskConfig;
|
|
1026
1048
|
private readonly subagentContext?: SubagentContextAdapter;
|
|
1049
|
+
private readonly onResolutionFailure?: SubagentResolutionFailureHandler;
|
|
1027
1050
|
private readonly executions: SubagentExecutionRegistry<
|
|
1028
1051
|
SubagentExecuteResult,
|
|
1029
1052
|
ResolvedSubagentConfig,
|
|
@@ -1069,6 +1092,7 @@ export class SubagentExecutor {
|
|
|
1069
1092
|
this.usageSink = options.usageSink;
|
|
1070
1093
|
this.taskConfig = options.taskConfig;
|
|
1071
1094
|
this.subagentContext = options.subagentContext;
|
|
1095
|
+
this.onResolutionFailure = options.onResolutionFailure;
|
|
1072
1096
|
const rawRegistry = options.parentHandlerRegistry;
|
|
1073
1097
|
if (typeof rawRegistry === 'function') {
|
|
1074
1098
|
this.resolveParentHandlerRegistry = rawRegistry;
|
|
@@ -1318,6 +1342,7 @@ export class SubagentExecutor {
|
|
|
1318
1342
|
tokenCounter: this.tokenCounter,
|
|
1319
1343
|
usageSink: this.usageSink,
|
|
1320
1344
|
subagentContext: this.subagentContext,
|
|
1345
|
+
onResolutionFailure: this.onResolutionFailure,
|
|
1321
1346
|
streamLimits: this.streamLimits,
|
|
1322
1347
|
humanInTheLoop:
|
|
1323
1348
|
this.humanInTheLoop?.enabled === true ||
|
|
@@ -1369,6 +1394,12 @@ export class SubagentExecutor {
|
|
|
1369
1394
|
result = await executeAttempt();
|
|
1370
1395
|
}
|
|
1371
1396
|
if (result.error != null) {
|
|
1397
|
+
if (result.resolutionFailure != null) {
|
|
1398
|
+
throw new SubagentResolutionError(
|
|
1399
|
+
result.resolutionFailure.phase,
|
|
1400
|
+
result.resolutionFailure.cause
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1372
1403
|
throw new Error(result.error);
|
|
1373
1404
|
}
|
|
1374
1405
|
if (this.humanInTheLoop?.enabled === true) {
|
|
@@ -1662,13 +1693,6 @@ export class SubagentExecutor {
|
|
|
1662
1693
|
),
|
|
1663
1694
|
});
|
|
1664
1695
|
}
|
|
1665
|
-
const sourceCheckpoints =
|
|
1666
|
-
await this.getLatestCheckpointSnapshot(baseChildThreadId);
|
|
1667
|
-
if (sourceCheckpoints.length === 0) {
|
|
1668
|
-
throw new Error(
|
|
1669
|
-
`Cannot fork subagent checkpoint thread "${baseChildThreadId}" without a checkpoint ID.`
|
|
1670
|
-
);
|
|
1671
|
-
}
|
|
1672
1696
|
const childRunId = persistedChildRunId ?? currentChildRunId;
|
|
1673
1697
|
const identity = {
|
|
1674
1698
|
childRunId,
|
|
@@ -1678,6 +1702,14 @@ export class SubagentExecutor {
|
|
|
1678
1702
|
resumeAttemptId
|
|
1679
1703
|
),
|
|
1680
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
|
+
}
|
|
1681
1713
|
const lease = this.executions.beginIdentityPreparation(execution, identity);
|
|
1682
1714
|
await this.prepareCheckpointFork(
|
|
1683
1715
|
sourceCheckpoints,
|
|
@@ -2083,9 +2115,10 @@ export class SubagentExecutor {
|
|
|
2083
2115
|
config: RunnableConfig
|
|
2084
2116
|
): Promise<SettledSubagentToolOutput | undefined> {
|
|
2085
2117
|
const parentToolCallId = call.id;
|
|
2118
|
+
const checkpointer = this.checkpointer;
|
|
2086
2119
|
if (
|
|
2087
2120
|
this.humanInTheLoop?.enabled !== true ||
|
|
2088
|
-
|
|
2121
|
+
checkpointer == null ||
|
|
2089
2122
|
parentToolCallId == null ||
|
|
2090
2123
|
parentToolCallId === ''
|
|
2091
2124
|
) {
|
|
@@ -2100,6 +2133,44 @@ export class SubagentExecutor {
|
|
|
2100
2133
|
parentToolCallId,
|
|
2101
2134
|
parentConfigurable,
|
|
2102
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> {
|
|
2103
2174
|
const { resumeExecution } = execution;
|
|
2104
2175
|
const inProcessSettledOutput = execution.settledOutput;
|
|
2105
2176
|
if (inProcessSettledOutput != null) {
|
|
@@ -2158,7 +2229,7 @@ export class SubagentExecutor {
|
|
|
2158
2229
|
const { childThreadId } =
|
|
2159
2230
|
await this.resolveChildExecutionIdentity(execution);
|
|
2160
2231
|
if (marker == null) {
|
|
2161
|
-
const checkpoint = await
|
|
2232
|
+
const checkpoint = await checkpointer.getTuple({
|
|
2162
2233
|
configurable: { thread_id: childThreadId },
|
|
2163
2234
|
});
|
|
2164
2235
|
messages = getCheckpointMessages(
|
|
@@ -2310,8 +2381,20 @@ export class SubagentExecutor {
|
|
|
2310
2381
|
},
|
|
2311
2382
|
persistedOutput,
|
|
2312
2383
|
async (): Promise<void> => {
|
|
2313
|
-
|
|
2314
|
-
|
|
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;
|
|
2315
2398
|
const activeChildRun = execution.activeRun;
|
|
2316
2399
|
if (activeChildRun != null) {
|
|
2317
2400
|
await this.persistChildCheckpointMarker(
|
|
@@ -2457,6 +2540,90 @@ export class SubagentExecutor {
|
|
|
2457
2540
|
}
|
|
2458
2541
|
}
|
|
2459
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
|
+
|
|
2460
2627
|
private async executeOnce(
|
|
2461
2628
|
params: SubagentExecuteParams,
|
|
2462
2629
|
execution: DurableExecutionRecord,
|
|
@@ -2490,16 +2657,16 @@ export class SubagentExecutor {
|
|
|
2490
2657
|
identity = await this.resolveChildExecutionIdentity(execution);
|
|
2491
2658
|
execution.assertUsable(childSignal);
|
|
2492
2659
|
} catch (error) {
|
|
2493
|
-
if (error
|
|
2660
|
+
if (isSubagentResolutionControlFlow(error)) {
|
|
2494
2661
|
throw error;
|
|
2495
2662
|
}
|
|
2496
|
-
|
|
2663
|
+
return this.createResolutionFailure(
|
|
2497
2664
|
'identity',
|
|
2498
|
-
|
|
2665
|
+
params,
|
|
2666
|
+
execution,
|
|
2499
2667
|
childSignal,
|
|
2500
2668
|
error
|
|
2501
2669
|
);
|
|
2502
|
-
return createSubagentFailure(SUBAGENT_RESOLUTION_ERROR_MESSAGE);
|
|
2503
2670
|
}
|
|
2504
2671
|
const { childRunId, childThreadId, approvalExecutionScope } = identity;
|
|
2505
2672
|
const bound = this.bindExecutionDefinition(
|
|
@@ -2526,16 +2693,16 @@ export class SubagentExecutor {
|
|
|
2526
2693
|
});
|
|
2527
2694
|
execution.assertUsable(childSignal);
|
|
2528
2695
|
} catch (error) {
|
|
2529
|
-
if (error
|
|
2696
|
+
if (isSubagentResolutionControlFlow(error)) {
|
|
2530
2697
|
throw error;
|
|
2531
2698
|
}
|
|
2532
|
-
|
|
2699
|
+
return this.createResolutionFailure(
|
|
2533
2700
|
'config',
|
|
2534
|
-
|
|
2701
|
+
params,
|
|
2702
|
+
execution,
|
|
2535
2703
|
childSignal,
|
|
2536
2704
|
error
|
|
2537
2705
|
);
|
|
2538
|
-
return createSubagentFailure(SUBAGENT_RESOLUTION_ERROR_MESSAGE);
|
|
2539
2706
|
}
|
|
2540
2707
|
|
|
2541
2708
|
const parentRegistry = this.getParentHandlerRegistry();
|
|
@@ -2667,6 +2834,7 @@ export class SubagentExecutor {
|
|
|
2667
2834
|
subagentScope: true,
|
|
2668
2835
|
subagentExecutionContext: childExecutionContext,
|
|
2669
2836
|
subagentContext: this.subagentContext,
|
|
2837
|
+
onSubagentResolutionFailure: this.onResolutionFailure,
|
|
2670
2838
|
...(resumeExecution?.graphState.fadingTier == null
|
|
2671
2839
|
? {}
|
|
2672
2840
|
: { fadingTier: resumeExecution.graphState.fadingTier }),
|
|
@@ -20,18 +20,77 @@ type SubagentErrorLabel =
|
|
|
20
20
|
| (typeof SUBAGENT_ERROR_TYPES)[number][0]
|
|
21
21
|
| ReturnType<typeof describeCodeApiError>['type'];
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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. */
|
|
29
47
|
export type SubagentResolutionDiagnostic = {
|
|
30
48
|
phase: SubagentResolutionPhase;
|
|
31
49
|
subagentType: string;
|
|
32
50
|
aborted: boolean;
|
|
33
51
|
type: SubagentErrorLabel;
|
|
34
|
-
|
|
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
|
+
}
|
|
35
94
|
|
|
36
95
|
function describeSubagentError(error: unknown): SubagentErrorLabel {
|
|
37
96
|
try {
|
|
@@ -50,14 +109,34 @@ export function logSubagentResolutionFailure(
|
|
|
50
109
|
phase: SubagentResolutionPhase,
|
|
51
110
|
subagentType: string,
|
|
52
111
|
signal: AbortSignal,
|
|
53
|
-
error: unknown
|
|
54
|
-
|
|
112
|
+
error: unknown,
|
|
113
|
+
context?: SubagentResolutionContext,
|
|
114
|
+
onResolutionFailure?: SubagentResolutionFailureHandler
|
|
115
|
+
): SubagentResolutionDiagnostic {
|
|
55
116
|
const detail: SubagentResolutionDiagnostic = {
|
|
117
|
+
...context,
|
|
56
118
|
phase,
|
|
57
119
|
subagentType,
|
|
58
120
|
aborted: signal.aborted,
|
|
59
121
|
type: describeSubagentError(error),
|
|
122
|
+
cause: 'unknown',
|
|
123
|
+
message: SUBAGENT_RESOLUTION_MESSAGES.unknown,
|
|
60
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
|
+
}
|
|
61
139
|
// eslint-disable-next-line no-console
|
|
62
140
|
console.warn('[SubagentExecutor] Subagent resolution failed', detail);
|
|
141
|
+
return detail;
|
|
63
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,
|