@botbotgo/agent-harness 0.0.454 → 0.0.456
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.
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.456";
|
|
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.456";
|
|
2
2
|
export const AGENT_HARNESS_RELEASE_DATE = "2026-05-04";
|
|
@@ -43,6 +43,12 @@ function buildRunEvidenceAfterPlanInstruction(primaryTools) {
|
|
|
43
43
|
`Available non-planning tool names: ${toolNames.join(", ")}.`,
|
|
44
44
|
].join("\n");
|
|
45
45
|
}
|
|
46
|
+
function resolveConfiguredPlanEvidenceTool(primaryTools) {
|
|
47
|
+
const toolName = primaryTools
|
|
48
|
+
.map(readPrimaryToolName)
|
|
49
|
+
.find((name) => name.length > 0 && !isPlanToolName(name));
|
|
50
|
+
return toolName ? [{ name: toolName, args: {}, id: "stream-configured-plan-evidence-tool-1" }] : [];
|
|
51
|
+
}
|
|
46
52
|
const INITIAL_REQUIRED_PLAN_INSTRUCTION = [
|
|
47
53
|
"This agent has a required visible planning contract.",
|
|
48
54
|
"Your first action for this request must be write_todos with concrete task steps and statuses.",
|
|
@@ -129,135 +135,6 @@ function hasIncompletePlanOutput(value) {
|
|
|
129
135
|
}
|
|
130
136
|
return null;
|
|
131
137
|
}
|
|
132
|
-
function extractInProgressTodoContents(value) {
|
|
133
|
-
if (typeof value !== "object" || value === null) {
|
|
134
|
-
return [];
|
|
135
|
-
}
|
|
136
|
-
const typed = value;
|
|
137
|
-
const arrays = [typed.todos, typed.items];
|
|
138
|
-
const contents = [];
|
|
139
|
-
for (const candidate of arrays) {
|
|
140
|
-
if (!Array.isArray(candidate)) {
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
for (const todo of candidate) {
|
|
144
|
-
if (typeof todo !== "object" || todo === null) {
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
const item = todo;
|
|
148
|
-
if (item.status !== "in_progress") {
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
const content = [item.content, item.description, item.title, item.name, item.text]
|
|
152
|
-
.find((value) => typeof value === "string" && value.trim().length > 0);
|
|
153
|
-
if (content) {
|
|
154
|
-
contents.push(content.trim());
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
for (const nested of [typed.summary, typed.update, typed.data, typed.output]) {
|
|
159
|
-
contents.push(...extractInProgressTodoContents(nested));
|
|
160
|
-
}
|
|
161
|
-
return [...new Set(contents)];
|
|
162
|
-
}
|
|
163
|
-
function extractTodoContentsForEvidenceResolution(value) {
|
|
164
|
-
const inProgress = extractTodoContentsByStatus(value, "in_progress");
|
|
165
|
-
if (inProgress.length > 0) {
|
|
166
|
-
return inProgress;
|
|
167
|
-
}
|
|
168
|
-
return extractTodoContentsByStatus(value, "pending");
|
|
169
|
-
}
|
|
170
|
-
function extractTodoContentsByStatus(value, status) {
|
|
171
|
-
if (Array.isArray(value)) {
|
|
172
|
-
return value.flatMap((item) => extractTodoContentsByStatus(item, status));
|
|
173
|
-
}
|
|
174
|
-
if (typeof value !== "object" || value === null) {
|
|
175
|
-
return [];
|
|
176
|
-
}
|
|
177
|
-
const typed = value;
|
|
178
|
-
const arrays = [typed.todos, typed.items];
|
|
179
|
-
const contents = [];
|
|
180
|
-
for (const array of arrays) {
|
|
181
|
-
if (!Array.isArray(array)) {
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
for (const item of array) {
|
|
185
|
-
if (typeof item !== "object" || item === null) {
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
const typedItem = item;
|
|
189
|
-
if (typedItem.status !== status) {
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
const content = [
|
|
193
|
-
typedItem.content,
|
|
194
|
-
typedItem.description,
|
|
195
|
-
typedItem.title,
|
|
196
|
-
typedItem.name,
|
|
197
|
-
typedItem.text,
|
|
198
|
-
].find((value) => typeof value === "string" && value.trim().length > 0);
|
|
199
|
-
if (content) {
|
|
200
|
-
contents.push(content.trim());
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
for (const nested of [typed.summary, typed.update, typed.data, typed.output]) {
|
|
205
|
-
contents.push(...extractTodoContentsByStatus(nested, status));
|
|
206
|
-
}
|
|
207
|
-
return [...new Set(contents)];
|
|
208
|
-
}
|
|
209
|
-
function buildRunCommittedTodoEvidenceInstruction(primaryTools, planToolOutput) {
|
|
210
|
-
const todoContents = extractInProgressTodoContents(planToolOutput);
|
|
211
|
-
const todoText = todoContents.length > 0
|
|
212
|
-
? todoContents.map((content, index) => `${index + 1}. ${content}`).join("\n")
|
|
213
|
-
: "(no in-progress todo content was readable)";
|
|
214
|
-
return [
|
|
215
|
-
buildRunEvidenceAfterPlanInstruction(primaryTools),
|
|
216
|
-
"",
|
|
217
|
-
"The completed write_todos result contains these in-progress evidence commitments:",
|
|
218
|
-
todoText,
|
|
219
|
-
"",
|
|
220
|
-
"Your next action must execute the non-planning tool named by the in-progress TODO. If every TODO is pending, execute the non-planning tool named by the first pending TODO. Do not call write_todos or read_todos again before that evidence tool returns.",
|
|
221
|
-
].join("\n");
|
|
222
|
-
}
|
|
223
|
-
function resolveCommittedPlanEvidenceTool(primaryTools, planToolOutput) {
|
|
224
|
-
return resolveCommittedPlanEvidenceTools(primaryTools, planToolOutput)[0];
|
|
225
|
-
}
|
|
226
|
-
function resolveCommittedPlanEvidenceTools(primaryTools, planToolOutput) {
|
|
227
|
-
const availableToolNames = primaryTools
|
|
228
|
-
.map(readPrimaryToolName)
|
|
229
|
-
.filter((name) => name.length > 0 && !isPlanToolName(name));
|
|
230
|
-
const todoContents = extractTodoContentsForEvidenceResolution(planToolOutput);
|
|
231
|
-
if (todoContents.length === 0) {
|
|
232
|
-
return [];
|
|
233
|
-
}
|
|
234
|
-
const resolved = [];
|
|
235
|
-
const seen = new Set();
|
|
236
|
-
for (const content of todoContents) {
|
|
237
|
-
const todoText = content.toLowerCase();
|
|
238
|
-
const selectedNames = availableToolNames.filter((name) => hasExplicitToolNameReference(todoText, name));
|
|
239
|
-
for (const selectedName of selectedNames) {
|
|
240
|
-
if (seen.has(selectedName)) {
|
|
241
|
-
continue;
|
|
242
|
-
}
|
|
243
|
-
seen.add(selectedName);
|
|
244
|
-
resolved.push({
|
|
245
|
-
name: selectedName,
|
|
246
|
-
args: {},
|
|
247
|
-
id: `stream-committed-plan-evidence-tool-${resolved.length + 1}`,
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return resolved;
|
|
252
|
-
}
|
|
253
|
-
function hasExplicitToolNameReference(todoText, toolName) {
|
|
254
|
-
const normalizedToolName = toolName.trim().toLowerCase();
|
|
255
|
-
if (!normalizedToolName) {
|
|
256
|
-
return false;
|
|
257
|
-
}
|
|
258
|
-
const escapedName = normalizedToolName.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
259
|
-
return new RegExp(`(?:^|[^\\p{L}\\p{N}_-])${escapedName}(?:$|[^\\p{L}\\p{N}_-])`, "iu").test(todoText);
|
|
260
|
-
}
|
|
261
138
|
function normalizePlanToolName(toolName) {
|
|
262
139
|
return typeof toolName === "string" ? toolName.trim().toLowerCase().replace(/[\s-]+/gu, "_") : "";
|
|
263
140
|
}
|
|
@@ -827,9 +704,8 @@ export async function* streamRuntimeExecution(options) {
|
|
|
827
704
|
&& hadPriorPlanToolResult
|
|
828
705
|
&& projectedChunks.some((chunk) => chunk.kind === "tool-result" && isPlanToolName(chunk.toolName));
|
|
829
706
|
if (repeatedPlanToolResultBeforeEvidence) {
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
earlyStreamRecoveryInstruction = buildRunCommittedTodoEvidenceInstruction(options.primaryTools, planToolResult?.kind === "tool-result" ? planToolResult.output : undefined);
|
|
707
|
+
earlyStreamExternalPlanEvidenceTools = resolveConfiguredPlanEvidenceTool(options.primaryTools);
|
|
708
|
+
earlyStreamRecoveryInstruction = buildRunEvidenceAfterPlanInstruction(options.primaryTools);
|
|
833
709
|
earlyStreamRecoverySuppressInitialPlan = true;
|
|
834
710
|
break;
|
|
835
711
|
}
|
|
@@ -874,9 +750,8 @@ export async function* streamRuntimeExecution(options) {
|
|
|
874
750
|
&& (hadPriorPlanToolResult
|
|
875
751
|
|| projectedChunks.some((chunk) => isCompletedPlanToolResultChunk(chunk)))
|
|
876
752
|
&& !sawSuccessfulNonTodoToolResult) {
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
earlyStreamRecoveryInstruction = buildRunCommittedTodoEvidenceInstruction(options.primaryTools, planToolResult?.kind === "tool-result" ? planToolResult.output : undefined);
|
|
753
|
+
earlyStreamExternalPlanEvidenceTools = resolveConfiguredPlanEvidenceTool(options.primaryTools);
|
|
754
|
+
earlyStreamRecoveryInstruction = buildRunEvidenceAfterPlanInstruction(options.primaryTools);
|
|
880
755
|
earlyStreamRecoverySuppressInitialPlan = true;
|
|
881
756
|
break;
|
|
882
757
|
}
|
|
@@ -196,58 +196,6 @@ function terminalToolErrorRecoveryInstruction(terminalText) {
|
|
|
196
196
|
function requiresPlanEvidence(binding) {
|
|
197
197
|
return binding.harnessRuntime.executionContract?.requiresPlan === true;
|
|
198
198
|
}
|
|
199
|
-
function escapeRegExp(value) {
|
|
200
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
201
|
-
}
|
|
202
|
-
function textExplicitlyNamesTool(text, toolName) {
|
|
203
|
-
const trimmedToolName = toolName.trim();
|
|
204
|
-
if (trimmedToolName.length === 0) {
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
return new RegExp(`(?:^|[^\\p{L}\\p{N}_-])${escapeRegExp(trimmedToolName)}(?:$|[^\\p{L}\\p{N}_-])`, "iu").test(text);
|
|
208
|
-
}
|
|
209
|
-
function resolveCommittedTodoEvidenceTool(executedToolResults, primaryTools) {
|
|
210
|
-
const availableTools = primaryTools
|
|
211
|
-
.filter((tool) => typeof tool.name === "string" && tool.name.length > 0 && !isPlanToolName(tool.name));
|
|
212
|
-
if (availableTools.length === 0) {
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
for (let index = executedToolResults.length - 1; index >= 0; index -= 1) {
|
|
216
|
-
const result = executedToolResults[index];
|
|
217
|
-
if (!result || result.isError === true || !isPlanToolName(result.toolName)) {
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
const output = result.output;
|
|
221
|
-
const summary = typeof output === "object" && output !== null
|
|
222
|
-
? output.summary
|
|
223
|
-
: undefined;
|
|
224
|
-
const items = typeof summary === "object" && summary !== null && Array.isArray(summary.items)
|
|
225
|
-
? summary.items
|
|
226
|
-
: [];
|
|
227
|
-
const activeItems = items.filter((item) => item.status === "in_progress");
|
|
228
|
-
const candidateItems = activeItems.length > 0
|
|
229
|
-
? activeItems
|
|
230
|
-
: items.filter((item) => item.status === "pending").slice(0, 1);
|
|
231
|
-
for (const item of candidateItems) {
|
|
232
|
-
const content = [
|
|
233
|
-
item.content,
|
|
234
|
-
item.description,
|
|
235
|
-
item.title,
|
|
236
|
-
item.name,
|
|
237
|
-
item.text,
|
|
238
|
-
].filter((value) => typeof value === "string").join(" ");
|
|
239
|
-
const matched = availableTools.map((tool) => tool.name).filter((toolName) => textExplicitlyNamesTool(content, toolName));
|
|
240
|
-
if (matched.length === 1) {
|
|
241
|
-
return {
|
|
242
|
-
name: matched[0],
|
|
243
|
-
args: {},
|
|
244
|
-
id: `todo-committed-evidence-${index}`,
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return null;
|
|
250
|
-
}
|
|
251
199
|
function extractLatestUserInput(request) {
|
|
252
200
|
const typedRequest = request;
|
|
253
201
|
const messages = Array.isArray(typedRequest.messages) ? typedRequest.messages : [];
|
|
@@ -344,7 +292,7 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
|
|
|
344
292
|
: await callRuntimeWithToolParseRecovery(activeRequest));
|
|
345
293
|
pendingResult = undefined;
|
|
346
294
|
let toolCalls = extractToolCallsFromResult(result);
|
|
347
|
-
const
|
|
295
|
+
const structuredExternalPlanEvidenceTool = requiresPlanEvidence(binding)
|
|
348
296
|
&& hasPlanStateEvidence(executedToolResults, externalPlanEvidence)
|
|
349
297
|
&& !hasNonTodoToolEvidence(executedToolResults)
|
|
350
298
|
&& (externalPlanEvidenceTool !== undefined || !hasIncompleteExecutedPlan(executedToolResults, externalPlanEvidence))
|
|
@@ -355,10 +303,10 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
|
|
|
355
303
|
args: externalPlanEvidenceTool.args ?? {},
|
|
356
304
|
id: externalPlanEvidenceTool.id ?? "external-plan-evidence-tool",
|
|
357
305
|
}
|
|
358
|
-
:
|
|
306
|
+
: null
|
|
359
307
|
: null;
|
|
360
|
-
if (
|
|
361
|
-
toolCalls = [
|
|
308
|
+
if (structuredExternalPlanEvidenceTool) {
|
|
309
|
+
toolCalls = [structuredExternalPlanEvidenceTool];
|
|
362
310
|
}
|
|
363
311
|
if (toolCalls.length === 0) {
|
|
364
312
|
const terminalText = sanitizeVisibleText(extractVisibleOutput(result) || "");
|
|
@@ -514,7 +462,6 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
|
|
|
514
462
|
role: "system",
|
|
515
463
|
content: TOOL_FOLLOW_UP_INSTRUCTION,
|
|
516
464
|
});
|
|
517
|
-
const hadNonTodoEvidenceBeforeToolReplay = hasNonTodoToolEvidence(executedToolResults);
|
|
518
465
|
for (let toolIndex = 0; toolIndex < toolCalls.length; toolIndex += 1) {
|
|
519
466
|
const toolCall = toolCalls[toolIndex];
|
|
520
467
|
const resolvedToolName = resolveModelFacingToolName(toolCall.name, toolNameMapping, primaryTools);
|
|
@@ -571,61 +518,6 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
|
|
|
571
518
|
content: stringifyToolOutput(safeToolResult),
|
|
572
519
|
}));
|
|
573
520
|
}
|
|
574
|
-
const committedEvidenceTool = requiresPlanEvidence(binding)
|
|
575
|
-
&& !hadNonTodoEvidenceBeforeToolReplay
|
|
576
|
-
&& !hasNonTodoToolEvidence(executedToolResults)
|
|
577
|
-
&& !hasIncompleteExecutedPlan(executedToolResults, externalPlanEvidence)
|
|
578
|
-
? resolveCommittedTodoEvidenceTool(executedToolResults, primaryTools)
|
|
579
|
-
: null;
|
|
580
|
-
if (committedEvidenceTool) {
|
|
581
|
-
const resolvedToolName = resolveModelFacingToolName(committedEvidenceTool.name, toolNameMapping, primaryTools);
|
|
582
|
-
const executable = executableTools.get(committedEvidenceTool.name) ?? executableTools.get(resolvedToolName);
|
|
583
|
-
if (executable) {
|
|
584
|
-
const compiledTool = toolCatalog.get(committedEvidenceTool.name) ?? toolCatalog.get(resolvedToolName);
|
|
585
|
-
const normalizedArgs = normalizeToolArgsForSchema(committedEvidenceTool.args, executable.schema, undefined, {
|
|
586
|
-
latestUserInput,
|
|
587
|
-
});
|
|
588
|
-
const gateway = validateToolGatewayInput({
|
|
589
|
-
toolName: executable.name,
|
|
590
|
-
schema: executable.schema,
|
|
591
|
-
args: normalizedArgs,
|
|
592
|
-
requiresApproval: compiledTool ? toolRequiresRuntimeApproval(compiledTool) : false,
|
|
593
|
-
});
|
|
594
|
-
if (gateway.ok) {
|
|
595
|
-
const toolResult = toolRuntimeContext
|
|
596
|
-
? await executable.invoke(gateway.input, { toolRuntimeContext })
|
|
597
|
-
: await executable.invoke(gateway.input);
|
|
598
|
-
const memoryCandidates = compiledTool ? extractMemoryCandidatesFromToolOutput(compiledTool, toolResult) : [];
|
|
599
|
-
const safeToolResult = await maybePersistLargeToolOutput({
|
|
600
|
-
toolName: executable.name,
|
|
601
|
-
output: toolResult,
|
|
602
|
-
toolRuntimeContext,
|
|
603
|
-
});
|
|
604
|
-
executedToolResults.push({
|
|
605
|
-
toolName: executable.name,
|
|
606
|
-
output: safeToolResult,
|
|
607
|
-
...(memoryCandidates.length > 0 ? { memoryCandidates } : {}),
|
|
608
|
-
});
|
|
609
|
-
nextMessages.push(new ToolMessage({
|
|
610
|
-
name: executable.name,
|
|
611
|
-
tool_call_id: committedEvidenceTool.id,
|
|
612
|
-
content: stringifyToolOutput(safeToolResult),
|
|
613
|
-
}));
|
|
614
|
-
}
|
|
615
|
-
else {
|
|
616
|
-
executedToolResults.push({
|
|
617
|
-
toolName: executable.name,
|
|
618
|
-
output: gateway.error,
|
|
619
|
-
isError: true,
|
|
620
|
-
});
|
|
621
|
-
nextMessages.push(new ToolMessage({
|
|
622
|
-
name: executable.name,
|
|
623
|
-
tool_call_id: committedEvidenceTool.id,
|
|
624
|
-
content: stringifyToolOutput(gateway.error),
|
|
625
|
-
}));
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
521
|
if (usedExternalPlanEvidenceToolThisIteration && hasNonTodoToolEvidence(executedToolResults)) {
|
|
630
522
|
return {
|
|
631
523
|
result: buildDeterministicFinalFromToolEvidence(executedToolResults),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AIMessage, HumanMessage
|
|
1
|
+
import { AIMessage, HumanMessage } from "@langchain/core/messages";
|
|
2
2
|
import { createMemoryMiddleware, createFilesystemMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSubAgentMiddleware, createSummarizationMiddleware, StateBackend, } from "deepagents";
|
|
3
3
|
import { createAgent, humanInTheLoopMiddleware, todoListMiddleware } from "langchain";
|
|
4
4
|
import { createBuiltinMiddlewareTools } from "./tool/builtin-middleware-tools.js";
|
|
@@ -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
|
|
12
|
+
import { DEFAULT_SUBAGENT_PROMPT } 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
|
const DEFAULT_BUILTIN_TASK_TIMEOUT_MS = 180_000;
|
|
@@ -58,364 +58,6 @@ function extractDeepAgentTaskContent(result) {
|
|
|
58
58
|
}
|
|
59
59
|
return undefined;
|
|
60
60
|
}
|
|
61
|
-
function readMessageType(message) {
|
|
62
|
-
if (typeof message !== "object" || message === null) {
|
|
63
|
-
return "";
|
|
64
|
-
}
|
|
65
|
-
if (typeof message._getType === "function") {
|
|
66
|
-
const typeName = message._getType();
|
|
67
|
-
return typeof typeName === "string" ? typeName : "";
|
|
68
|
-
}
|
|
69
|
-
const typed = message;
|
|
70
|
-
if (typeof typed.type === "string") {
|
|
71
|
-
return typed.type;
|
|
72
|
-
}
|
|
73
|
-
const kwargs = typeof typed.kwargs === "object" && typed.kwargs !== null
|
|
74
|
-
? typed.kwargs
|
|
75
|
-
: undefined;
|
|
76
|
-
if (typeof kwargs?.type === "string") {
|
|
77
|
-
return kwargs.type;
|
|
78
|
-
}
|
|
79
|
-
const lcKwargs = typeof typed.lc_kwargs === "object" && typed.lc_kwargs !== null
|
|
80
|
-
? typed.lc_kwargs
|
|
81
|
-
: undefined;
|
|
82
|
-
return typeof lcKwargs?.type === "string" ? lcKwargs.type : "";
|
|
83
|
-
}
|
|
84
|
-
function readMessageName(message) {
|
|
85
|
-
if (typeof message !== "object" || message === null) {
|
|
86
|
-
return "";
|
|
87
|
-
}
|
|
88
|
-
const typed = message;
|
|
89
|
-
if (typeof typed.name === "string") {
|
|
90
|
-
return typed.name;
|
|
91
|
-
}
|
|
92
|
-
const kwargs = typeof typed.kwargs === "object" && typed.kwargs !== null
|
|
93
|
-
? typed.kwargs
|
|
94
|
-
: undefined;
|
|
95
|
-
if (typeof kwargs?.name === "string") {
|
|
96
|
-
return kwargs.name;
|
|
97
|
-
}
|
|
98
|
-
const lcKwargs = typeof typed.lc_kwargs === "object" && typed.lc_kwargs !== null
|
|
99
|
-
? typed.lc_kwargs
|
|
100
|
-
: undefined;
|
|
101
|
-
return typeof lcKwargs?.name === "string" ? lcKwargs.name : "";
|
|
102
|
-
}
|
|
103
|
-
function readMessages(result) {
|
|
104
|
-
if (typeof result !== "object" || result === null) {
|
|
105
|
-
return [];
|
|
106
|
-
}
|
|
107
|
-
const messages = result.messages;
|
|
108
|
-
return Array.isArray(messages) ? messages : [];
|
|
109
|
-
}
|
|
110
|
-
function readToolNames(tools) {
|
|
111
|
-
if (!Array.isArray(tools)) {
|
|
112
|
-
return new Set();
|
|
113
|
-
}
|
|
114
|
-
return new Set(tools
|
|
115
|
-
.map((tool) => {
|
|
116
|
-
if (typeof tool !== "object" || tool === null) {
|
|
117
|
-
return "";
|
|
118
|
-
}
|
|
119
|
-
const name = tool.name;
|
|
120
|
-
return typeof name === "string" ? name : "";
|
|
121
|
-
})
|
|
122
|
-
.filter((name) => name.length > 0));
|
|
123
|
-
}
|
|
124
|
-
function readResolvedEvidenceTools(tools) {
|
|
125
|
-
if (!Array.isArray(tools)) {
|
|
126
|
-
return [];
|
|
127
|
-
}
|
|
128
|
-
return tools
|
|
129
|
-
.map((tool) => {
|
|
130
|
-
if (typeof tool !== "object" || tool === null) {
|
|
131
|
-
return undefined;
|
|
132
|
-
}
|
|
133
|
-
const typed = tool;
|
|
134
|
-
if (typeof typed.name !== "string" || typed.name.trim().length === 0) {
|
|
135
|
-
return undefined;
|
|
136
|
-
}
|
|
137
|
-
const handler = [typed.invoke, typed.call, typed.func]
|
|
138
|
-
.find((candidate) => typeof candidate === "function");
|
|
139
|
-
return {
|
|
140
|
-
name: typed.name,
|
|
141
|
-
...(typeof typed.description === "string" ? { description: typed.description } : {}),
|
|
142
|
-
schema: typed.schema,
|
|
143
|
-
...(handler ? { invoke: (input, config) => handler.call(tool, input, config) } : {}),
|
|
144
|
-
};
|
|
145
|
-
})
|
|
146
|
-
.filter((tool) => tool !== undefined);
|
|
147
|
-
}
|
|
148
|
-
function hasSubagentExecutionToolEvidence(result, resolvedTools, configuredTools) {
|
|
149
|
-
const requiredToolNames = new Set([
|
|
150
|
-
...readToolNames(configuredTools),
|
|
151
|
-
...readToolNames(resolvedTools),
|
|
152
|
-
]);
|
|
153
|
-
if (requiredToolNames.size === 0) {
|
|
154
|
-
return true;
|
|
155
|
-
}
|
|
156
|
-
for (const message of readMessages(result)) {
|
|
157
|
-
const typeName = readMessageType(message);
|
|
158
|
-
if (typeName !== "tool" && typeName !== "ToolMessage") {
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
const name = readMessageName(message);
|
|
162
|
-
if (name === "write_todos" || name === "read_todos") {
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
if (requiredToolNames.has(name)) {
|
|
166
|
-
return true;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return false;
|
|
170
|
-
}
|
|
171
|
-
function stringifyTaskState(value) {
|
|
172
|
-
try {
|
|
173
|
-
return JSON.stringify(value);
|
|
174
|
-
}
|
|
175
|
-
catch {
|
|
176
|
-
return String(value ?? "");
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
function extractUrls(value) {
|
|
180
|
-
return [...new Set((value.match(/https?:\/\/[^\s<>"')\]}]+/giu) ?? [])
|
|
181
|
-
.map((url) => url.replace(/[.,;:!?]+$/u, "")))];
|
|
182
|
-
}
|
|
183
|
-
function readSchemaObjectShape(schema) {
|
|
184
|
-
if (!isRecord(schema)) {
|
|
185
|
-
return undefined;
|
|
186
|
-
}
|
|
187
|
-
if (isRecord(schema.properties)) {
|
|
188
|
-
return schema.properties;
|
|
189
|
-
}
|
|
190
|
-
const candidates = [
|
|
191
|
-
schema._def?.shape,
|
|
192
|
-
schema._zod?.def?.shape,
|
|
193
|
-
schema.def?.shape,
|
|
194
|
-
];
|
|
195
|
-
for (const candidate of candidates) {
|
|
196
|
-
const shape = typeof candidate === "function" ? candidate() : candidate;
|
|
197
|
-
if (isRecord(shape)) {
|
|
198
|
-
return shape;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return undefined;
|
|
202
|
-
}
|
|
203
|
-
function buildUrlSourceArgs(urls) {
|
|
204
|
-
return urls.map((url) => ({ type: "url", url, timeoutMs: 30000 }));
|
|
205
|
-
}
|
|
206
|
-
function buildEvidenceToolArgs(tool, taskText, evidenceText = taskText) {
|
|
207
|
-
const urls = extractUrls(evidenceText);
|
|
208
|
-
if (urls.length === 0) {
|
|
209
|
-
return {};
|
|
210
|
-
}
|
|
211
|
-
const shape = readSchemaObjectShape(tool.schema);
|
|
212
|
-
if (!shape) {
|
|
213
|
-
return {};
|
|
214
|
-
}
|
|
215
|
-
const args = {};
|
|
216
|
-
if ("sources" in shape) {
|
|
217
|
-
args.sources = buildUrlSourceArgs(urls);
|
|
218
|
-
}
|
|
219
|
-
if ("url" in shape) {
|
|
220
|
-
args.url = urls[0];
|
|
221
|
-
}
|
|
222
|
-
if ("urls" in shape) {
|
|
223
|
-
args.urls = urls;
|
|
224
|
-
}
|
|
225
|
-
if ("question" in shape) {
|
|
226
|
-
args.question = taskText.trim() || evidenceText.trim();
|
|
227
|
-
}
|
|
228
|
-
return args;
|
|
229
|
-
}
|
|
230
|
-
function escapeRegExp(value) {
|
|
231
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
232
|
-
}
|
|
233
|
-
function textExplicitlyNamesTool(text, toolName) {
|
|
234
|
-
const name = toolName.trim();
|
|
235
|
-
if (!name) {
|
|
236
|
-
return false;
|
|
237
|
-
}
|
|
238
|
-
const pattern = new RegExp(`(?:^|[^\\p{L}\\p{N}_-])${escapeRegExp(name)}(?:$|[^\\p{L}\\p{N}_-])`, "iu");
|
|
239
|
-
return pattern.test(text);
|
|
240
|
-
}
|
|
241
|
-
function resolveCommittedEvidenceTools(input) {
|
|
242
|
-
const availableTools = readResolvedEvidenceTools(input.resolvedTools)
|
|
243
|
-
.filter((tool) => tool.name !== "write_todos" && tool.name !== "read_todos" && typeof tool.invoke === "function");
|
|
244
|
-
if (availableTools.length === 0) {
|
|
245
|
-
return [];
|
|
246
|
-
}
|
|
247
|
-
const stateText = `${input.taskText}\n${stringifyTaskState(readMessages(input.result))}`;
|
|
248
|
-
const selected = [];
|
|
249
|
-
for (const tool of availableTools) {
|
|
250
|
-
if (!textExplicitlyNamesTool(stateText, tool.name)) {
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
selected.push({
|
|
254
|
-
tool,
|
|
255
|
-
args: buildEvidenceToolArgs(tool, input.taskText, stateText),
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
if (selected.length > 0) {
|
|
259
|
-
return selected.slice(0, 3);
|
|
260
|
-
}
|
|
261
|
-
return [];
|
|
262
|
-
}
|
|
263
|
-
async function appendCommittedEvidenceToolResults(input) {
|
|
264
|
-
const toolCalls = resolveCommittedEvidenceTools({
|
|
265
|
-
result: input.result,
|
|
266
|
-
taskText: input.taskText,
|
|
267
|
-
resolvedTools: input.resolvedTools,
|
|
268
|
-
});
|
|
269
|
-
if (process.env.AGENT_HARNESS_PROMPTED_JSON_DEBUG === "1") {
|
|
270
|
-
console.error(JSON.stringify({
|
|
271
|
-
type: "builtin-task-committed-evidence",
|
|
272
|
-
selectedTools: toolCalls.map((item) => item.tool.name),
|
|
273
|
-
availableTools: readResolvedEvidenceTools(input.resolvedTools).map((tool) => ({
|
|
274
|
-
name: tool.name,
|
|
275
|
-
hasInvoke: typeof tool.invoke === "function",
|
|
276
|
-
})),
|
|
277
|
-
}));
|
|
278
|
-
}
|
|
279
|
-
if (toolCalls.length === 0) {
|
|
280
|
-
return input.result;
|
|
281
|
-
}
|
|
282
|
-
const messages = [...readMessages(input.result)];
|
|
283
|
-
for (const [index, item] of toolCalls.entries()) {
|
|
284
|
-
const toolCallId = `committed-evidence-${item.tool.name}-${index + 1}`;
|
|
285
|
-
messages.push(new AIMessage({
|
|
286
|
-
content: "",
|
|
287
|
-
tool_calls: [{
|
|
288
|
-
id: toolCallId,
|
|
289
|
-
name: item.tool.name,
|
|
290
|
-
args: item.args,
|
|
291
|
-
type: "tool_call",
|
|
292
|
-
}],
|
|
293
|
-
}));
|
|
294
|
-
try {
|
|
295
|
-
const output = await item.tool.invoke(item.args, input.invokeConfig);
|
|
296
|
-
messages.push(new ToolMessage({
|
|
297
|
-
name: item.tool.name,
|
|
298
|
-
tool_call_id: toolCallId,
|
|
299
|
-
content: stringifyVisibleTaskContent(output) || stringifyTaskState(output),
|
|
300
|
-
}));
|
|
301
|
-
}
|
|
302
|
-
catch (error) {
|
|
303
|
-
messages.push(new ToolMessage({
|
|
304
|
-
name: item.tool.name,
|
|
305
|
-
tool_call_id: toolCallId,
|
|
306
|
-
content: error instanceof Error ? error.message : String(error),
|
|
307
|
-
}));
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
return {
|
|
311
|
-
...(typeof input.result === "object" && input.result !== null ? input.result : {}),
|
|
312
|
-
messages,
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
function hasFinalAssistantAfterExecutionTool(result) {
|
|
316
|
-
const messages = readMessages(result);
|
|
317
|
-
const lastMessage = messages.at(-1);
|
|
318
|
-
const lastTypeName = readMessageType(lastMessage);
|
|
319
|
-
if (lastTypeName === "tool" || lastTypeName === "ToolMessage") {
|
|
320
|
-
return false;
|
|
321
|
-
}
|
|
322
|
-
let sawExecutionTool = false;
|
|
323
|
-
for (const message of messages) {
|
|
324
|
-
const typeName = readMessageType(message);
|
|
325
|
-
if (typeName === "tool" || typeName === "ToolMessage") {
|
|
326
|
-
const name = readMessageName(message);
|
|
327
|
-
if (name !== "write_todos" && name !== "read_todos") {
|
|
328
|
-
sawExecutionTool = true;
|
|
329
|
-
}
|
|
330
|
-
continue;
|
|
331
|
-
}
|
|
332
|
-
if (!sawExecutionTool) {
|
|
333
|
-
continue;
|
|
334
|
-
}
|
|
335
|
-
if (typeName === "ai" || typeName === "AIMessage" || typeName === "assistant") {
|
|
336
|
-
const content = typeof message === "object" && message !== null && "content" in message
|
|
337
|
-
? message.content
|
|
338
|
-
: undefined;
|
|
339
|
-
if (typeof content === "string" && content.trim().length > 0) {
|
|
340
|
-
return true;
|
|
341
|
-
}
|
|
342
|
-
if (Array.isArray(content) && content.length > 0) {
|
|
343
|
-
return true;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
return false;
|
|
348
|
-
}
|
|
349
|
-
const FINALIZE_AFTER_TOOL_EVIDENCE_INSTRUCTION = [
|
|
350
|
-
"You have already gathered the required non-planning tool evidence.",
|
|
351
|
-
"Now produce the final answer for the delegated task.",
|
|
352
|
-
"Do not return raw command stdout as the final answer.",
|
|
353
|
-
"Do not stop at a tool result.",
|
|
354
|
-
"Update TODO statuses if needed, then return the configured response format with concise findings, blockers, next actions, and report.",
|
|
355
|
-
].join("\n");
|
|
356
|
-
function stringifyVisibleTaskContent(value) {
|
|
357
|
-
if (typeof value === "string") {
|
|
358
|
-
return value;
|
|
359
|
-
}
|
|
360
|
-
if (Array.isArray(value)) {
|
|
361
|
-
return value
|
|
362
|
-
.map((block) => {
|
|
363
|
-
if (typeof block === "string") {
|
|
364
|
-
return block;
|
|
365
|
-
}
|
|
366
|
-
if (isRecord(block) && typeof block.text === "string") {
|
|
367
|
-
return block.text;
|
|
368
|
-
}
|
|
369
|
-
return "";
|
|
370
|
-
})
|
|
371
|
-
.filter(Boolean)
|
|
372
|
-
.join("\n");
|
|
373
|
-
}
|
|
374
|
-
return "";
|
|
375
|
-
}
|
|
376
|
-
function looksLikeRawToolOutput(value) {
|
|
377
|
-
const normalized = value.trim();
|
|
378
|
-
return /^(?:stdout|stderr)\s*:/iu.test(normalized)
|
|
379
|
-
|| /(?:^|\n)\s*(?:stdout|stderr)\s*:/iu.test(normalized)
|
|
380
|
-
|| /(?:^|\n)\s*exitCode\s*:\s*-?\d+\s*$/iu.test(normalized);
|
|
381
|
-
}
|
|
382
|
-
function collectNonPlanningToolNames(result) {
|
|
383
|
-
const names = [];
|
|
384
|
-
for (const message of readMessages(result)) {
|
|
385
|
-
const typeName = readMessageType(message);
|
|
386
|
-
if (typeName !== "tool" && typeName !== "ToolMessage") {
|
|
387
|
-
continue;
|
|
388
|
-
}
|
|
389
|
-
const name = readMessageName(message);
|
|
390
|
-
if (!name || name === "write_todos" || name === "read_todos") {
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
if (!names.includes(name)) {
|
|
394
|
-
names.push(name);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
return names;
|
|
398
|
-
}
|
|
399
|
-
function formatDelegatedRawToolEvidenceReport(input) {
|
|
400
|
-
const toolNames = collectNonPlanningToolNames(input.result);
|
|
401
|
-
const excerpt = input.output.trim().slice(0, 4000);
|
|
402
|
-
return JSON.stringify({
|
|
403
|
-
status: "completed",
|
|
404
|
-
routing: [`task delegated to ${input.subagentName}`],
|
|
405
|
-
plan: ["Delegate to specialist", "Collect required tool evidence", "Return synthesized result"],
|
|
406
|
-
execution: [
|
|
407
|
-
`task invoked ${input.subagentName}`,
|
|
408
|
-
`tool evidence: ${toolNames.length > 0 ? toolNames.join(", ") : "non-planning tool"}`,
|
|
409
|
-
],
|
|
410
|
-
todoTrace: ["specialist TODO plan was created and completed"],
|
|
411
|
-
stepResults: [`${input.subagentName} completed delegated tool evidence collection`],
|
|
412
|
-
summary: ["Delegated specialist completed the required evidence tool path."],
|
|
413
|
-
findings: ["The delegated agent ended on raw tool output, so the runtime wrapped that evidence in a structured completion report."],
|
|
414
|
-
blockers: ["none"],
|
|
415
|
-
nextActions: ["Use the captured evidence for final parent synthesis."],
|
|
416
|
-
report: excerpt,
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
61
|
export function extractSubagentRequestText(state) {
|
|
420
62
|
if (!isRecord(state)) {
|
|
421
63
|
return "";
|
|
@@ -693,27 +335,7 @@ export async function invokeBuiltinTaskTool(input) {
|
|
|
693
335
|
new HumanMessage({ content }),
|
|
694
336
|
],
|
|
695
337
|
}, invokeConfig), taskTimeoutMs, selectedSubagent.name);
|
|
696
|
-
|
|
697
|
-
if (!hasSubagentExecutionToolEvidence(result, resolvedSubagentTools, selectedCompiledSubagent?.tools)) {
|
|
698
|
-
result = await invokeSubagent([description, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"));
|
|
699
|
-
if (!hasSubagentExecutionToolEvidence(result, resolvedSubagentTools, selectedCompiledSubagent?.tools)) {
|
|
700
|
-
result = await appendCommittedEvidenceToolResults({
|
|
701
|
-
result,
|
|
702
|
-
taskText: description,
|
|
703
|
-
resolvedTools: resolvedSubagentTools,
|
|
704
|
-
invokeConfig,
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
|
-
if (!hasSubagentExecutionToolEvidence(result, resolvedSubagentTools, selectedCompiledSubagent?.tools)) {
|
|
708
|
-
throw new Error(`Delegated agent ${selectedSubagent.name} completed without tool execution evidence: lacked non-planning tool evidence.`);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
if (!hasFinalAssistantAfterExecutionTool(result)
|
|
712
|
-
|| looksLikeRawToolOutput(stringifyVisibleTaskContent(extractDeepAgentTaskContent(result)))
|
|
713
|
-
|| looksLikeRawToolOutput(extractVisibleOutput(result))
|
|
714
|
-
|| looksLikeRawToolOutput(extractToolFallbackContext(result))) {
|
|
715
|
-
result = await invokeSubagent(FINALIZE_AFTER_TOOL_EVIDENCE_INSTRUCTION, readMessages(result));
|
|
716
|
-
}
|
|
338
|
+
const result = await invokeSubagent(description);
|
|
717
339
|
const structuredResponse = typeof result === "object" && result !== null && "structuredResponse" in result
|
|
718
340
|
? result.structuredResponse
|
|
719
341
|
: undefined;
|
|
@@ -721,14 +343,6 @@ export async function invokeBuiltinTaskTool(input) {
|
|
|
721
343
|
return JSON.stringify(structuredResponse);
|
|
722
344
|
}
|
|
723
345
|
const taskContent = extractDeepAgentTaskContent(result);
|
|
724
|
-
const taskContentText = stringifyVisibleTaskContent(taskContent);
|
|
725
|
-
if (looksLikeRawToolOutput(taskContentText)) {
|
|
726
|
-
return formatDelegatedRawToolEvidenceReport({
|
|
727
|
-
subagentName: selectedSubagent.name,
|
|
728
|
-
output: taskContentText,
|
|
729
|
-
result,
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
346
|
if (Array.isArray(taskContent)) {
|
|
733
347
|
const filtered = taskContent.filter((block) => {
|
|
734
348
|
const blockType = typeof block === "object" && block !== null && "type" in block
|
|
@@ -744,24 +358,10 @@ export async function invokeBuiltinTaskTool(input) {
|
|
|
744
358
|
return taskContent;
|
|
745
359
|
}
|
|
746
360
|
const visibleOutput = extractVisibleOutput(result);
|
|
747
|
-
if (looksLikeRawToolOutput(visibleOutput)) {
|
|
748
|
-
return formatDelegatedRawToolEvidenceReport({
|
|
749
|
-
subagentName: selectedSubagent.name,
|
|
750
|
-
output: visibleOutput,
|
|
751
|
-
result,
|
|
752
|
-
});
|
|
753
|
-
}
|
|
754
361
|
if (visibleOutput) {
|
|
755
362
|
return visibleOutput;
|
|
756
363
|
}
|
|
757
364
|
const fallbackOutput = extractToolFallbackContext(result);
|
|
758
|
-
if (looksLikeRawToolOutput(fallbackOutput)) {
|
|
759
|
-
return formatDelegatedRawToolEvidenceReport({
|
|
760
|
-
subagentName: selectedSubagent.name,
|
|
761
|
-
output: fallbackOutput,
|
|
762
|
-
result,
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
365
|
return fallbackOutput || "Task completed";
|
|
766
366
|
}
|
|
767
367
|
export async function resolveBuiltinMiddlewareTools(input) {
|