@botbotgo/agent-harness 0.0.465 → 0.0.467

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.
@@ -211,6 +211,10 @@ export type CompiledBuiltinToolsConfig = {
211
211
  };
212
212
  export type CompiledExecutionContract = {
213
213
  requiresPlan?: boolean;
214
+ recoveryEvidenceTool?: {
215
+ name: string;
216
+ args?: Record<string, unknown>;
217
+ };
214
218
  };
215
219
  export type LangChainAgentParams = {
216
220
  model: CompiledModel;
@@ -1,2 +1,2 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.465";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.467";
2
2
  export declare const AGENT_HARNESS_RELEASE_DATE = "2026-05-04";
@@ -1,2 +1,2 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.465";
1
+ export const AGENT_HARNESS_VERSION = "0.0.467";
2
2
  export const AGENT_HARNESS_RELEASE_DATE = "2026-05-04";
@@ -25,8 +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
+ "Honor any structured workspace or runtime evidence-tool contract already supplied in this turn.",
29
+ "Do not choose tools by matching free-form request or TODO text.",
30
30
  "After that evidence tool returns, update the todo board and then provide the final answer required by the agent response format.",
31
31
  ].join("\n");
32
32
  function readPrimaryToolName(tool) {
@@ -770,8 +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
+ "Honor any structured workspace or runtime evidence-tool contract already supplied in this turn.",
774
+ "Do not choose tools by matching free-form request or TODO text.",
775
775
  "Do not call write_todos or read_todos now.",
776
776
  "Do not write prose, markdown, analysis, or a plain-text plan.",
777
777
  ].join("\n")
@@ -396,26 +396,16 @@ function buildDelegatedPlanEvidenceRecoveryOptions(binding, baseOptions, request
396
396
  const nonPlanningTools = binding
397
397
  ? getBindingPrimaryTools(binding).filter((tool) => !isPlanToolName(tool.name))
398
398
  : [];
399
- const explicitlyRequestedTools = resolveExplicitlyRequestedToolNames(requestText, nonPlanningTools.map((tool) => tool.name));
400
- const externalPlanEvidenceTools = explicitlyRequestedTools.length === 1
401
- ? [{
402
- name: explicitlyRequestedTools[0],
403
- args: buildExternalPlanEvidenceArgs(nonPlanningTools.find((tool) => tool.name === explicitlyRequestedTools[0]), requestText),
404
- id: "delegated-plan-evidence-tool-1",
405
- }]
399
+ const configuredRecoveryTool = resolveConfiguredRecoveryEvidenceTool(binding, nonPlanningTools, requestText);
400
+ const externalPlanEvidenceTools = configuredRecoveryTool
401
+ ? [configuredRecoveryTool]
406
402
  : nonPlanningTools.length === 1
407
403
  ? [{
408
404
  name: nonPlanningTools[0].name,
409
405
  args: buildExternalPlanEvidenceArgs(nonPlanningTools[0], requestText),
410
406
  id: "delegated-plan-evidence-tool-1",
411
407
  }]
412
- : nonPlanningTools.length > 1
413
- ? [{
414
- name: nonPlanningTools[0].name,
415
- args: buildExternalPlanEvidenceArgs(nonPlanningTools[0], requestText),
416
- id: "delegated-plan-evidence-tool-1",
417
- }]
418
- : undefined;
408
+ : undefined;
419
409
  return {
420
410
  ...baseOptions,
421
411
  suppressInitialRequiredPlanInstruction: true,
@@ -427,6 +417,22 @@ function buildDelegatedPlanEvidenceRecoveryOptions(binding, baseOptions, request
427
417
  },
428
418
  };
429
419
  }
420
+ function resolveConfiguredRecoveryEvidenceTool(binding, nonPlanningTools, requestText) {
421
+ const configured = binding?.harnessRuntime.executionContract?.recoveryEvidenceTool;
422
+ const configuredName = typeof configured?.name === "string" ? configured.name.trim() : "";
423
+ if (!configuredName) {
424
+ return undefined;
425
+ }
426
+ const tool = nonPlanningTools.find((candidate) => candidate.name === configuredName);
427
+ if (!tool) {
428
+ return undefined;
429
+ }
430
+ return {
431
+ name: tool.name,
432
+ args: configured?.args ?? buildExternalPlanEvidenceArgs(tool, requestText),
433
+ id: "delegated-plan-evidence-tool-1",
434
+ };
435
+ }
430
436
  function buildExternalPlanEvidenceArgs(tool, requestText) {
431
437
  const properties = tool?.modelSchema && typeof tool.modelSchema === "object"
432
438
  && tool.modelSchema !== null
@@ -453,21 +459,6 @@ function buildExternalPlanEvidenceArgs(tool, requestText) {
453
459
  }
454
460
  return args;
455
461
  }
456
- function escapeRegExp(value) {
457
- return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
458
- }
459
- function resolveExplicitlyRequestedToolNames(text, availableToolNames) {
460
- if (!text.trim() || availableToolNames.length === 0) {
461
- return [];
462
- }
463
- return availableToolNames.filter((toolName) => {
464
- const trimmed = toolName.trim();
465
- if (!trimmed) {
466
- return false;
467
- }
468
- return new RegExp(`(^|[^A-Za-z0-9_])${escapeRegExp(trimmed)}([^A-Za-z0-9_]|$)`, "u").test(text);
469
- });
470
- }
471
462
  function looksLikeRawCommandTranscript(value) {
472
463
  const normalized = value.trim();
473
464
  return /^(?:stdout|stderr)\s*:/iu.test(normalized)
@@ -282,8 +282,30 @@ function resolveExecutionContract(agent) {
282
282
  if (!value) {
283
283
  return undefined;
284
284
  }
285
+ const recoveryEvidenceTool = typeof value.recoveryEvidenceTool === "string" && value.recoveryEvidenceTool.trim().length > 0
286
+ ? { name: value.recoveryEvidenceTool.trim() }
287
+ : typeof value.recoveryEvidenceTool === "object" && value.recoveryEvidenceTool && !Array.isArray(value.recoveryEvidenceTool)
288
+ ? value.recoveryEvidenceTool
289
+ : undefined;
290
+ const recoveryEvidenceToolName = recoveryEvidenceTool && typeof recoveryEvidenceTool.name === "string" && recoveryEvidenceTool.name.trim().length > 0
291
+ ? recoveryEvidenceTool.name.trim()
292
+ : undefined;
293
+ const recoveryEvidenceToolArgs = recoveryEvidenceTool
294
+ && typeof recoveryEvidenceTool.args === "object"
295
+ && recoveryEvidenceTool.args
296
+ && !Array.isArray(recoveryEvidenceTool.args)
297
+ ? recoveryEvidenceTool.args
298
+ : undefined;
285
299
  return {
286
300
  ...(value.requiresPlan === true ? { requiresPlan: true } : {}),
301
+ ...(recoveryEvidenceToolName
302
+ ? {
303
+ recoveryEvidenceTool: {
304
+ name: recoveryEvidenceToolName,
305
+ ...(recoveryEvidenceToolArgs ? { args: recoveryEvidenceToolArgs } : {}),
306
+ },
307
+ }
308
+ : {}),
287
309
  };
288
310
  }
289
311
  function resolveCompiledMiddleware(agent, models) {
@@ -50,6 +50,16 @@ function readExecutionContractConfig(agent) {
50
50
  ? value
51
51
  : undefined;
52
52
  }
53
+ function readRecoveryEvidenceToolName(value) {
54
+ if (typeof value === "string") {
55
+ return value.trim();
56
+ }
57
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
58
+ return "";
59
+ }
60
+ const name = value.name;
61
+ return typeof name === "string" ? name.trim() : "";
62
+ }
53
63
  function collectAgentToolNames(agent, tools, ownsDelegation) {
54
64
  const names = new Set(FRAMEWORK_EXECUTION_TOOL_NAMES);
55
65
  if (ownsDelegation) {
@@ -208,6 +218,13 @@ function validateAgentContract(agent, referencedSubagentIds, tools, refs, issues
208
218
  if (executionContract?.requiresPlan === true && builtinTools?.todos === false) {
209
219
  addIssue(issues, "agent.execution_contract.plan_without_todos", `Agent ${agent.id} requires plan evidence but disables todo tools. Enable todo tools or remove config.executionContract.requiresPlan.`);
210
220
  }
221
+ const recoveryEvidenceToolName = readRecoveryEvidenceToolName(executionContract?.recoveryEvidenceTool);
222
+ if (recoveryEvidenceToolName) {
223
+ const toolNames = collectAgentToolNames(agent, tools, ownsDelegation);
224
+ if (FRAMEWORK_EXECUTION_TOOL_NAMES.has(recoveryEvidenceToolName) || !toolNames.has(recoveryEvidenceToolName)) {
225
+ addIssue(issues, "agent.execution_contract.invalid_recovery_evidence_tool", `Agent ${agent.id} config.executionContract.recoveryEvidenceTool must name one declared non-planning tool.`);
226
+ }
227
+ }
211
228
  if (ownsDelegation) {
212
229
  if (hasTools) {
213
230
  addIssue(issues, "agent.orchestrator.mixed_tool_surface", `Delegating agent ${agent.id} defines both subagents and direct tools. Keep routing agents focused on delegation, and move execution tools to specialist agents.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.465",
3
+ "version": "0.0.467",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",