@botbotgo/agent-harness 0.0.390 → 0.0.391

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.390";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.391";
2
2
  export declare const AGENT_HARNESS_RELEASE_DATE = "2026-05-01";
@@ -1,2 +1,2 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.390";
1
+ export const AGENT_HARNESS_VERSION = "0.0.391";
2
2
  export const AGENT_HARNESS_RELEASE_DATE = "2026-05-01";
@@ -119,6 +119,15 @@ function hasMissingDelegatedExecutionEvidence(evidence) {
119
119
  function hasMissingDelegatedFindings(evidence) {
120
120
  return evidence.hasDelegatedAgentWithConfiguredTools && evidence.hasOnlyPlaceholderTaskCompletion;
121
121
  }
122
+ function hasCompletedPlanWithEvidence(evidence) {
123
+ return evidence.hasPlanStateEvidence
124
+ && !evidence.hasIncompletePlanState
125
+ && evidence.hasSuccessfulNonTodoToolResultEvidence
126
+ && !evidence.hasOpenTaskDelegation
127
+ && !evidence.hasFailedTaskDelegation
128
+ && !hasMissingDelegatedExecutionEvidence(evidence)
129
+ && !hasMissingDelegatedFindings(evidence);
130
+ }
122
131
  function readBindingExecutionParams(binding) {
123
132
  const params = binding.execution?.params ?? binding.deepAgentParams ?? binding.langchainAgentParams;
124
133
  return {
@@ -549,6 +558,12 @@ export async function* streamRuntimeExecution(options) {
549
558
  }
550
559
  }
551
560
  const streamedExecutionEvidence = buildExecutionRecoveryEvidence({ projectionState });
561
+ if (requiresPlanEvidence(options.binding) && hasCompletedPlanWithEvidence(streamedExecutionEvidence)) {
562
+ if (deferredStreamContent.length > 0) {
563
+ yield* flushDeferredStreamContent();
564
+ }
565
+ return;
566
+ }
552
567
  const streamedDelegatedRecoveryInstruction = resolveDelegatedExecutionRecoveryInstruction(streamedExecutionEvidence);
553
568
  const streamedDelegationOnlyRecoveryInstruction = resolveDelegationOnlyRecoveryInstruction(options.binding, streamedExecutionEvidence);
554
569
  const streamedIncompletePlanRecoveryInstruction = requiresPlanEvidence(options.binding) && streamedExecutionEvidence.hasIncompletePlanState
@@ -41,6 +41,49 @@ function hasIncompleteExecutedPlan(executedToolResults) {
41
41
  function hasNonTodoToolEvidence(executedToolResults) {
42
42
  return executedToolResults.some((item) => item.toolName !== "write_todos" && item.toolName !== "read_todos");
43
43
  }
44
+ function isPlanToolName(toolName) {
45
+ return toolName === "write_todos"
46
+ || toolName === "read_todos"
47
+ || toolName === "tool_call_write_todos"
48
+ || toolName === "tool_call_read_todos";
49
+ }
50
+ function isFallbackTodoCompletionToolCall(toolCall) {
51
+ return typeof toolCall.id === "string"
52
+ && toolCall.id.startsWith("fallback-complete-")
53
+ && (toolCall.name === "write_todos" || toolCall.name === "tool_call_write_todos");
54
+ }
55
+ function isCompletedTodoUpdateToolCall(toolCall) {
56
+ if (toolCall.name !== "write_todos" && toolCall.name !== "tool_call_write_todos") {
57
+ return false;
58
+ }
59
+ if (typeof toolCall.args !== "object" || toolCall.args === null || !Array.isArray(toolCall.args.todos)) {
60
+ return false;
61
+ }
62
+ const todos = toolCall.args.todos;
63
+ return todos.length > 0 && todos.every((todo) => typeof todo === "object"
64
+ && todo !== null
65
+ && typeof todo.status === "string"
66
+ && todo.status.trim().toLowerCase() === "completed");
67
+ }
68
+ function buildDeterministicFinalFromToolEvidence(executedToolResults) {
69
+ const evidence = executedToolResults
70
+ .filter((item) => item.isError !== true && item.toolName !== "write_todos" && item.toolName !== "read_todos")
71
+ .map((item) => {
72
+ const output = stringifyToolOutput(item.output).trim();
73
+ const clipped = output.length > 4000 ? `${output.slice(0, 4000)}\n... [truncated]` : output;
74
+ return `## ${item.toolName}\n${clipped}`;
75
+ });
76
+ const output = [
77
+ "Status: completed",
78
+ "Summary:",
79
+ "- Completed the required TODO burn down after collecting tool evidence.",
80
+ "- Returning deterministic evidence summary because the model did not provide a timely final synthesis after tool completion.",
81
+ "",
82
+ "Evidence:",
83
+ evidence.length > 0 ? evidence.join("\n\n") : "(no non-planning tool evidence captured)",
84
+ ].join("\n");
85
+ return { output };
86
+ }
44
87
  function hasPlanStateEvidence(executedToolResults) {
45
88
  return executedToolResults.some((item) => item.toolName === "write_todos" || item.toolName === "read_todos" || readPlanStateSummary(item.output) !== null);
46
89
  }
@@ -191,6 +234,16 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
191
234
  content: stringifyToolOutput(safeToolResult),
192
235
  }));
193
236
  }
237
+ if (requiresPlanEvidence(binding)
238
+ && toolCalls.length > 0
239
+ && toolCalls.every((toolCall) => isPlanToolName(toolCall.name))
240
+ && !hasIncompleteExecutedPlan(executedToolResults)
241
+ && hasNonTodoToolEvidence(executedToolResults)) {
242
+ return {
243
+ result: buildDeterministicFinalFromToolEvidence(executedToolResults),
244
+ executedToolResults,
245
+ };
246
+ }
194
247
  currentMessages = nextMessages;
195
248
  activeRequest = {
196
249
  ...activeRequest,
@@ -670,7 +670,7 @@ function buildFallbackEvidenceToolCall(input, tools) {
670
670
  return new AIMessage({
671
671
  content: "",
672
672
  tool_calls: [{
673
- id: `tool-${Math.random().toString(36).slice(2, 10)}`,
673
+ id: `fallback-evidence-${Math.random().toString(36).slice(2, 10)}`,
674
674
  name: boundToolName,
675
675
  args: buildFallbackEvidenceToolArgs(evidenceToolName, input),
676
676
  type: "tool_call",
@@ -703,7 +703,7 @@ function buildFallbackTodoCompletionToolCall(input, tools) {
703
703
  return new AIMessage({
704
704
  content: "",
705
705
  tool_calls: [{
706
- id: `tool-${Math.random().toString(36).slice(2, 10)}`,
706
+ id: `fallback-complete-${Math.random().toString(36).slice(2, 10)}`,
707
707
  name: planningToolName,
708
708
  args: { todos },
709
709
  type: "tool_call",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.390",
3
+ "version": "0.0.391",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",