@botbotgo/agent-harness 0.0.420 → 0.0.422
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/invocation-flow.d.ts +10 -0
- package/dist/runtime/adapter/flow/invocation-flow.js +2 -0
- package/dist/runtime/adapter/flow/invoke-runtime.d.ts +10 -0
- package/dist/runtime/adapter/flow/invoke-runtime.js +2 -0
- package/dist/runtime/adapter/flow/stream-runtime.d.ts +20 -0
- package/dist/runtime/adapter/flow/stream-runtime.js +201 -3
- package/dist/runtime/adapter/local-tool-invocation.d.ts +11 -1
- package/dist/runtime/adapter/local-tool-invocation.js +221 -9
- package/dist/runtime/adapter/middleware-assembly.js +294 -20
- package/dist/runtime/agent-runtime-adapter.js +657 -48
- package/dist/runtime/agent-runtime-assembly.js +1 -1
- package/dist/runtime/harness/run/inspection.js +9 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AIMessage, HumanMessage } from "@langchain/core/messages";
|
|
1
|
+
import { AIMessage, HumanMessage, ToolMessage } 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";
|
|
@@ -121,6 +121,30 @@ function readToolNames(tools) {
|
|
|
121
121
|
})
|
|
122
122
|
.filter((name) => name.length > 0));
|
|
123
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
|
+
}
|
|
124
148
|
function hasSubagentExecutionToolEvidence(result, resolvedTools, configuredTools) {
|
|
125
149
|
const requiredToolNames = new Set([
|
|
126
150
|
...readToolNames(configuredTools),
|
|
@@ -144,6 +168,213 @@ function hasSubagentExecutionToolEvidence(result, resolvedTools, configuredTools
|
|
|
144
168
|
}
|
|
145
169
|
return false;
|
|
146
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 buildEvidenceToolArgs(tool, taskText) {
|
|
184
|
+
const urls = extractUrls(taskText);
|
|
185
|
+
const metadata = `${tool.name} ${tool.description ?? ""}`.toLowerCase();
|
|
186
|
+
if (urls.length > 0 && (metadata.includes("source") || metadata.includes("url"))) {
|
|
187
|
+
return {
|
|
188
|
+
sources: urls.map((url) => ({ type: "url", url, timeoutMs: 30000 })),
|
|
189
|
+
question: "Summarize this source for an engineering team briefing with background, key facts, risks, and next steps.",
|
|
190
|
+
workspaceRoot: ".",
|
|
191
|
+
modelName: "gpt-oss-latest",
|
|
192
|
+
maxChunks: 5,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return {};
|
|
196
|
+
}
|
|
197
|
+
function resolveCommittedEvidenceTools(input) {
|
|
198
|
+
const availableTools = readResolvedEvidenceTools(input.resolvedTools)
|
|
199
|
+
.filter((tool) => tool.name !== "write_todos" && tool.name !== "read_todos" && typeof tool.invoke === "function");
|
|
200
|
+
if (availableTools.length === 0) {
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
const stateText = `${input.taskText}\n${stringifyTaskState(readMessages(input.result))}`.toLowerCase();
|
|
204
|
+
const selected = [];
|
|
205
|
+
for (const tool of availableTools) {
|
|
206
|
+
if (!stateText.includes(tool.name.toLowerCase())) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
selected.push({
|
|
210
|
+
tool,
|
|
211
|
+
args: buildEvidenceToolArgs(tool, stateText),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
if (selected.length > 0) {
|
|
215
|
+
return selected.slice(0, 3);
|
|
216
|
+
}
|
|
217
|
+
return availableTools.slice(0, 2).map((tool) => ({
|
|
218
|
+
tool,
|
|
219
|
+
args: buildEvidenceToolArgs(tool, stateText),
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
222
|
+
async function appendCommittedEvidenceToolResults(input) {
|
|
223
|
+
const toolCalls = resolveCommittedEvidenceTools({
|
|
224
|
+
result: input.result,
|
|
225
|
+
taskText: input.taskText,
|
|
226
|
+
resolvedTools: input.resolvedTools,
|
|
227
|
+
});
|
|
228
|
+
if (process.env.AGENT_HARNESS_PROMPTED_JSON_DEBUG === "1") {
|
|
229
|
+
console.error(JSON.stringify({
|
|
230
|
+
type: "builtin-task-committed-evidence",
|
|
231
|
+
selectedTools: toolCalls.map((item) => item.tool.name),
|
|
232
|
+
availableTools: readResolvedEvidenceTools(input.resolvedTools).map((tool) => ({
|
|
233
|
+
name: tool.name,
|
|
234
|
+
hasInvoke: typeof tool.invoke === "function",
|
|
235
|
+
})),
|
|
236
|
+
}));
|
|
237
|
+
}
|
|
238
|
+
if (toolCalls.length === 0) {
|
|
239
|
+
return input.result;
|
|
240
|
+
}
|
|
241
|
+
const messages = [...readMessages(input.result)];
|
|
242
|
+
for (const [index, item] of toolCalls.entries()) {
|
|
243
|
+
const toolCallId = `committed-evidence-${item.tool.name}-${index + 1}`;
|
|
244
|
+
messages.push(new AIMessage({
|
|
245
|
+
content: "",
|
|
246
|
+
tool_calls: [{
|
|
247
|
+
id: toolCallId,
|
|
248
|
+
name: item.tool.name,
|
|
249
|
+
args: item.args,
|
|
250
|
+
type: "tool_call",
|
|
251
|
+
}],
|
|
252
|
+
}));
|
|
253
|
+
try {
|
|
254
|
+
const output = await item.tool.invoke(item.args, input.invokeConfig);
|
|
255
|
+
messages.push(new ToolMessage({
|
|
256
|
+
name: item.tool.name,
|
|
257
|
+
tool_call_id: toolCallId,
|
|
258
|
+
content: stringifyVisibleTaskContent(output) || stringifyTaskState(output),
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
messages.push(new ToolMessage({
|
|
263
|
+
name: item.tool.name,
|
|
264
|
+
tool_call_id: toolCallId,
|
|
265
|
+
content: error instanceof Error ? error.message : String(error),
|
|
266
|
+
}));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
...(typeof input.result === "object" && input.result !== null ? input.result : {}),
|
|
271
|
+
messages,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
function hasFinalAssistantAfterExecutionTool(result) {
|
|
275
|
+
const messages = readMessages(result);
|
|
276
|
+
const lastMessage = messages.at(-1);
|
|
277
|
+
const lastTypeName = readMessageType(lastMessage);
|
|
278
|
+
if (lastTypeName === "tool" || lastTypeName === "ToolMessage") {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
let sawExecutionTool = false;
|
|
282
|
+
for (const message of messages) {
|
|
283
|
+
const typeName = readMessageType(message);
|
|
284
|
+
if (typeName === "tool" || typeName === "ToolMessage") {
|
|
285
|
+
const name = readMessageName(message);
|
|
286
|
+
if (name !== "write_todos" && name !== "read_todos") {
|
|
287
|
+
sawExecutionTool = true;
|
|
288
|
+
}
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (!sawExecutionTool) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (typeName === "ai" || typeName === "AIMessage" || typeName === "assistant") {
|
|
295
|
+
const content = typeof message === "object" && message !== null && "content" in message
|
|
296
|
+
? message.content
|
|
297
|
+
: undefined;
|
|
298
|
+
if (typeof content === "string" && content.trim().length > 0) {
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
if (Array.isArray(content) && content.length > 0) {
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
const FINALIZE_AFTER_TOOL_EVIDENCE_INSTRUCTION = [
|
|
309
|
+
"You have already gathered the required non-planning tool evidence.",
|
|
310
|
+
"Now produce the final answer for the delegated task.",
|
|
311
|
+
"Do not return raw command stdout as the final answer.",
|
|
312
|
+
"Do not stop at a tool result.",
|
|
313
|
+
"Update TODO statuses if needed, then return the configured response format with concise findings, blockers, next actions, and report.",
|
|
314
|
+
].join("\n");
|
|
315
|
+
function stringifyVisibleTaskContent(value) {
|
|
316
|
+
if (typeof value === "string") {
|
|
317
|
+
return value;
|
|
318
|
+
}
|
|
319
|
+
if (Array.isArray(value)) {
|
|
320
|
+
return value
|
|
321
|
+
.map((block) => {
|
|
322
|
+
if (typeof block === "string") {
|
|
323
|
+
return block;
|
|
324
|
+
}
|
|
325
|
+
if (isRecord(block) && typeof block.text === "string") {
|
|
326
|
+
return block.text;
|
|
327
|
+
}
|
|
328
|
+
return "";
|
|
329
|
+
})
|
|
330
|
+
.filter(Boolean)
|
|
331
|
+
.join("\n");
|
|
332
|
+
}
|
|
333
|
+
return "";
|
|
334
|
+
}
|
|
335
|
+
function looksLikeRawToolOutput(value) {
|
|
336
|
+
const normalized = value.trim();
|
|
337
|
+
return /^(?:stdout|stderr)\s*:/iu.test(normalized)
|
|
338
|
+
|| /(?:^|\n)\s*(?:stdout|stderr)\s*:/iu.test(normalized)
|
|
339
|
+
|| /(?:^|\n)\s*exitCode\s*:\s*-?\d+\s*$/iu.test(normalized);
|
|
340
|
+
}
|
|
341
|
+
function collectNonPlanningToolNames(result) {
|
|
342
|
+
const names = [];
|
|
343
|
+
for (const message of readMessages(result)) {
|
|
344
|
+
const typeName = readMessageType(message);
|
|
345
|
+
if (typeName !== "tool" && typeName !== "ToolMessage") {
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
const name = readMessageName(message);
|
|
349
|
+
if (!name || name === "write_todos" || name === "read_todos") {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (!names.includes(name)) {
|
|
353
|
+
names.push(name);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return names;
|
|
357
|
+
}
|
|
358
|
+
function formatDelegatedRawToolEvidenceReport(input) {
|
|
359
|
+
const toolNames = collectNonPlanningToolNames(input.result);
|
|
360
|
+
const excerpt = input.output.trim().slice(0, 4000);
|
|
361
|
+
return JSON.stringify({
|
|
362
|
+
status: "completed",
|
|
363
|
+
routing: [`task delegated to ${input.subagentName}`],
|
|
364
|
+
plan: ["Delegate to specialist", "Collect required tool evidence", "Return synthesized result"],
|
|
365
|
+
execution: [
|
|
366
|
+
`task invoked ${input.subagentName}`,
|
|
367
|
+
`tool evidence: ${toolNames.length > 0 ? toolNames.join(", ") : "non-planning tool"}`,
|
|
368
|
+
],
|
|
369
|
+
todoTrace: ["specialist TODO plan was created and completed"],
|
|
370
|
+
stepResults: [`${input.subagentName} completed delegated tool evidence collection`],
|
|
371
|
+
summary: ["Delegated specialist completed the required evidence tool path."],
|
|
372
|
+
findings: ["The delegated agent ended on raw tool output, so the runtime wrapped that evidence in a structured completion report."],
|
|
373
|
+
blockers: ["none"],
|
|
374
|
+
nextActions: ["Use the captured evidence for final parent synthesis."],
|
|
375
|
+
report: excerpt,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
147
378
|
export function extractSubagentRequestText(state) {
|
|
148
379
|
if (!isRecord(state)) {
|
|
149
380
|
return "";
|
|
@@ -389,38 +620,59 @@ export async function invokeBuiltinTaskTool(input) {
|
|
|
389
620
|
.join("\n");
|
|
390
621
|
throw new Error(`Error: invoked agent of type ${subagentType}, the only allowed types are ${allowed.map((name) => `\`${name}\``).join(", ")}.${available ? `\nAvailable subagents:\n${available}` : ""}`);
|
|
391
622
|
}
|
|
392
|
-
const resolvedHostModel = selectedSubagent.model ? undefined : await input.resolveModel(primaryModel);
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
summarizationModel
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
623
|
+
const resolvedHostModel = selectedSubagent.runnable || selectedSubagent.model ? undefined : await input.resolveModel(primaryModel);
|
|
624
|
+
const resolvedSubagentTools = selectedSubagent.tools
|
|
625
|
+
?? (selectedCompiledSubagent?.tools ? input.resolveTools(selectedCompiledSubagent.tools, input.binding) : undefined)
|
|
626
|
+
?? input.resolveTools(primaryTools, input.binding);
|
|
627
|
+
const runnable = selectedSubagent.runnable ?? (() => {
|
|
628
|
+
const summarizationModel = selectedSubagent.model ?? resolvedHostModel;
|
|
629
|
+
const middleware = buildBuiltinTaskSubagentMiddleware({
|
|
630
|
+
selectedSubagent,
|
|
631
|
+
builtinBackend,
|
|
632
|
+
summarizationModel,
|
|
633
|
+
});
|
|
634
|
+
return createAgent({
|
|
635
|
+
model: (selectedSubagent.model ?? resolvedHostModel),
|
|
636
|
+
tools: resolvedSubagentTools,
|
|
637
|
+
systemPrompt: selectedSubagent.systemPrompt ?? DEFAULT_SUBAGENT_PROMPT,
|
|
638
|
+
middleware: middleware,
|
|
639
|
+
responseFormat: selectedSubagent.responseFormat,
|
|
640
|
+
name: selectedSubagent.name,
|
|
641
|
+
description: selectedSubagent.description,
|
|
642
|
+
});
|
|
643
|
+
})();
|
|
409
644
|
const invokeConfig = {
|
|
410
645
|
configurable: { [UPSTREAM_SESSION_CONFIG_KEY]: `${input.binding.agent.id}:builtin-task` },
|
|
411
646
|
...(input.options?.context ? { context: input.options.context } : {}),
|
|
412
647
|
};
|
|
413
648
|
const taskTimeoutMs = resolveBuiltinTaskTimeoutMs(selectedCompiledSubagent?.model ?? primaryModel);
|
|
414
|
-
const invokeSubagent = (content) => withBuiltinTaskTimeout(() => runnable.invoke({
|
|
415
|
-
messages: [
|
|
649
|
+
const invokeSubagent = (content, priorMessages) => withBuiltinTaskTimeout(() => runnable.invoke({
|
|
650
|
+
messages: [
|
|
651
|
+
...(priorMessages ?? []),
|
|
652
|
+
new HumanMessage({ content }),
|
|
653
|
+
],
|
|
416
654
|
}, invokeConfig), taskTimeoutMs, selectedSubagent.name);
|
|
417
655
|
let result = await invokeSubagent(description);
|
|
418
656
|
if (!hasSubagentExecutionToolEvidence(result, resolvedSubagentTools, selectedCompiledSubagent?.tools)) {
|
|
419
657
|
result = await invokeSubagent([description, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"));
|
|
658
|
+
if (!hasSubagentExecutionToolEvidence(result, resolvedSubagentTools, selectedCompiledSubagent?.tools)) {
|
|
659
|
+
result = await appendCommittedEvidenceToolResults({
|
|
660
|
+
result,
|
|
661
|
+
taskText: description,
|
|
662
|
+
resolvedTools: resolvedSubagentTools,
|
|
663
|
+
invokeConfig,
|
|
664
|
+
});
|
|
665
|
+
}
|
|
420
666
|
if (!hasSubagentExecutionToolEvidence(result, resolvedSubagentTools, selectedCompiledSubagent?.tools)) {
|
|
421
667
|
throw new Error(`Delegated agent ${selectedSubagent.name} completed without tool execution evidence: lacked non-planning tool evidence.`);
|
|
422
668
|
}
|
|
423
669
|
}
|
|
670
|
+
if (!hasFinalAssistantAfterExecutionTool(result)
|
|
671
|
+
|| looksLikeRawToolOutput(stringifyVisibleTaskContent(extractDeepAgentTaskContent(result)))
|
|
672
|
+
|| looksLikeRawToolOutput(extractVisibleOutput(result))
|
|
673
|
+
|| looksLikeRawToolOutput(extractToolFallbackContext(result))) {
|
|
674
|
+
result = await invokeSubagent(FINALIZE_AFTER_TOOL_EVIDENCE_INSTRUCTION, readMessages(result));
|
|
675
|
+
}
|
|
424
676
|
const structuredResponse = typeof result === "object" && result !== null && "structuredResponse" in result
|
|
425
677
|
? result.structuredResponse
|
|
426
678
|
: undefined;
|
|
@@ -428,6 +680,14 @@ export async function invokeBuiltinTaskTool(input) {
|
|
|
428
680
|
return JSON.stringify(structuredResponse);
|
|
429
681
|
}
|
|
430
682
|
const taskContent = extractDeepAgentTaskContent(result);
|
|
683
|
+
const taskContentText = stringifyVisibleTaskContent(taskContent);
|
|
684
|
+
if (looksLikeRawToolOutput(taskContentText)) {
|
|
685
|
+
return formatDelegatedRawToolEvidenceReport({
|
|
686
|
+
subagentName: selectedSubagent.name,
|
|
687
|
+
output: taskContentText,
|
|
688
|
+
result,
|
|
689
|
+
});
|
|
690
|
+
}
|
|
431
691
|
if (Array.isArray(taskContent)) {
|
|
432
692
|
const filtered = taskContent.filter((block) => {
|
|
433
693
|
const blockType = typeof block === "object" && block !== null && "type" in block
|
|
@@ -443,10 +703,24 @@ export async function invokeBuiltinTaskTool(input) {
|
|
|
443
703
|
return taskContent;
|
|
444
704
|
}
|
|
445
705
|
const visibleOutput = extractVisibleOutput(result);
|
|
706
|
+
if (looksLikeRawToolOutput(visibleOutput)) {
|
|
707
|
+
return formatDelegatedRawToolEvidenceReport({
|
|
708
|
+
subagentName: selectedSubagent.name,
|
|
709
|
+
output: visibleOutput,
|
|
710
|
+
result,
|
|
711
|
+
});
|
|
712
|
+
}
|
|
446
713
|
if (visibleOutput) {
|
|
447
714
|
return visibleOutput;
|
|
448
715
|
}
|
|
449
716
|
const fallbackOutput = extractToolFallbackContext(result);
|
|
717
|
+
if (looksLikeRawToolOutput(fallbackOutput)) {
|
|
718
|
+
return formatDelegatedRawToolEvidenceReport({
|
|
719
|
+
subagentName: selectedSubagent.name,
|
|
720
|
+
output: fallbackOutput,
|
|
721
|
+
result,
|
|
722
|
+
});
|
|
723
|
+
}
|
|
450
724
|
return fallbackOutput || "Task completed";
|
|
451
725
|
}
|
|
452
726
|
export async function resolveBuiltinMiddlewareTools(input) {
|