@botbotgo/agent-harness 0.0.349 → 0.0.351
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/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/resources/prompts/runtime/delegation-only-recovery.md +8 -0
- package/dist/runtime/adapter/flow/invocation-flow.js +207 -2
- package/dist/runtime/adapter/flow/stream-runtime.js +53 -4
- package/dist/runtime/adapter/middleware-assembly.js +111 -5
- package/dist/runtime/adapter/model/model-providers.js +50 -0
- package/dist/runtime/adapter/resilience.js +18 -0
- package/dist/runtime/agent-runtime-assembly.js +11 -0
- package/dist/runtime/prompts/runtime-prompts.d.ts +1 -0
- package/dist/runtime/prompts/runtime-prompts.js +1 -0
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.351";
|
|
2
2
|
export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-24";
|
package/dist/package-version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.351";
|
|
2
2
|
export const AGENT_HARNESS_RELEASE_DATE = "2026-04-24";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
A previous response ended without delegating work through the task tool.
|
|
2
|
+
|
|
3
|
+
This agent has configured subagents and no direct execution skills or raw tools for local specialist work. Your next response must do exactly one of these:
|
|
4
|
+
|
|
5
|
+
1. If the request fits any listed subagent description, call the task tool with the exact configured subagent_type and preserve the user's original request in the task description.
|
|
6
|
+
2. If no listed subagent can responsibly handle the request, return a final refusal that says the configured subagents do not cover the request.
|
|
7
|
+
|
|
8
|
+
Do not answer locally, do not merely announce that you will delegate, and do not invent or mention tools that are not listed.
|
|
@@ -4,6 +4,197 @@ import { finalizeRequestResult } from "../invocation-result.js";
|
|
|
4
4
|
import { invokeRuntimeWithLocalTools } from "./invoke-runtime.js";
|
|
5
5
|
import { buildInvocationRequest } from "../model/invocation-request.js";
|
|
6
6
|
import { UPSTREAM_REQUEST_CONFIG_KEY, UPSTREAM_SESSION_CONFIG_KEY } from "../upstream-configurable-keys.js";
|
|
7
|
+
import { extractVisibleOutput, tryParseJson } from "../../parsing/output-parsing.js";
|
|
8
|
+
function readBindingExecutionParams(binding) {
|
|
9
|
+
const params = binding.execution?.params ?? binding.deepAgentParams ?? binding.langchainAgentParams;
|
|
10
|
+
return {
|
|
11
|
+
tools: Array.isArray(params?.tools) ? params.tools : [],
|
|
12
|
+
subagents: Array.isArray(params?.subagents)
|
|
13
|
+
? (params.subagents ?? [])
|
|
14
|
+
: [],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function isDelegationOnlyBinding(binding) {
|
|
18
|
+
const { tools, subagents } = readBindingExecutionParams(binding);
|
|
19
|
+
const agent = binding.agent;
|
|
20
|
+
const configuredSubagents = [
|
|
21
|
+
...subagents,
|
|
22
|
+
...(agent?.subagentRefs ?? []),
|
|
23
|
+
...(agent?.subagentPathRefs ?? []),
|
|
24
|
+
...(agent?.asyncSubagents ?? []),
|
|
25
|
+
];
|
|
26
|
+
const configuredTools = [
|
|
27
|
+
...tools,
|
|
28
|
+
...(agent?.toolRefs ?? []),
|
|
29
|
+
...(agent?.toolBindings ?? []),
|
|
30
|
+
];
|
|
31
|
+
const skillRefs = agent?.skillPathRefs ?? [];
|
|
32
|
+
return configuredSubagents.length > 0 && configuredTools.length === 0 && skillRefs.length === 0;
|
|
33
|
+
}
|
|
34
|
+
function hasTaskDelegationEvidence(executedToolResults) {
|
|
35
|
+
return executedToolResults.some((item) => item.toolName === "task");
|
|
36
|
+
}
|
|
37
|
+
function hasNativeTaskDelegationIntent(value) {
|
|
38
|
+
if (typeof value !== "object" || value === null) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
return value.some((item) => hasNativeTaskDelegationIntent(item));
|
|
43
|
+
}
|
|
44
|
+
const typed = value;
|
|
45
|
+
if (typed.name === "task") {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return hasNativeTaskDelegationIntent(typed.tool_calls) || hasNativeTaskDelegationIntent(typed.messages);
|
|
49
|
+
}
|
|
50
|
+
function readStructuredToolCall(value) {
|
|
51
|
+
const text = typeof value === "string" ? value.trim() : "";
|
|
52
|
+
const parsed = text ? (tryParseJson(text) ?? extractFirstJsonObject(text)) : value;
|
|
53
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const typed = parsed;
|
|
57
|
+
const nameCandidate = typed.name ?? typed.tool;
|
|
58
|
+
const name = typeof nameCandidate === "string" ? nameCandidate.trim() : "";
|
|
59
|
+
if (!name) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const argsCandidate = typed.arguments ?? typed.args ?? typed.parameters ?? {};
|
|
63
|
+
const args = typeof argsCandidate === "object" && argsCandidate !== null && !Array.isArray(argsCandidate)
|
|
64
|
+
? argsCandidate
|
|
65
|
+
: {};
|
|
66
|
+
return { name, args };
|
|
67
|
+
}
|
|
68
|
+
function extractFirstJsonObject(text) {
|
|
69
|
+
let start = -1;
|
|
70
|
+
let depth = 0;
|
|
71
|
+
let inString = false;
|
|
72
|
+
let escaped = false;
|
|
73
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
74
|
+
const char = text[index];
|
|
75
|
+
if (start < 0) {
|
|
76
|
+
if (char === "{") {
|
|
77
|
+
start = index;
|
|
78
|
+
depth = 1;
|
|
79
|
+
}
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (escaped) {
|
|
83
|
+
escaped = false;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (char === "\\") {
|
|
87
|
+
escaped = inString;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (char === "\"") {
|
|
91
|
+
inString = !inString;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (inString) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (char === "{") {
|
|
98
|
+
depth += 1;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (char === "}") {
|
|
102
|
+
depth -= 1;
|
|
103
|
+
if (depth === 0) {
|
|
104
|
+
const parsed = tryParseJson(text.slice(start, index + 1));
|
|
105
|
+
if (parsed) {
|
|
106
|
+
return parsed;
|
|
107
|
+
}
|
|
108
|
+
start = -1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
async function replayStructuredTaskToolCall(input) {
|
|
115
|
+
if (hasTaskDelegationEvidence(input.invocation.executedToolResults)) {
|
|
116
|
+
return input.invocation;
|
|
117
|
+
}
|
|
118
|
+
const visibleOutput = extractVisibleOutput(input.invocation.result);
|
|
119
|
+
const toolCall = readStructuredToolCall(visibleOutput);
|
|
120
|
+
if (toolCall?.name !== "task") {
|
|
121
|
+
return input.invocation;
|
|
122
|
+
}
|
|
123
|
+
const taskTool = input.builtinExecutableTools.get("task");
|
|
124
|
+
if (!taskTool) {
|
|
125
|
+
return input.invocation;
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const output = await taskTool.invoke(toolCall.args, input.toolRuntimeContext);
|
|
129
|
+
return {
|
|
130
|
+
result: { output },
|
|
131
|
+
executedToolResults: [
|
|
132
|
+
...input.invocation.executedToolResults,
|
|
133
|
+
{ toolName: "task", output },
|
|
134
|
+
],
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
const output = error instanceof Error ? error.message : String(error);
|
|
139
|
+
return {
|
|
140
|
+
result: { output },
|
|
141
|
+
executedToolResults: [
|
|
142
|
+
...input.invocation.executedToolResults,
|
|
143
|
+
{ toolName: "task", output, isError: true },
|
|
144
|
+
],
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function formatDelegationSubagentCatalog(binding) {
|
|
149
|
+
const { subagents } = readBindingExecutionParams(binding);
|
|
150
|
+
return subagents
|
|
151
|
+
.map((subagent) => {
|
|
152
|
+
if (typeof subagent !== "object" || subagent === null) {
|
|
153
|
+
return "";
|
|
154
|
+
}
|
|
155
|
+
const typed = subagent;
|
|
156
|
+
const name = typeof typed.name === "string" ? typed.name.trim() : "";
|
|
157
|
+
if (!name) {
|
|
158
|
+
return "";
|
|
159
|
+
}
|
|
160
|
+
const description = typeof typed.description === "string" ? typed.description.trim() : "";
|
|
161
|
+
return description ? `- ${name}: ${description}` : `- ${name}`;
|
|
162
|
+
})
|
|
163
|
+
.filter(Boolean)
|
|
164
|
+
.join("\n");
|
|
165
|
+
}
|
|
166
|
+
function buildDelegationOnlyRecoveryInstruction(binding, input) {
|
|
167
|
+
const subagentCatalog = formatDelegationSubagentCatalog(binding) || "(none)";
|
|
168
|
+
return [
|
|
169
|
+
"/no_think",
|
|
170
|
+
"Runtime correction: this agent is delegation-only.",
|
|
171
|
+
"Return exactly one JSON object and no prose.",
|
|
172
|
+
"Use this shape when a listed subagent fits:",
|
|
173
|
+
"{\"name\":\"task\",\"arguments\":{\"subagent_type\":\"<listed name>\",\"description\":\"<original request>\"}}",
|
|
174
|
+
"Use this shape only when no listed subagent fits:",
|
|
175
|
+
"{\"status\":\"refused\",\"reason\":\"No configured subagent can handle the request.\"}",
|
|
176
|
+
"Listed subagents:",
|
|
177
|
+
subagentCatalog,
|
|
178
|
+
"Original request:",
|
|
179
|
+
JSON.stringify(input),
|
|
180
|
+
].join("\n");
|
|
181
|
+
}
|
|
182
|
+
function appendUserRecoveryInstruction(input, instruction) {
|
|
183
|
+
const content = [
|
|
184
|
+
"Runtime correction:",
|
|
185
|
+
instruction,
|
|
186
|
+
].join("\n\n");
|
|
187
|
+
if (Array.isArray(input)) {
|
|
188
|
+
return [...input, { role: "user", content }];
|
|
189
|
+
}
|
|
190
|
+
if (typeof input === "object" && input && Array.isArray(input.messages)) {
|
|
191
|
+
return {
|
|
192
|
+
...input,
|
|
193
|
+
messages: [...(input.messages), { role: "user", content }],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return input;
|
|
197
|
+
}
|
|
7
198
|
export async function executeRequestInvocation(options) {
|
|
8
199
|
const history = options.history ?? [];
|
|
9
200
|
const invokeOptions = options.invokeOptions ?? {};
|
|
@@ -24,9 +215,9 @@ export async function executeRequestInvocation(options) {
|
|
|
24
215
|
},
|
|
25
216
|
});
|
|
26
217
|
const builtinExecutableTools = await options.resolveBuiltinMiddlewareTools(options.binding, invokeOptions);
|
|
27
|
-
const
|
|
218
|
+
const invokeOnce = (activeRequest) => invokeRuntimeWithLocalTools({
|
|
28
219
|
binding: options.binding,
|
|
29
|
-
request,
|
|
220
|
+
request: activeRequest,
|
|
30
221
|
resumePayload: options.resumePayload,
|
|
31
222
|
primaryTools,
|
|
32
223
|
defersToUpstreamHitlExecution,
|
|
@@ -36,6 +227,20 @@ export async function executeRequestInvocation(options) {
|
|
|
36
227
|
callRuntimeWithToolParseRecovery: options.callRuntimeWithToolParseRecovery,
|
|
37
228
|
toolRuntimeContext: invokeOptions.toolRuntimeContext,
|
|
38
229
|
});
|
|
230
|
+
let localOrUpstreamInvocation = await invokeOnce(request);
|
|
231
|
+
if (options.resumePayload === undefined
|
|
232
|
+
&& isDelegationOnlyBinding(options.binding)
|
|
233
|
+
&& !hasTaskDelegationEvidence(localOrUpstreamInvocation.executedToolResults)
|
|
234
|
+
&& !hasNativeTaskDelegationIntent(localOrUpstreamInvocation.result)) {
|
|
235
|
+
const delegationRecoveryInstruction = buildDelegationOnlyRecoveryInstruction(options.binding, options.input);
|
|
236
|
+
const recoveredRequest = appendUserRecoveryInstruction(request, delegationRecoveryInstruction);
|
|
237
|
+
const recoveredInvocation = await invokeOnce(recoveredRequest);
|
|
238
|
+
localOrUpstreamInvocation = await replayStructuredTaskToolCall({
|
|
239
|
+
invocation: recoveredInvocation,
|
|
240
|
+
builtinExecutableTools: builtinExecutableTools,
|
|
241
|
+
toolRuntimeContext: invokeOptions.toolRuntimeContext,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
39
244
|
const result = localOrUpstreamInvocation.result;
|
|
40
245
|
const executedToolResults = [...localOrUpstreamInvocation.executedToolResults];
|
|
41
246
|
if (!result) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { extractVisibleOutput, isToolCallRecoveryFailure, isRetrySafeInvalidToolSelectionError, resolveMissingPlanRecoveryInstruction, resolveExecutionWithoutToolEvidenceTextInstruction, shouldValidateExecutionWithoutToolEvidence, resolveToolCallRecoveryInstruction, sanitizeVisibleText, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION, INVALID_TOOL_SELECTION_RECOVERY_INSTRUCTION, } from "../../parsing/output-parsing.js";
|
|
2
|
-
import { DELEGATED_TASK_FAILURE_RECOVERY_INSTRUCTION } from "../../prompts/runtime-prompts.js";
|
|
2
|
+
import { DELEGATED_TASK_FAILURE_RECOVERY_INSTRUCTION, DELEGATION_ONLY_RECOVERY_INSTRUCTION, } from "../../prompts/runtime-prompts.js";
|
|
3
3
|
import { buildInvocationRequest } from "../model/invocation-request.js";
|
|
4
4
|
import { buildRawModelMessages } from "../model/message-assembly.js";
|
|
5
5
|
import { projectRuntimeStreamEvent, createStreamEventProjectionState } from "../stream-event-projection.js";
|
|
@@ -85,6 +85,7 @@ function buildExecutionRecoveryEvidence(params) {
|
|
|
85
85
|
hasOnlyPlaceholderTaskCompletion: projectionState.emittedSuccessfulTaskResult
|
|
86
86
|
&& projectionState.emittedPlaceholderTaskResult
|
|
87
87
|
&& !projectionState.emittedDelegatedTerminalOutput,
|
|
88
|
+
hasSuccessfulTaskToolEvidence: projectionState.emittedSuccessfulTaskResult || hasSuccessfulTaskToolEvidence(executedToolResults),
|
|
88
89
|
};
|
|
89
90
|
}
|
|
90
91
|
function hasUnresolvedExecution(evidence) {
|
|
@@ -98,6 +99,45 @@ function hasMissingDelegatedExecutionEvidence(evidence) {
|
|
|
98
99
|
function hasMissingDelegatedFindings(evidence) {
|
|
99
100
|
return evidence.hasDelegatedAgentWithConfiguredTools && evidence.hasOnlyPlaceholderTaskCompletion;
|
|
100
101
|
}
|
|
102
|
+
function readBindingExecutionParams(binding) {
|
|
103
|
+
const params = binding.execution?.params ?? binding.deepAgentParams ?? binding.langchainAgentParams;
|
|
104
|
+
return {
|
|
105
|
+
tools: Array.isArray(params?.tools) ? params.tools : [],
|
|
106
|
+
subagents: Array.isArray(params?.subagents)
|
|
107
|
+
? (params.subagents ?? [])
|
|
108
|
+
: [],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function isDelegationOnlyBinding(binding) {
|
|
112
|
+
const { tools, subagents } = readBindingExecutionParams(binding);
|
|
113
|
+
const agent = binding.agent;
|
|
114
|
+
const configuredSubagents = [
|
|
115
|
+
...subagents,
|
|
116
|
+
...(agent?.subagentRefs ?? []),
|
|
117
|
+
...(agent?.subagentPathRefs ?? []),
|
|
118
|
+
...(agent?.asyncSubagents ?? []),
|
|
119
|
+
];
|
|
120
|
+
const configuredTools = [
|
|
121
|
+
...tools,
|
|
122
|
+
...(agent?.toolRefs ?? []),
|
|
123
|
+
...(agent?.toolBindings ?? []),
|
|
124
|
+
];
|
|
125
|
+
const skillRefs = agent?.skillPathRefs ?? [];
|
|
126
|
+
return configuredSubagents.length > 0 && configuredTools.length === 0 && skillRefs.length === 0;
|
|
127
|
+
}
|
|
128
|
+
function hasDelegationEvidence(evidence) {
|
|
129
|
+
return (evidence.hasSuccessfulTaskToolEvidence
|
|
130
|
+
|| evidence.hasOpenTaskDelegation
|
|
131
|
+
|| evidence.hasFailedTaskDelegation
|
|
132
|
+
|| evidence.hasDelegatedAgentWithConfiguredTools
|
|
133
|
+
|| evidence.hasDelegatedExecutionToolEvidence);
|
|
134
|
+
}
|
|
135
|
+
function resolveDelegationOnlyRecoveryInstruction(binding, evidence) {
|
|
136
|
+
if (!isDelegationOnlyBinding(binding) || hasDelegationEvidence(evidence)) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return DELEGATION_ONLY_RECOVERY_INSTRUCTION;
|
|
140
|
+
}
|
|
101
141
|
function isRuntimeFailureOutput(value) {
|
|
102
142
|
return value.trim().startsWith("runtime_error=");
|
|
103
143
|
}
|
|
@@ -328,7 +368,7 @@ export async function* streamRuntimeExecution(options) {
|
|
|
328
368
|
});
|
|
329
369
|
throw error;
|
|
330
370
|
}
|
|
331
|
-
if (!options.forceInvokeFallback && typeof runnable.streamEvents === "function") {
|
|
371
|
+
if (!options.forceInvokeFallback && !isDelegationOnlyBinding(options.binding) && typeof runnable.streamEvents === "function") {
|
|
332
372
|
const streamEventsStart = startProfileStep({
|
|
333
373
|
id: "profile:agent:stream-events-start",
|
|
334
374
|
kind: "agent",
|
|
@@ -423,13 +463,15 @@ export async function* streamRuntimeExecution(options) {
|
|
|
423
463
|
hasToolResultEvidence: terminalExecutionEvidence.hasSuccessfulNonTodoToolResultEvidence,
|
|
424
464
|
})
|
|
425
465
|
: null;
|
|
466
|
+
const terminalDelegationOnlyRecoveryInstruction = resolveDelegationOnlyRecoveryInstruction(options.binding, terminalExecutionEvidence);
|
|
426
467
|
if (!shouldDeferStreamContent()
|
|
427
468
|
&& !terminalExecutionEvidence.hasIncompletePlanState
|
|
428
469
|
&& !terminalExecutionEvidence.hasFailedTaskDelegation
|
|
429
470
|
&& !terminalExecutionEvidence.hasOpenTaskDelegation
|
|
430
471
|
&& !hasMissingDelegatedExecutionEvidence(terminalExecutionEvidence)
|
|
431
472
|
&& !hasMissingDelegatedFindings(terminalExecutionEvidence)
|
|
432
|
-
&& !terminalMissingPlanRecoveryInstruction
|
|
473
|
+
&& !terminalMissingPlanRecoveryInstruction
|
|
474
|
+
&& !terminalDelegationOnlyRecoveryInstruction) {
|
|
433
475
|
if (deferredStreamContent.length > 0) {
|
|
434
476
|
yield* flushDeferredStreamContent();
|
|
435
477
|
}
|
|
@@ -462,6 +504,7 @@ export async function* streamRuntimeExecution(options) {
|
|
|
462
504
|
}
|
|
463
505
|
const streamedExecutionEvidence = buildExecutionRecoveryEvidence({ projectionState });
|
|
464
506
|
const streamedDelegatedRecoveryInstruction = resolveDelegatedExecutionRecoveryInstruction(streamedExecutionEvidence);
|
|
507
|
+
const streamedDelegationOnlyRecoveryInstruction = resolveDelegationOnlyRecoveryInstruction(options.binding, streamedExecutionEvidence);
|
|
465
508
|
const delegatedExecutionRecoveryInstruction = !emittedUnsafeStreamSideEffects || streamedDelegatedRecoveryInstruction
|
|
466
509
|
? streamedDelegatedRecoveryInstruction
|
|
467
510
|
: null;
|
|
@@ -493,6 +536,7 @@ export async function* streamRuntimeExecution(options) {
|
|
|
493
536
|
: delegatedExecutionRecoveryInstruction
|
|
494
537
|
?? streamedRuntimeFailureRecoveryInstruction
|
|
495
538
|
?? missingPlanRecoveryInstruction
|
|
539
|
+
?? streamedDelegationOnlyRecoveryInstruction
|
|
496
540
|
?? executionWithoutToolEvidenceInstruction;
|
|
497
541
|
if (retryInstruction) {
|
|
498
542
|
let retried;
|
|
@@ -565,7 +609,10 @@ export async function* streamRuntimeExecution(options) {
|
|
|
565
609
|
return;
|
|
566
610
|
}
|
|
567
611
|
}
|
|
568
|
-
if (!options.forceInvokeFallback
|
|
612
|
+
if (!options.forceInvokeFallback
|
|
613
|
+
&& options.isLangChainBinding(options.binding)
|
|
614
|
+
&& !isDelegationOnlyBinding(options.binding)
|
|
615
|
+
&& typeof runnable.stream === "function") {
|
|
569
616
|
const streamStart = startProfileStep({
|
|
570
617
|
id: "profile:agent:stream-start",
|
|
571
618
|
kind: "agent",
|
|
@@ -705,7 +752,9 @@ export async function* streamRuntimeExecution(options) {
|
|
|
705
752
|
hasToolResultEvidence: invokeExecutionEvidence.hasSuccessfulNonTodoToolResultEvidence,
|
|
706
753
|
})
|
|
707
754
|
: null;
|
|
755
|
+
const invokeFallbackDelegationOnlyRecoveryInstruction = resolveDelegationOnlyRecoveryInstruction(options.binding, invokeExecutionEvidence);
|
|
708
756
|
const effectiveInvokeFallbackRecoveryInstruction = invokeFallbackMissingPlanRecoveryInstruction
|
|
757
|
+
?? invokeFallbackDelegationOnlyRecoveryInstruction
|
|
709
758
|
?? invokeFallbackRecoveryInstruction;
|
|
710
759
|
if (effectiveInvokeFallbackRecoveryInstruction) {
|
|
711
760
|
const recovered = await options.invoke(options.applyToolRecoveryInstruction(options.binding, effectiveInvokeFallbackRecoveryInstruction), options.input, options.sessionId, options.runtimeOptions.requestId ?? options.sessionId, undefined, options.history, options.runtimeOptions);
|
|
@@ -9,7 +9,7 @@ import { resolveDeclaredMiddleware } from "./tool/declared-middleware.js";
|
|
|
9
9
|
import { UPSTREAM_SESSION_CONFIG_KEY } from "./upstream-configurable-keys.js";
|
|
10
10
|
import { bindingHasLangChainSubagentSupport, bindingHasMiddlewareKind, getBindingBuiltinToolsConfig, getBindingExecutionKind, getBindingGeneralPurposeAgent, getBindingDeepAgentSubagents, getBindingInterruptCompatibilityRules, getBindingMiddlewareConfigs, getBindingMemorySources, getBindingPrimaryModel, getBindingPrimaryTools, getBindingSkills, getBindingSubagents, getBindingTaskDescription, isDeepAgentBinding, isLangChainBinding, } from "../support/compiled-binding.js";
|
|
11
11
|
import { materializeDeepAgentSkillSourcePaths } from "./compat/deepagent-compat.js";
|
|
12
|
-
import { DEFAULT_SUBAGENT_PROMPT } from "../prompts/runtime-prompts.js";
|
|
12
|
+
import { DEFAULT_SUBAGENT_PROMPT, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION, } from "../prompts/runtime-prompts.js";
|
|
13
13
|
import { createContextHygieneMiddleware } from "./middleware/context-hygiene.js";
|
|
14
14
|
const INVALID_TOOL_MESSAGE_BLOCK_TYPES = new Set(["tool_use", "thinking", "redacted_thinking"]);
|
|
15
15
|
function extractDeepAgentTaskContent(result) {
|
|
@@ -32,6 +32,92 @@ function extractDeepAgentTaskContent(result) {
|
|
|
32
32
|
}
|
|
33
33
|
return undefined;
|
|
34
34
|
}
|
|
35
|
+
function readMessageType(message) {
|
|
36
|
+
if (typeof message !== "object" || message === null) {
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
if (typeof message._getType === "function") {
|
|
40
|
+
const typeName = message._getType();
|
|
41
|
+
return typeof typeName === "string" ? typeName : "";
|
|
42
|
+
}
|
|
43
|
+
const typed = message;
|
|
44
|
+
if (typeof typed.type === "string") {
|
|
45
|
+
return typed.type;
|
|
46
|
+
}
|
|
47
|
+
const kwargs = typeof typed.kwargs === "object" && typed.kwargs !== null
|
|
48
|
+
? typed.kwargs
|
|
49
|
+
: undefined;
|
|
50
|
+
if (typeof kwargs?.type === "string") {
|
|
51
|
+
return kwargs.type;
|
|
52
|
+
}
|
|
53
|
+
const lcKwargs = typeof typed.lc_kwargs === "object" && typed.lc_kwargs !== null
|
|
54
|
+
? typed.lc_kwargs
|
|
55
|
+
: undefined;
|
|
56
|
+
return typeof lcKwargs?.type === "string" ? lcKwargs.type : "";
|
|
57
|
+
}
|
|
58
|
+
function readMessageName(message) {
|
|
59
|
+
if (typeof message !== "object" || message === null) {
|
|
60
|
+
return "";
|
|
61
|
+
}
|
|
62
|
+
const typed = message;
|
|
63
|
+
if (typeof typed.name === "string") {
|
|
64
|
+
return typed.name;
|
|
65
|
+
}
|
|
66
|
+
const kwargs = typeof typed.kwargs === "object" && typed.kwargs !== null
|
|
67
|
+
? typed.kwargs
|
|
68
|
+
: undefined;
|
|
69
|
+
if (typeof kwargs?.name === "string") {
|
|
70
|
+
return kwargs.name;
|
|
71
|
+
}
|
|
72
|
+
const lcKwargs = typeof typed.lc_kwargs === "object" && typed.lc_kwargs !== null
|
|
73
|
+
? typed.lc_kwargs
|
|
74
|
+
: undefined;
|
|
75
|
+
return typeof lcKwargs?.name === "string" ? lcKwargs.name : "";
|
|
76
|
+
}
|
|
77
|
+
function readMessages(result) {
|
|
78
|
+
if (typeof result !== "object" || result === null) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const messages = result.messages;
|
|
82
|
+
return Array.isArray(messages) ? messages : [];
|
|
83
|
+
}
|
|
84
|
+
function readToolNames(tools) {
|
|
85
|
+
if (!Array.isArray(tools)) {
|
|
86
|
+
return new Set();
|
|
87
|
+
}
|
|
88
|
+
return new Set(tools
|
|
89
|
+
.map((tool) => {
|
|
90
|
+
if (typeof tool !== "object" || tool === null) {
|
|
91
|
+
return "";
|
|
92
|
+
}
|
|
93
|
+
const name = tool.name;
|
|
94
|
+
return typeof name === "string" ? name : "";
|
|
95
|
+
})
|
|
96
|
+
.filter((name) => name.length > 0));
|
|
97
|
+
}
|
|
98
|
+
function hasSubagentExecutionToolEvidence(result, resolvedTools, configuredTools) {
|
|
99
|
+
const requiredToolNames = new Set([
|
|
100
|
+
...readToolNames(configuredTools),
|
|
101
|
+
...readToolNames(resolvedTools),
|
|
102
|
+
]);
|
|
103
|
+
if (requiredToolNames.size === 0) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
for (const message of readMessages(result)) {
|
|
107
|
+
const typeName = readMessageType(message);
|
|
108
|
+
if (typeName !== "tool" && typeName !== "ToolMessage") {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const name = readMessageName(message);
|
|
112
|
+
if (name === "write_todos" || name === "read_todos") {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (requiredToolNames.has(name)) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
35
121
|
export function extractSubagentRequestText(state) {
|
|
36
122
|
if (!isRecord(state)) {
|
|
37
123
|
return "";
|
|
@@ -257,6 +343,7 @@ export async function invokeBuiltinTaskTool(input) {
|
|
|
257
343
|
const builtinBackend = input.resolveBuiltinMiddlewareBackend(input.binding, input.options);
|
|
258
344
|
const resolvedSubagents = await input.resolveSubagents(compiledSubagents, input.binding);
|
|
259
345
|
const selectedSubagent = resolvedSubagents.find((subagent) => subagent.name === subagentType);
|
|
346
|
+
const selectedCompiledSubagent = compiledSubagents.find((subagent) => subagent.name === subagentType);
|
|
260
347
|
if (!selectedSubagent) {
|
|
261
348
|
const allowed = resolvedSubagents.map((subagent) => subagent.name);
|
|
262
349
|
throw new Error(`Error: invoked agent of type ${subagentType}, the only allowed types are ${allowed.map((name) => `\`${name}\``).join(", ")}`);
|
|
@@ -282,9 +369,16 @@ export async function invokeBuiltinTaskTool(input) {
|
|
|
282
369
|
configurable: { [UPSTREAM_SESSION_CONFIG_KEY]: `${input.binding.agent.id}:builtin-task` },
|
|
283
370
|
...(input.options?.context ? { context: input.options.context } : {}),
|
|
284
371
|
};
|
|
285
|
-
const
|
|
286
|
-
messages: [new HumanMessage({ content
|
|
372
|
+
const invokeSubagent = (content) => runnable.invoke({
|
|
373
|
+
messages: [new HumanMessage({ content })],
|
|
287
374
|
}, invokeConfig);
|
|
375
|
+
let result = await invokeSubagent(description);
|
|
376
|
+
if (!hasSubagentExecutionToolEvidence(result, resolvedSubagentTools, selectedCompiledSubagent?.tools)) {
|
|
377
|
+
result = await invokeSubagent([description, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"));
|
|
378
|
+
if (!hasSubagentExecutionToolEvidence(result, resolvedSubagentTools, selectedCompiledSubagent?.tools)) {
|
|
379
|
+
throw new Error(`Delegated agent ${selectedSubagent.name} completed without tool execution evidence.`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
288
382
|
const structuredResponse = typeof result === "object" && result !== null && "structuredResponse" in result
|
|
289
383
|
? result.structuredResponse
|
|
290
384
|
: undefined;
|
|
@@ -315,11 +409,23 @@ export async function invokeBuiltinTaskTool(input) {
|
|
|
315
409
|
}
|
|
316
410
|
export async function resolveBuiltinMiddlewareTools(input) {
|
|
317
411
|
const backend = input.resolveBuiltinMiddlewareBackend(input.binding, input.options);
|
|
412
|
+
const params = input.binding.execution?.params ?? input.binding.deepAgentParams ?? input.binding.langchainAgentParams;
|
|
413
|
+
const configuredSubagents = [
|
|
414
|
+
...(Array.isArray(params?.subagents)
|
|
415
|
+
? (params.subagents ?? [])
|
|
416
|
+
: []),
|
|
417
|
+
...(input.binding.agent.subagentRefs ?? []),
|
|
418
|
+
...(input.binding.agent.subagentPathRefs ?? []),
|
|
419
|
+
...(input.binding.agent.asyncSubagents ?? []),
|
|
420
|
+
];
|
|
421
|
+
const includeTaskTool = configuredSubagents.length > 0;
|
|
318
422
|
const tools = (await createBuiltinMiddlewareTools(backend, {
|
|
319
|
-
includeTaskTool
|
|
423
|
+
includeTaskTool,
|
|
320
424
|
workspaceRoot: input.binding.harnessRuntime.workspaceRoot,
|
|
321
425
|
toolRuntimeContext: input.options?.toolRuntimeContext,
|
|
322
|
-
invokeTaskTool:
|
|
426
|
+
invokeTaskTool: includeTaskTool
|
|
427
|
+
? (toolInput) => input.invokeBuiltinTaskTool(input.binding, toolInput, input.options)
|
|
428
|
+
: undefined,
|
|
323
429
|
}));
|
|
324
430
|
const builtinTools = getBindingBuiltinToolsConfig(input.binding) ?? {};
|
|
325
431
|
if (builtinTools.todos === false) {
|
|
@@ -299,6 +299,10 @@ function extractToolCallPayload(text) {
|
|
|
299
299
|
if (direct) {
|
|
300
300
|
return direct;
|
|
301
301
|
}
|
|
302
|
+
const firstJsonObject = extractFirstJsonObjectPayload(trimmed);
|
|
303
|
+
if (firstJsonObject) {
|
|
304
|
+
return firstJsonObject;
|
|
305
|
+
}
|
|
302
306
|
const fenced = extractFencePayload(trimmed);
|
|
303
307
|
if (fenced) {
|
|
304
308
|
const parsed = tryParseJson(fenced);
|
|
@@ -326,6 +330,52 @@ function extractToolCallPayload(text) {
|
|
|
326
330
|
}
|
|
327
331
|
return null;
|
|
328
332
|
}
|
|
333
|
+
function extractFirstJsonObjectPayload(text) {
|
|
334
|
+
let start = -1;
|
|
335
|
+
let depth = 0;
|
|
336
|
+
let inString = false;
|
|
337
|
+
let escaped = false;
|
|
338
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
339
|
+
const char = text[index];
|
|
340
|
+
if (start < 0) {
|
|
341
|
+
if (char === "{") {
|
|
342
|
+
start = index;
|
|
343
|
+
depth = 1;
|
|
344
|
+
}
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (escaped) {
|
|
348
|
+
escaped = false;
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
if (char === "\\") {
|
|
352
|
+
escaped = inString;
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
if (char === "\"") {
|
|
356
|
+
inString = !inString;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (inString) {
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (char === "{") {
|
|
363
|
+
depth += 1;
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (char === "}") {
|
|
367
|
+
depth -= 1;
|
|
368
|
+
if (depth === 0) {
|
|
369
|
+
const parsed = tryParseJson(text.slice(start, index + 1));
|
|
370
|
+
if (parsed) {
|
|
371
|
+
return parsed;
|
|
372
|
+
}
|
|
373
|
+
start = -1;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
329
379
|
function extractFencePayload(text) {
|
|
330
380
|
const start = text.indexOf("```");
|
|
331
381
|
if (start < 0) {
|
|
@@ -41,6 +41,21 @@ function isEmptyFinalAiMessageError(error) {
|
|
|
41
41
|
const message = error instanceof Error ? error.message : String(error);
|
|
42
42
|
return message.toLowerCase().startsWith("empty_final_ai_message:");
|
|
43
43
|
}
|
|
44
|
+
function isRetryableHttpStatus(status) {
|
|
45
|
+
return typeof status === "number" && Number.isInteger(status) && status >= 500 && status <= 599;
|
|
46
|
+
}
|
|
47
|
+
function hasRetryableHttpStatus(error, seen = new Set()) {
|
|
48
|
+
if (typeof error !== "object" || error === null || seen.has(error)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
seen.add(error);
|
|
52
|
+
const typed = error;
|
|
53
|
+
return isRetryableHttpStatus(typed.status)
|
|
54
|
+
|| isRetryableHttpStatus(typed.statusCode)
|
|
55
|
+
|| isRetryableHttpStatus(typed.status_code)
|
|
56
|
+
|| isRetryableHttpStatus(typed.code)
|
|
57
|
+
|| hasRetryableHttpStatus(typed.cause, seen);
|
|
58
|
+
}
|
|
44
59
|
export function resolveProviderRetryPolicy(binding) {
|
|
45
60
|
const resilience = typeof binding.harnessRuntime.resilience === "object" && binding.harnessRuntime.resilience
|
|
46
61
|
? binding.harnessRuntime.resilience
|
|
@@ -71,6 +86,9 @@ export function isRetryableProviderError(binding, error) {
|
|
|
71
86
|
if (isEmptyFinalAiMessageError(error)) {
|
|
72
87
|
return true;
|
|
73
88
|
}
|
|
89
|
+
if (hasRetryableHttpStatus(error)) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
74
92
|
const message = error instanceof Error ? error.message : String(error);
|
|
75
93
|
const normalized = message.toLowerCase();
|
|
76
94
|
const { retryableMessages } = resolveProviderRetryPolicy(binding);
|
|
@@ -58,6 +58,7 @@ export function buildDeepAgentSystemPromptWithCapabilityHierarchy(input) {
|
|
|
58
58
|
const skills = buildSkillCatalog(input.skills ?? []);
|
|
59
59
|
const tools = input.tools ?? [];
|
|
60
60
|
const hasNoConfiguredCapabilities = input.subagents.length === 0 && skills.length === 0 && tools.length === 0;
|
|
61
|
+
const isDelegationOnly = input.subagents.length > 0 && skills.length === 0 && tools.length === 0;
|
|
61
62
|
const catalogPrompt = [
|
|
62
63
|
"Capability selection hierarchy:",
|
|
63
64
|
"1. If the current request fits an available subagent, delegate with the task tool before using local skills or raw tools.",
|
|
@@ -109,6 +110,16 @@ export function buildDeepAgentSystemPromptWithCapabilityHierarchy(input) {
|
|
|
109
110
|
"The only valid next step is for the workspace owner to attach an appropriate subagent, skill, or tool and retry.",
|
|
110
111
|
]
|
|
111
112
|
: []),
|
|
113
|
+
...(isDelegationOnly
|
|
114
|
+
? [
|
|
115
|
+
"",
|
|
116
|
+
"Delegation-only mode:",
|
|
117
|
+
"This agent has specialist subagents but no direct execution skills or raw tools.",
|
|
118
|
+
"For any request that fits a listed subagent, the first assistant response must be a task tool call only.",
|
|
119
|
+
"Do not write a natural-language promise such as saying you will use a subagent. Actually call the task tool.",
|
|
120
|
+
"If no listed subagent fits, return a final refusal instead of answering locally.",
|
|
121
|
+
]
|
|
122
|
+
: []),
|
|
112
123
|
"",
|
|
113
124
|
].join("\n");
|
|
114
125
|
return [basePrompt, catalogPrompt].filter((part) => typeof part === "string" && part.length > 0).join("\n\n");
|
|
@@ -12,6 +12,7 @@ export declare const EXECUTION_WITH_TOOL_EVIDENCE_INSTRUCTION: string;
|
|
|
12
12
|
export declare const EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION: string;
|
|
13
13
|
export declare const AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION: string;
|
|
14
14
|
export declare const DELEGATED_TASK_FAILURE_RECOVERY_INSTRUCTION: string;
|
|
15
|
+
export declare const DELEGATION_ONLY_RECOVERY_INSTRUCTION: string;
|
|
15
16
|
export declare const INTERNAL_RUNTIME_SPILL_PATH_INSTRUCTION: string;
|
|
16
17
|
export declare const WORKSPACE_RELATIVE_PATH_INSTRUCTION: string;
|
|
17
18
|
export declare function renderDurableMemoryContextPrompt(memoryContext: string): string;
|
|
@@ -15,6 +15,7 @@ export const EXECUTION_WITH_TOOL_EVIDENCE_INSTRUCTION = readRuntimePrompt("execu
|
|
|
15
15
|
export const EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION = readRuntimePrompt("execution-with-tool-evidence-retry");
|
|
16
16
|
export const AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION = readRuntimePrompt("autonomous-investigation-recovery");
|
|
17
17
|
export const DELEGATED_TASK_FAILURE_RECOVERY_INSTRUCTION = readRuntimePrompt("delegated-task-failure-recovery");
|
|
18
|
+
export const DELEGATION_ONLY_RECOVERY_INSTRUCTION = readRuntimePrompt("delegation-only-recovery");
|
|
18
19
|
export const INTERNAL_RUNTIME_SPILL_PATH_INSTRUCTION = readRuntimePrompt("internal-runtime-spill-path");
|
|
19
20
|
export const WORKSPACE_RELATIVE_PATH_INSTRUCTION = readRuntimePrompt("workspace-relative-path");
|
|
20
21
|
export function renderDurableMemoryContextPrompt(memoryContext) {
|