@botbotgo/agent-harness 0.0.461 → 0.0.463
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/runtime/adapter/flow/stream-runtime.js +8 -6
- package/dist/runtime/adapter/local-tool-invocation.js +26 -7
- package/dist/runtime/adapter/model/model-providers.js +4 -2
- package/dist/runtime/adapter/tool/tool-arguments.js +35 -3
- package/dist/runtime/agent-runtime-adapter.js +76 -70
- package/dist/runtime/parsing/output-tool-args.d.ts +4 -0
- package/dist/runtime/parsing/output-tool-args.js +38 -2
- 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.463";
|
|
2
2
|
export declare const AGENT_HARNESS_RELEASE_DATE = "2026-05-04";
|
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.463";
|
|
2
2
|
export const AGENT_HARNESS_RELEASE_DATE = "2026-05-04";
|
|
@@ -25,6 +25,8 @@ const RUN_EVIDENCE_AFTER_PREMATURE_PLAN_CLOSE_INSTRUCTION = [
|
|
|
25
25
|
"The required todo board was closed before any non-TODO evidence tool returned.",
|
|
26
26
|
"Do not call write_todos again yet.",
|
|
27
27
|
"Your next action must be exactly one non-TODO evidence tool call selected from the available tool descriptions and schemas.",
|
|
28
|
+
"If the current request or todo board explicitly names one available non-TODO tool, call that named tool.",
|
|
29
|
+
"Do not substitute a neighboring, broader, narrower, or similarly named tool when an exact available tool name is present.",
|
|
28
30
|
"After that evidence tool returns, update the todo board and then provide the final answer required by the agent response format.",
|
|
29
31
|
].join("\n");
|
|
30
32
|
function readPrimaryToolName(tool) {
|
|
@@ -43,11 +45,11 @@ function buildRunEvidenceAfterPlanInstruction(primaryTools) {
|
|
|
43
45
|
`Available non-planning tool names: ${toolNames.join(", ")}.`,
|
|
44
46
|
].join("\n");
|
|
45
47
|
}
|
|
46
|
-
function
|
|
47
|
-
const
|
|
48
|
+
function resolveSingleConfiguredPlanEvidenceTool(primaryTools) {
|
|
49
|
+
const toolNames = primaryTools
|
|
48
50
|
.map(readPrimaryToolName)
|
|
49
|
-
.
|
|
50
|
-
return
|
|
51
|
+
.filter((name) => name.length > 0 && !isPlanToolName(name));
|
|
52
|
+
return toolNames.length === 1 ? [{ name: toolNames[0], args: {}, id: "stream-single-plan-evidence-tool-1" }] : [];
|
|
51
53
|
}
|
|
52
54
|
const INITIAL_REQUIRED_PLAN_INSTRUCTION = [
|
|
53
55
|
"This agent has a required visible planning contract.",
|
|
@@ -704,7 +706,7 @@ export async function* streamRuntimeExecution(options) {
|
|
|
704
706
|
&& hadPriorPlanToolResult
|
|
705
707
|
&& projectedChunks.some((chunk) => chunk.kind === "tool-result" && isPlanToolName(chunk.toolName));
|
|
706
708
|
if (repeatedPlanToolResultBeforeEvidence) {
|
|
707
|
-
earlyStreamExternalPlanEvidenceTools =
|
|
709
|
+
earlyStreamExternalPlanEvidenceTools = resolveSingleConfiguredPlanEvidenceTool(options.primaryTools);
|
|
708
710
|
earlyStreamRecoveryInstruction = buildRunEvidenceAfterPlanInstruction(options.primaryTools);
|
|
709
711
|
earlyStreamRecoverySuppressInitialPlan = true;
|
|
710
712
|
break;
|
|
@@ -750,7 +752,7 @@ export async function* streamRuntimeExecution(options) {
|
|
|
750
752
|
&& (hadPriorPlanToolResult
|
|
751
753
|
|| projectedChunks.some((chunk) => isCompletedPlanToolResultChunk(chunk)))
|
|
752
754
|
&& !sawSuccessfulNonTodoToolResult) {
|
|
753
|
-
earlyStreamExternalPlanEvidenceTools =
|
|
755
|
+
earlyStreamExternalPlanEvidenceTools = resolveSingleConfiguredPlanEvidenceTool(options.primaryTools);
|
|
754
756
|
earlyStreamRecoveryInstruction = buildRunEvidenceAfterPlanInstruction(options.primaryTools);
|
|
755
757
|
earlyStreamRecoverySuppressInitialPlan = true;
|
|
756
758
|
break;
|
|
@@ -13,15 +13,21 @@ const TOOL_FOLLOW_UP_INSTRUCTION = "One or more tool results are already availab
|
|
|
13
13
|
const DEFAULT_MAX_TOOL_ITERATIONS = 10_000;
|
|
14
14
|
const MAX_REPEATED_RECOVERY_WITHOUT_PROGRESS = 2;
|
|
15
15
|
const MAX_REPEATED_PLAN_ONLY_AFTER_PLAN = 2;
|
|
16
|
-
|
|
16
|
+
const REQUIRED_PLAN_CONTRACT_MARKER = "This agent has a required visible planning contract.";
|
|
17
|
+
const INITIAL_WRITE_TODOS_MARKER = "Your first action for this request must be write_todos";
|
|
18
|
+
function resolveSingleBootstrapEvidenceTool(primaryTools) {
|
|
17
19
|
const evidenceTools = primaryTools
|
|
18
20
|
.map((tool) => typeof tool.name === "string" ? tool.name.trim() : "")
|
|
19
21
|
.filter((name) => name.length > 0 && !isPlanToolName(name));
|
|
20
|
-
return evidenceTools.
|
|
22
|
+
return evidenceTools.length === 1 ? evidenceTools[0] : undefined;
|
|
21
23
|
}
|
|
22
24
|
function createBootstrapTodoPlan(primaryTools) {
|
|
23
|
-
const evidenceTool =
|
|
24
|
-
|
|
25
|
+
const evidenceTool = resolveSingleBootstrapEvidenceTool(primaryTools);
|
|
26
|
+
const evidenceToolCount = primaryTools
|
|
27
|
+
.map((tool) => typeof tool.name === "string" ? tool.name.trim() : "")
|
|
28
|
+
.filter((name) => name.length > 0 && !isPlanToolName(name))
|
|
29
|
+
.length;
|
|
30
|
+
if (evidenceToolCount === 0) {
|
|
25
31
|
return [
|
|
26
32
|
{
|
|
27
33
|
content: "Establish the required visible plan for this request",
|
|
@@ -35,7 +41,9 @@ function createBootstrapTodoPlan(primaryTools) {
|
|
|
35
41
|
}
|
|
36
42
|
return [
|
|
37
43
|
{
|
|
38
|
-
content:
|
|
44
|
+
content: evidenceTool
|
|
45
|
+
? `Run the only configured non-planning evidence tool: ${evidenceTool}`
|
|
46
|
+
: "Select and run the appropriate non-planning evidence tool from the declared tool surface",
|
|
39
47
|
status: "in_progress",
|
|
40
48
|
},
|
|
41
49
|
{
|
|
@@ -76,6 +84,15 @@ function buildExternalPlanEvidenceToolResult(tools) {
|
|
|
76
84
|
}],
|
|
77
85
|
};
|
|
78
86
|
}
|
|
87
|
+
function stripSatisfiedInitialPlanInstruction(messages) {
|
|
88
|
+
return messages.filter((message) => {
|
|
89
|
+
const typed = typeof message === "object" && message !== null ? message : {};
|
|
90
|
+
if (typeof typed.content !== "string") {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
return !(typed.content.includes(REQUIRED_PLAN_CONTRACT_MARKER) && typed.content.includes(INITIAL_WRITE_TODOS_MARKER));
|
|
94
|
+
});
|
|
95
|
+
}
|
|
79
96
|
function readPlanStateSummary(output) {
|
|
80
97
|
if (typeof output !== "object" || output === null) {
|
|
81
98
|
return null;
|
|
@@ -217,7 +234,7 @@ function debugLocalToolReplay(input) {
|
|
|
217
234
|
}
|
|
218
235
|
console.error(JSON.stringify({
|
|
219
236
|
type: "local-tool-replay",
|
|
220
|
-
|
|
237
|
+
toolCalls: input.toolCalls.map((toolCall) => ({ name: toolCall.name, args: toolCall.args })),
|
|
221
238
|
resultMessages: summarizeResultMessages(input.result),
|
|
222
239
|
executableToolNames: input.executableToolNames,
|
|
223
240
|
builtinToolNames: input.builtinToolNames,
|
|
@@ -534,7 +551,9 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
|
|
|
534
551
|
executedToolResults,
|
|
535
552
|
};
|
|
536
553
|
}
|
|
537
|
-
currentMessages =
|
|
554
|
+
currentMessages = hasPlanStateEvidence(executedToolResults, externalPlanEvidence)
|
|
555
|
+
? stripSatisfiedInitialPlanInstruction(nextMessages)
|
|
556
|
+
: nextMessages;
|
|
538
557
|
activeRequest = {
|
|
539
558
|
...activeRequest,
|
|
540
559
|
messages: currentMessages,
|
|
@@ -6,7 +6,7 @@ import { ChatOpenAI } from "@langchain/openai";
|
|
|
6
6
|
import { AIMessage } from "langchain";
|
|
7
7
|
import { initChatModel } from "langchain";
|
|
8
8
|
import { salvageToolArgs, tryParseJson } from "../../parsing/output-parsing.js";
|
|
9
|
-
import { salvageJsonToolCalls } from "../../parsing/output-tool-args.js";
|
|
9
|
+
import { normalizeKnownToolArgs, salvageJsonToolCalls } from "../../parsing/output-tool-args.js";
|
|
10
10
|
import { normalizeModelFacingToolSchema } from "../tool/resolved-tool.js";
|
|
11
11
|
import { normalizeOpenAICompatibleInit } from "../compat/openai-compatible.js";
|
|
12
12
|
import { recordPromptedJsonToolCall } from "./prompted-json-tool-call-capture.js";
|
|
@@ -640,7 +640,7 @@ function normalizeParsedToolCall(payload) {
|
|
|
640
640
|
const args = Array.isArray(argsCandidate)
|
|
641
641
|
? { args: argsCandidate }
|
|
642
642
|
: salvageToolArgs(argsCandidate) ?? {};
|
|
643
|
-
return { name, args };
|
|
643
|
+
return { name, args: normalizeKnownToolArgs(name, args) };
|
|
644
644
|
}
|
|
645
645
|
function buildFallbackTodoContents() {
|
|
646
646
|
return [
|
|
@@ -770,6 +770,8 @@ function withPromptedJsonToolPrompt(input, tools, options = {}) {
|
|
|
770
770
|
? [
|
|
771
771
|
"Required evidence tool call:",
|
|
772
772
|
"A todo board already exists. Your next action must be exactly one non-planning tool call chosen from the available tool descriptions and schemas.",
|
|
773
|
+
"If the current request or todo board explicitly names one available non-planning tool, call that named tool.",
|
|
774
|
+
"Do not substitute a neighboring, broader, narrower, or similarly named tool when an exact available tool name is present.",
|
|
773
775
|
"Do not call write_todos or read_todos now.",
|
|
774
776
|
"Do not write prose, markdown, analysis, or a plain-text plan.",
|
|
775
777
|
].join("\n")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { salvageToolArgs } from "../../parsing/output-parsing.js";
|
|
2
|
-
import { salvageJsonToolCalls } from "../../parsing/output-tool-args.js";
|
|
2
|
+
import { normalizeKnownToolArgs, salvageJsonToolCalls, salvageResultLabeledToolCall } from "../../parsing/output-tool-args.js";
|
|
3
3
|
import { isRecord } from "../../../utils/object.js";
|
|
4
4
|
import { extractExplicitResourceReferences, hasExplicitResourceReference } from "../../harness/system/runtime-memory-policy.js";
|
|
5
5
|
import { readCapturedPromptedJsonToolCalls } from "../model/prompted-json-tool-call-capture.js";
|
|
@@ -175,6 +175,29 @@ function mapDelimitedListLikeArgs(args) {
|
|
|
175
175
|
}
|
|
176
176
|
return next;
|
|
177
177
|
}
|
|
178
|
+
function dropDelimitedScalarPathArgs(args, shape) {
|
|
179
|
+
let next = args;
|
|
180
|
+
for (const [key, schemaPart] of Object.entries(shape)) {
|
|
181
|
+
const value = next[key];
|
|
182
|
+
if (typeof value !== "string") {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const normalizedKey = key.trim().toLowerCase();
|
|
186
|
+
if (!/(?:^path$|path$|^filepath$|^targetpath$)/u.test(normalizedKey)) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (schemaPartExpectsArray(schemaPart)) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const raw = value.trim();
|
|
193
|
+
if (!/[,;\n]/u.test(raw)) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const { [key]: _dropped, ...rest } = next;
|
|
197
|
+
next = rest;
|
|
198
|
+
}
|
|
199
|
+
return next;
|
|
200
|
+
}
|
|
178
201
|
export function normalizeToolArgsForSchema(args, schema, rawArgsInput, options = {}) {
|
|
179
202
|
const schemaDef = isObject(schema) ? schema._def : undefined;
|
|
180
203
|
const zodShape = schemaDef
|
|
@@ -191,7 +214,7 @@ export function normalizeToolArgsForSchema(args, schema, rawArgsInput, options =
|
|
|
191
214
|
if (!shape || !isRecord(shape)) {
|
|
192
215
|
return mapDelimitedListLikeArgs(args);
|
|
193
216
|
}
|
|
194
|
-
const aliasMappedArgs = mapStringArrayFields(mapCommonArgumentAliases(args, shape), shape);
|
|
217
|
+
const aliasMappedArgs = dropDelimitedScalarPathArgs(mapStringArrayFields(mapCommonArgumentAliases(args, shape), shape), shape);
|
|
195
218
|
const keys = Object.keys(shape);
|
|
196
219
|
if (keys.length !== 1) {
|
|
197
220
|
return fillLatestUserInputForQueryLikeFields(aliasMappedArgs, shape, options.latestUserInput);
|
|
@@ -270,7 +293,7 @@ export function extractToolCallsFromResult(result) {
|
|
|
270
293
|
if (id && answeredToolCallIds.has(id)) {
|
|
271
294
|
return null;
|
|
272
295
|
}
|
|
273
|
-
return { id, name, args: rawArgs, rawArgsInput };
|
|
296
|
+
return { id, name, args: normalizeKnownToolArgs(name, rawArgs), rawArgsInput };
|
|
274
297
|
})
|
|
275
298
|
.filter((item) => item !== null);
|
|
276
299
|
if (extracted.length > 0) {
|
|
@@ -296,6 +319,15 @@ export function extractToolCallsFromResult(result) {
|
|
|
296
319
|
if (!content.trim()) {
|
|
297
320
|
continue;
|
|
298
321
|
}
|
|
322
|
+
const resultLabeledToolCall = salvageResultLabeledToolCall(content);
|
|
323
|
+
if (resultLabeledToolCall) {
|
|
324
|
+
return [{
|
|
325
|
+
id: "salvaged-result-label-1",
|
|
326
|
+
name: resultLabeledToolCall.name,
|
|
327
|
+
args: resultLabeledToolCall.args,
|
|
328
|
+
rawArgsInput: content,
|
|
329
|
+
}];
|
|
330
|
+
}
|
|
299
331
|
const salvaged = salvageJsonToolCalls(content);
|
|
300
332
|
if (salvaged.length > 0) {
|
|
301
333
|
return salvaged.map((toolCall, salvageIndex) => ({
|
|
@@ -7,7 +7,6 @@ import { salvageJsonToolCalls } from "./parsing/output-tool-args.js";
|
|
|
7
7
|
import { extractMessageText } from "../utils/message-content.js";
|
|
8
8
|
import { AGENT_INTERRUPT_SENTINEL_PREFIX, buildDeepAgentCreateParams, buildDeepAgentSystemPromptWithCapabilityCatalog, buildLangChainCreateParams, DEFAULT_DEEPAGENT_RECURSION_LIMIT, materializeModelExposedBuiltinMiddlewareTools, resolveLangChainInvocationConfig, resolveRunnableCheckpointer, resolveRunnableInterruptOn, shouldAttachDeepAgentBackend, shouldAttachDeepAgentCheckpointer, shouldAttachDeepAgentStore, } from "./agent-runtime-assembly.js";
|
|
9
9
|
import { resolveDeepAgentSkillSourcePaths, resolveDeepAgentSkillSourceRootPaths, } from "./adapter/compat/deepagent-compat.js";
|
|
10
|
-
import { EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION } from "./prompts/runtime-prompts.js";
|
|
11
10
|
import { buildToolNameMapping, } from "./adapter/tool/tool-name-mapping.js";
|
|
12
11
|
import { executeRequestInvocation } from "./adapter/flow/invocation-flow.js";
|
|
13
12
|
import { streamRuntimeExecution } from "./adapter/flow/stream-runtime.js";
|
|
@@ -25,16 +24,6 @@ export { buildAuthOmittingFetch, normalizeOpenAICompatibleInit } from "./adapter
|
|
|
25
24
|
export { buildToolNameMapping, createModelFacingToolNameCandidates, createModelFacingToolNameLookupCandidates, resolveModelFacingToolName, sanitizeToolNameForModel, } from "./adapter/tool/tool-name-mapping.js";
|
|
26
25
|
export { computeRemainingTimeoutMs, isRetryableProviderError, resolveBindingTimeout, resolveProviderRetryPolicy, resolveStreamIdleTimeout, resolveTimeoutMs, } from "./adapter/resilience.js";
|
|
27
26
|
import { getBindingAdapterKind, getBindingBuiltinToolsConfig, getBindingDeepAgentSubagents, getBindingExecutionParams, getBindingExecutionKind, getBindingFilesystemConfig, getBindingMemorySources, getBindingPrimaryModel, getBindingSkills, getBindingSubagents, getBindingToolCount, getBindingPrimaryTools, getBindingSystemPrompt, isDeepAgentBinding, isLangChainBinding, } from "./support/compiled-binding.js";
|
|
28
|
-
function hasDelegatedExecutionToolEvidence(result) {
|
|
29
|
-
const executedToolResults = Array.isArray(result.metadata?.executedToolResults)
|
|
30
|
-
? result.metadata.executedToolResults
|
|
31
|
-
: [];
|
|
32
|
-
return executedToolResults.some((toolResult) => (toolResult.isError !== true
|
|
33
|
-
&& !isPlanToolName(toolResult.toolName)));
|
|
34
|
-
}
|
|
35
|
-
function hasRequiredDelegatedExecutionToolEvidence(result) {
|
|
36
|
-
return hasDelegatedExecutionToolEvidence(result);
|
|
37
|
-
}
|
|
38
27
|
function buildDelegatedPlanEvidenceBlocker(agentId) {
|
|
39
28
|
return JSON.stringify({
|
|
40
29
|
status: "blocked",
|
|
@@ -50,30 +39,6 @@ function buildDelegatedPlanEvidenceBlocker(agentId) {
|
|
|
50
39
|
report: `routing delegated to ${agentId}; todoTrace ${agentId}: TODO evidence missing; stepResults blocked; summary missing planning evidence; findings require retry; blockers missing TODO planning evidence; nextActions inspect delegated model/tool behavior; report task delegated to ${agentId}.`,
|
|
51
40
|
});
|
|
52
41
|
}
|
|
53
|
-
function buildDelegatedExecutionEvidenceBlocker(agentId, expectedToolNames = []) {
|
|
54
|
-
const expectedTools = expectedToolNames.length > 0 ? expectedToolNames.join(", ") : "configured non-planning tools";
|
|
55
|
-
return JSON.stringify({
|
|
56
|
-
status: "blocked",
|
|
57
|
-
routing: [`delegated agent ${agentId}`],
|
|
58
|
-
plan: ["delegate to specialist", "require non-planning tool evidence", "return blocker when evidence is absent"],
|
|
59
|
-
execution: [
|
|
60
|
-
`task delegated to ${agentId}`,
|
|
61
|
-
`expected evidence tools: ${expectedTools}`,
|
|
62
|
-
`delegated agent ${agentId} did not return any non-planning tool evidence after retry`,
|
|
63
|
-
],
|
|
64
|
-
todoTrace: [`${agentId}: TODO evidence observed; delegated planning board did not produce completed non-planning evidence.`],
|
|
65
|
-
stepResults: ["delegated execution evidence was not observed"],
|
|
66
|
-
summary: [`Delegated agent ${agentId} did not return any non-planning tool evidence after retry.`],
|
|
67
|
-
findings: [
|
|
68
|
-
`Expected evidence tools from configuration: ${expectedTools}.`,
|
|
69
|
-
"The TODO board alone is not execution evidence.",
|
|
70
|
-
"The framework cannot mark the delegated task complete without a non-planning tool result or an explicit blocker from that tool path.",
|
|
71
|
-
],
|
|
72
|
-
blockers: ["missing delegated non-planning tool evidence"],
|
|
73
|
-
nextActions: ["Retry the request or inspect the delegated agent's model/tool-call behavior."],
|
|
74
|
-
report: `routing delegated to ${agentId}; todoTrace ${agentId}: TODO evidence observed but non-planning evidence missing; stepResults blocked; summary missing non-planning tool evidence; findings expected evidence tools ${expectedTools}; blockers missing execution evidence; nextActions inspect delegated model/tool behavior; report task delegated to ${agentId}.`,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
42
|
function normalizePlanToolName(toolName) {
|
|
78
43
|
return typeof toolName === "string" ? toolName.trim().toLowerCase().replace(/[\s-]+/gu, "_") : "";
|
|
79
44
|
}
|
|
@@ -282,6 +247,32 @@ function hasDelegatedPlanEvidence(result) {
|
|
|
282
247
|
return Array.isArray(toolResults)
|
|
283
248
|
&& toolResults.some((item) => isPlanToolName(item.toolName));
|
|
284
249
|
}
|
|
250
|
+
function hasIncompleteDelegatedTodos(value) {
|
|
251
|
+
if (Array.isArray(value)) {
|
|
252
|
+
return value.some((item) => hasIncompleteDelegatedTodos(item));
|
|
253
|
+
}
|
|
254
|
+
if (typeof value !== "object" || value === null) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
const record = value;
|
|
258
|
+
const status = typeof record.status === "string" ? record.status.trim().toLowerCase() : "";
|
|
259
|
+
if (status === "pending" || status === "in_progress") {
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
return hasIncompleteDelegatedTodos(record.todos)
|
|
263
|
+
|| hasIncompleteDelegatedTodos(record.update)
|
|
264
|
+
|| hasIncompleteDelegatedTodos(record.stateSnapshot)
|
|
265
|
+
|| hasIncompleteDelegatedTodos(record.metadata);
|
|
266
|
+
}
|
|
267
|
+
function hasIncompleteDelegatedPlanState(result) {
|
|
268
|
+
const toolResults = result?.metadata?.executedToolResults;
|
|
269
|
+
return Array.isArray(toolResults)
|
|
270
|
+
&& toolResults.some((item) => isPlanToolName(item.toolName) && hasIncompleteDelegatedTodos(item.output));
|
|
271
|
+
}
|
|
272
|
+
function needsDelegatedPlanRecovery(binding, result) {
|
|
273
|
+
return binding?.harnessRuntime.executionContract?.requiresPlan === true
|
|
274
|
+
&& (!hasDelegatedPlanEvidence(result) || hasIncompleteDelegatedPlanState(result));
|
|
275
|
+
}
|
|
285
276
|
function readUpstreamToolEvidence(event) {
|
|
286
277
|
if (typeof event !== "object" || event === null) {
|
|
287
278
|
return null;
|
|
@@ -342,6 +333,12 @@ const DELEGATED_PLAN_EVIDENCE_RETRY_INSTRUCTION = [
|
|
|
342
333
|
"Before any other tool call or final answer, call write_todos with concrete task steps and statuses.",
|
|
343
334
|
"Then continue the task to completion, update TODO statuses after evidence steps, and close every TODO as completed or failed before the final answer.",
|
|
344
335
|
].join("\n");
|
|
336
|
+
const DELEGATED_PLAN_EVIDENCE_FINAL_RETRY_INSTRUCTION = [
|
|
337
|
+
"The delegated task still has no visible TODO planning evidence.",
|
|
338
|
+
"Use the actual write_todos tool interface now. Do not print JSON, markdown, or a tool-call transcript as text.",
|
|
339
|
+
"The next runtime event must be the write_todos tool call result, not an assistant message describing the call.",
|
|
340
|
+
"After write_todos succeeds, continue the delegated task and close every TODO as completed or failed.",
|
|
341
|
+
].join("\n");
|
|
345
342
|
function looksLikeRawCommandTranscript(value) {
|
|
346
343
|
const normalized = value.trim();
|
|
347
344
|
return /^(?:stdout|stderr)\s*:/iu.test(normalized)
|
|
@@ -868,6 +865,28 @@ export class AgentRuntimeAdapter {
|
|
|
868
865
|
const inlineSubagents = input.resolvedSubagents.filter((subagent) => !("graphId" in subagent));
|
|
869
866
|
const asyncSubagents = input.resolvedSubagents.filter((subagent) => "graphId" in subagent);
|
|
870
867
|
const subagents = inlineSubagents;
|
|
868
|
+
const subagentDefaultMiddleware = [
|
|
869
|
+
...(builtinTools.todos === false ? [] : [todoListMiddleware()]),
|
|
870
|
+
...(builtinTools.filesystem === false ? [] : [createFilesystemMiddleware({ backend })]),
|
|
871
|
+
createSummarizationMiddleware({
|
|
872
|
+
model: input.resolvedModel,
|
|
873
|
+
backend,
|
|
874
|
+
}),
|
|
875
|
+
createPatchToolCallsMiddleware(),
|
|
876
|
+
];
|
|
877
|
+
const generalPurposeMiddleware = [
|
|
878
|
+
...subagentDefaultMiddleware,
|
|
879
|
+
...(input.resolvedSkills.length > 0 ? [createSkillsMiddleware({
|
|
880
|
+
backend,
|
|
881
|
+
sources: resolveDeepAgentSkillSourceRootPaths({
|
|
882
|
+
workspaceRoot: binding.harnessRuntime.workspaceRoot,
|
|
883
|
+
runtimeRoot: binding.harnessRuntime.runtimeRoot,
|
|
884
|
+
ownerId: binding.agent.id,
|
|
885
|
+
skillPaths: input.resolvedSkills,
|
|
886
|
+
}) ?? input.resolvedSkills,
|
|
887
|
+
})] : []),
|
|
888
|
+
];
|
|
889
|
+
const hasGeneralPurposeOverride = subagents.some((subagent) => subagent.name === "general-purpose");
|
|
871
890
|
const middleware = [
|
|
872
891
|
...(builtinTools.todos === false ? [] : [todoListMiddleware()]),
|
|
873
892
|
...(input.resolvedSkills.length > 0 ? [createSkillsMiddleware({
|
|
@@ -880,15 +899,15 @@ export class AgentRuntimeAdapter {
|
|
|
880
899
|
}) ?? input.resolvedSkills,
|
|
881
900
|
})] : []),
|
|
882
901
|
...(builtinTools.filesystem === false ? [] : [createFilesystemMiddleware({ backend })]),
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
902
|
+
createSubAgentMiddleware({
|
|
903
|
+
defaultModel: input.resolvedModel,
|
|
904
|
+
defaultTools: input.resolvedTools,
|
|
905
|
+
defaultMiddleware: subagentDefaultMiddleware,
|
|
906
|
+
generalPurposeMiddleware: generalPurposeMiddleware,
|
|
907
|
+
defaultInterruptOn: input.resolvedInterruptOn,
|
|
908
|
+
subagents: subagents,
|
|
909
|
+
generalPurposeAgent: !hasGeneralPurposeOverride,
|
|
910
|
+
}),
|
|
892
911
|
createSummarizationMiddleware({
|
|
893
912
|
model: input.resolvedModel,
|
|
894
913
|
backend,
|
|
@@ -1224,8 +1243,7 @@ export class AgentRuntimeAdapter {
|
|
|
1224
1243
|
};
|
|
1225
1244
|
}
|
|
1226
1245
|
}
|
|
1227
|
-
if (selectedBinding
|
|
1228
|
-
&& !hasDelegatedPlanEvidence(delegatedResult)) {
|
|
1246
|
+
if (needsDelegatedPlanRecovery(selectedBinding, delegatedResult)) {
|
|
1229
1247
|
try {
|
|
1230
1248
|
delegatedResult = await runDelegatedRequest([requestText, DELEGATED_PLAN_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":plan-evidence-retry");
|
|
1231
1249
|
}
|
|
@@ -1243,8 +1261,7 @@ export class AgentRuntimeAdapter {
|
|
|
1243
1261
|
};
|
|
1244
1262
|
}
|
|
1245
1263
|
}
|
|
1246
|
-
if (selectedBinding
|
|
1247
|
-
&& !hasDelegatedPlanEvidence(delegatedResult)) {
|
|
1264
|
+
if (needsDelegatedPlanRecovery(selectedBinding, delegatedResult)) {
|
|
1248
1265
|
const output = buildDelegatedPlanEvidenceBlocker(selectedBinding.agent.id);
|
|
1249
1266
|
return {
|
|
1250
1267
|
toolOutput: output,
|
|
@@ -1635,12 +1652,16 @@ export class AgentRuntimeAdapter {
|
|
|
1635
1652
|
agentId: selectedBinding?.agent.id ?? planned.subagentType,
|
|
1636
1653
|
};
|
|
1637
1654
|
let delegatedResult = yield* runPlannedDelegation(planned.subagentType, delegatedText);
|
|
1638
|
-
if (selectedBinding
|
|
1655
|
+
if (needsDelegatedPlanRecovery(selectedBinding, delegatedResult)) {
|
|
1639
1656
|
const previousDelegatedResult = delegatedResult;
|
|
1640
1657
|
delegatedResult = mergeDelegatedResultToolEvidence(yield* runPlannedDelegation(planned.subagentType, [delegatedText, DELEGATED_PLAN_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":plan-evidence-retry"), previousDelegatedResult);
|
|
1641
1658
|
}
|
|
1642
|
-
if (selectedBinding
|
|
1643
|
-
const
|
|
1659
|
+
if (needsDelegatedPlanRecovery(selectedBinding, delegatedResult)) {
|
|
1660
|
+
const previousDelegatedResult = delegatedResult;
|
|
1661
|
+
delegatedResult = mergeDelegatedResultToolEvidence(yield* runPlannedDelegation(planned.subagentType, [delegatedText, DELEGATED_PLAN_EVIDENCE_FINAL_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":plan-evidence-final-retry"), previousDelegatedResult);
|
|
1662
|
+
}
|
|
1663
|
+
if (needsDelegatedPlanRecovery(selectedBinding, delegatedResult)) {
|
|
1664
|
+
const output = buildDelegatedPlanEvidenceBlocker(selectedBinding?.agent.id ?? planned.subagentType);
|
|
1644
1665
|
delegatedResult = {
|
|
1645
1666
|
...delegatedResult,
|
|
1646
1667
|
state: "failed",
|
|
@@ -1832,21 +1853,15 @@ export class AgentRuntimeAdapter {
|
|
|
1832
1853
|
originalRequest: requestText,
|
|
1833
1854
|
});
|
|
1834
1855
|
let delegatedResult = yield* runDelegatedStreamAttempt(delegatedText);
|
|
1835
|
-
|
|
1836
|
-
if (selectedBinding.harnessRuntime.executionContract?.requiresPlan === true
|
|
1837
|
-
&& !hasDelegatedPlanEvidence(delegatedResult)) {
|
|
1856
|
+
if (needsDelegatedPlanRecovery(selectedBinding, delegatedResult)) {
|
|
1838
1857
|
const previousDelegatedResult = delegatedResult;
|
|
1839
1858
|
delegatedResult = mergeDelegatedResultToolEvidence(yield* runDelegatedStreamAttempt([delegatedText, DELEGATED_PLAN_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":plan-evidence-retry"), previousDelegatedResult);
|
|
1840
1859
|
}
|
|
1841
|
-
if (
|
|
1860
|
+
if (needsDelegatedPlanRecovery(selectedBinding, delegatedResult)) {
|
|
1842
1861
|
const previousDelegatedResult = delegatedResult;
|
|
1843
|
-
delegatedResult = mergeDelegatedResultToolEvidence(yield* runDelegatedStreamAttempt([
|
|
1844
|
-
delegatedText,
|
|
1845
|
-
EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION,
|
|
1846
|
-
].filter(Boolean).join("\n\n"), ":tool-evidence-retry"), previousDelegatedResult);
|
|
1862
|
+
delegatedResult = mergeDelegatedResultToolEvidence(yield* runDelegatedStreamAttempt([delegatedText, DELEGATED_PLAN_EVIDENCE_FINAL_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":plan-evidence-final-retry"), previousDelegatedResult);
|
|
1847
1863
|
}
|
|
1848
|
-
if (selectedBinding
|
|
1849
|
-
&& !hasDelegatedPlanEvidence(delegatedResult)) {
|
|
1864
|
+
if (needsDelegatedPlanRecovery(selectedBinding, delegatedResult)) {
|
|
1850
1865
|
const output = buildDelegatedPlanEvidenceBlocker(selectedBinding.agent.id);
|
|
1851
1866
|
delegatedResult = {
|
|
1852
1867
|
...delegatedResult,
|
|
@@ -1855,15 +1870,6 @@ export class AgentRuntimeAdapter {
|
|
|
1855
1870
|
finalMessageText: output,
|
|
1856
1871
|
};
|
|
1857
1872
|
}
|
|
1858
|
-
if (targetRequiresExecutionToolEvidence && !hasRequiredDelegatedExecutionToolEvidence(delegatedResult)) {
|
|
1859
|
-
const output = buildDelegatedExecutionEvidenceBlocker(selectedBinding.agent.id, getBindingPrimaryTools(selectedBinding).map((tool) => tool.name));
|
|
1860
|
-
delegatedResult = {
|
|
1861
|
-
...delegatedResult,
|
|
1862
|
-
state: "failed",
|
|
1863
|
-
output,
|
|
1864
|
-
finalMessageText: output,
|
|
1865
|
-
};
|
|
1866
|
-
}
|
|
1867
1873
|
const delegatedToolResults = Array.isArray(delegatedResult.metadata?.executedToolResults)
|
|
1868
1874
|
? delegatedResult.metadata.executedToolResults
|
|
1869
1875
|
: [];
|
|
@@ -7,6 +7,10 @@ export declare function salvageLabeledToolCall(value: unknown): {
|
|
|
7
7
|
name: string;
|
|
8
8
|
args: Record<string, unknown>;
|
|
9
9
|
} | null;
|
|
10
|
+
export declare function salvageResultLabeledToolCall(value: unknown): {
|
|
11
|
+
name: string;
|
|
12
|
+
args: Record<string, unknown>;
|
|
13
|
+
} | null;
|
|
10
14
|
export declare function salvageToolArgs(value: unknown): Record<string, unknown> | null;
|
|
11
15
|
export declare function salvageJsonToolCalls(value: unknown): Array<{
|
|
12
16
|
name: string;
|
|
@@ -179,6 +179,22 @@ export function salvageLabeledToolCall(value) {
|
|
|
179
179
|
}
|
|
180
180
|
return null;
|
|
181
181
|
}
|
|
182
|
+
export function salvageResultLabeledToolCall(value) {
|
|
183
|
+
if (typeof value !== "string") {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
const lines = value
|
|
187
|
+
.split("\n")
|
|
188
|
+
.map((line) => line.trim())
|
|
189
|
+
.filter(Boolean);
|
|
190
|
+
const label = lines[0]?.replace(/[*`#]/gu, "").trim() ?? "";
|
|
191
|
+
const match = /^([A-Za-z_][A-Za-z0-9_]*)\s+result\b/iu.exec(label);
|
|
192
|
+
if (!match || !isToolName(match[1])) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const args = salvageToolArgs(lines.slice(1).join("\n")) ?? {};
|
|
196
|
+
return { name: match[1], args: normalizeKnownToolArgs(match[1], args) };
|
|
197
|
+
}
|
|
182
198
|
function extractBalancedJsonValue(value, openChar, closeChar) {
|
|
183
199
|
const start = value.indexOf(openChar);
|
|
184
200
|
if (start < 0)
|
|
@@ -514,12 +530,26 @@ function normalizeWriteTodosArgs(args) {
|
|
|
514
530
|
if (Array.isArray(args.items) && !Array.isArray(args.todos)) {
|
|
515
531
|
return normalizeWriteTodosArgs({ ...args, todos: args.items });
|
|
516
532
|
}
|
|
533
|
+
if (Array.isArray(args.tasks) && !Array.isArray(args.todos)) {
|
|
534
|
+
return normalizeWriteTodosArgs({ ...args, todos: args.tasks });
|
|
535
|
+
}
|
|
536
|
+
if (Array.isArray(args.todo) && !Array.isArray(args.todos)) {
|
|
537
|
+
return normalizeWriteTodosArgs({ ...args, todos: args.todo });
|
|
538
|
+
}
|
|
517
539
|
if (!Array.isArray(args.todos)) {
|
|
518
540
|
return args;
|
|
519
541
|
}
|
|
542
|
+
const { items: _items, tasks: _tasks, todo: _todo, ...rest } = args;
|
|
520
543
|
return {
|
|
521
|
-
...
|
|
544
|
+
...rest,
|
|
522
545
|
todos: args.todos.map((todo, index) => {
|
|
546
|
+
if (typeof todo === "string") {
|
|
547
|
+
const content = todo.trim();
|
|
548
|
+
return {
|
|
549
|
+
content: content.length > 0 ? content : `Step ${index + 1}`,
|
|
550
|
+
status: index === 0 ? "in_progress" : "pending",
|
|
551
|
+
};
|
|
552
|
+
}
|
|
523
553
|
if (typeof todo !== "object" || !todo || Array.isArray(todo)) {
|
|
524
554
|
return todo;
|
|
525
555
|
}
|
|
@@ -534,7 +564,13 @@ function normalizeWriteTodosArgs(args) {
|
|
|
534
564
|
? record.name
|
|
535
565
|
: typeof record.text === "string" && record.text.trim().length > 0
|
|
536
566
|
? record.text
|
|
537
|
-
:
|
|
567
|
+
: typeof record.task === "string" && record.task.trim().length > 0
|
|
568
|
+
? record.task
|
|
569
|
+
: typeof record.action === "string" && record.action.trim().length > 0
|
|
570
|
+
? record.action
|
|
571
|
+
: typeof record.step === "string" && record.step.trim().length > 0
|
|
572
|
+
? record.step
|
|
573
|
+
: `Step ${index + 1}`;
|
|
538
574
|
const normalized = {};
|
|
539
575
|
if (content !== undefined)
|
|
540
576
|
normalized.content = content;
|