@botbotgo/agent-harness 0.0.454 → 0.0.455

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.454";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.455";
2
2
  export declare const AGENT_HARNESS_RELEASE_DATE = "2026-05-04";
@@ -1,2 +1,2 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.454";
1
+ export const AGENT_HARNESS_VERSION = "0.0.455";
2
2
  export const AGENT_HARNESS_RELEASE_DATE = "2026-05-04";
@@ -1,4 +1,4 @@
1
- import { AIMessage, HumanMessage, ToolMessage } from "@langchain/core/messages";
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, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION, } from "../prompts/runtime-prompts.js";
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
- let result = await invokeSubagent(description);
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.454",
3
+ "version": "0.0.455",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",