@botbotgo/agent-harness 0.0.365 → 0.0.367

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.365";
2
- export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-28";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.367";
2
+ export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-30";
@@ -1,2 +1,2 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.365";
2
- export const AGENT_HARNESS_RELEASE_DATE = "2026-04-28";
1
+ export const AGENT_HARNESS_VERSION = "0.0.367";
2
+ export const AGENT_HARNESS_RELEASE_DATE = "2026-04-30";
@@ -8,6 +8,19 @@ import { appendToolRecoveryInstruction, extractVisibleOutput, tryParseJson } fro
8
8
  import { salvageJsonToolCalls } from "../../parsing/output-tool-args.js";
9
9
  import { isEmptyFinalAiMessageError } from "../resilience.js";
10
10
  import { AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION, } from "../../prompts/runtime-prompts.js";
11
+ const CLOSE_REQUIRED_PLAN_RECOVERY_INSTRUCTION = [
12
+ "The current required todo board still has unfinished work.",
13
+ "Do not broaden the investigation, restart planning, or ask the user what to do next.",
14
+ "Use the existing tool evidence already available in this run.",
15
+ "Your next action must be write_todos: update every remaining pending or in_progress item to completed if evidence was gathered, or failed if it cannot be completed with the available tools.",
16
+ "After that write_todos call, provide the final answer required by the agent response format.",
17
+ ].join("\n");
18
+ const INITIAL_REQUIRED_PLAN_INSTRUCTION = [
19
+ "This agent has a required visible planning contract.",
20
+ "Your first action for this request must be write_todos with concrete task steps and statuses.",
21
+ "Do not call any domain/evidence tool and do not provide a final answer before the initial write_todos call succeeds.",
22
+ "After each evidence step, update the todo board. Before the final answer, close every todo as completed or failed.",
23
+ ].join("\n");
11
24
  function readBindingExecutionParams(binding) {
12
25
  const params = binding.execution?.params ?? binding.deepAgentParams ?? binding.langchainAgentParams;
13
26
  return {
@@ -280,9 +293,13 @@ function appendUserRecoveryInstruction(input, instruction) {
280
293
  export async function executeRequestInvocation(options) {
281
294
  const history = options.history ?? [];
282
295
  const invokeOptions = options.invokeOptions ?? {};
283
- const request = options.resumePayload === undefined
296
+ let request = options.resumePayload === undefined
284
297
  ? buildInvocationRequest(options.binding, history, options.input, invokeOptions)
285
298
  : new Command({ resume: options.resumePayload });
299
+ if (options.resumePayload === undefined
300
+ && options.binding.harnessRuntime.executionContract?.requiresPlan === true) {
301
+ request = appendToolRecoveryInstruction(request, INITIAL_REQUIRED_PLAN_INSTRUCTION);
302
+ }
286
303
  const { primaryTools, toolNameMapping, executableTools, defersToUpstreamHitlExecution, } = buildBindingToolExecutionContext({
287
304
  binding: options.binding,
288
305
  resolveTools: options.resolveTools,
@@ -335,7 +352,7 @@ export async function executeRequestInvocation(options) {
335
352
  ? result.messages
336
353
  : undefined;
337
354
  const recoveryBase = messages ? { messages } : request;
338
- const recoveredRequest = appendToolRecoveryInstruction(recoveryBase, AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION);
355
+ const recoveredRequest = appendToolRecoveryInstruction(recoveryBase, CLOSE_REQUIRED_PLAN_RECOVERY_INSTRUCTION);
339
356
  const recoveredInvocation = await invokeOnce(recoveredRequest);
340
357
  localOrUpstreamInvocation = recoveredInvocation;
341
358
  result = recoveredInvocation.result;
@@ -343,8 +360,7 @@ export async function executeRequestInvocation(options) {
343
360
  }
344
361
  if (options.resumePayload === undefined
345
362
  && options.binding.harnessRuntime.executionContract?.requiresPlan === true
346
- && hasIncompleteUpstreamPlan(result)
347
- && !extractVisibleOutput(result).trim()) {
363
+ && hasIncompleteUpstreamPlan(result)) {
348
364
  const messages = Array.isArray(result.messages)
349
365
  ? result.messages
350
366
  : undefined;
@@ -164,6 +164,11 @@ function hasDelegatedPlanEvidence(result) {
164
164
  return Array.isArray(toolResults)
165
165
  && toolResults.some((item) => item.toolName === "write_todos" || item.toolName === "read_todos");
166
166
  }
167
+ const DELEGATED_PLAN_EVIDENCE_RETRY_INSTRUCTION = [
168
+ "The delegated task requires visible TODO planning evidence.",
169
+ "Before any other tool call or final answer, call write_todos with concrete task steps and statuses.",
170
+ "Then continue the task to completion, update TODO statuses after evidence steps, and close every TODO as completed or failed before the final answer.",
171
+ ].join("\n");
167
172
  function resolveDelegatedResultOutput(result) {
168
173
  const executedToolResults = Array.isArray(result.metadata?.executedToolResults)
169
174
  ? result.metadata.executedToolResults
@@ -936,21 +941,62 @@ export class AgentRuntimeAdapter {
936
941
  delegatedResult = await runDelegatedRequest(requestText);
937
942
  }
938
943
  catch (error) {
939
- const output = error instanceof Error ? error.message : String(error);
940
- return {
941
- toolOutput: output,
942
- delegatedSubagentType: subagentType,
943
- delegatedResult: {
944
- sessionId,
945
- requestId,
946
- agentId: selectedBinding.agent.id,
947
- state: "failed",
948
- output,
949
- finalMessageText: output,
950
- },
951
- };
944
+ if (selectedBinding.harnessRuntime.executionContract?.requiresPlan === true) {
945
+ try {
946
+ delegatedResult = await runDelegatedRequest([requestText, DELEGATED_PLAN_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":plan-evidence-retry");
947
+ }
948
+ catch (recoveryError) {
949
+ const output = recoveryError instanceof Error ? recoveryError.message : String(recoveryError);
950
+ return {
951
+ toolOutput: output,
952
+ delegatedSubagentType: subagentType,
953
+ delegatedResult: {
954
+ sessionId,
955
+ requestId,
956
+ agentId: selectedBinding.agent.id,
957
+ state: "failed",
958
+ output,
959
+ finalMessageText: output,
960
+ },
961
+ };
962
+ }
963
+ }
964
+ else {
965
+ const output = error instanceof Error ? error.message : String(error);
966
+ return {
967
+ toolOutput: output,
968
+ delegatedSubagentType: subagentType,
969
+ delegatedResult: {
970
+ sessionId,
971
+ requestId,
972
+ agentId: selectedBinding.agent.id,
973
+ state: "failed",
974
+ output,
975
+ finalMessageText: output,
976
+ },
977
+ };
978
+ }
952
979
  }
953
980
  const targetRequiresExecutionToolEvidence = getBindingPrimaryTools(selectedBinding).length > 0;
981
+ if (selectedBinding.harnessRuntime.executionContract?.requiresPlan === true
982
+ && !hasDelegatedPlanEvidence(delegatedResult)) {
983
+ try {
984
+ delegatedResult = await runDelegatedRequest([requestText, DELEGATED_PLAN_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":plan-evidence-retry");
985
+ }
986
+ catch (error) {
987
+ const output = error instanceof Error ? error.message : String(error);
988
+ return {
989
+ toolOutput: output,
990
+ delegatedSubagentType: subagentType,
991
+ delegatedResult: {
992
+ ...delegatedResult,
993
+ state: "failed",
994
+ output,
995
+ finalMessageText: output,
996
+ },
997
+ };
998
+ }
999
+ }
954
1000
  if (targetRequiresExecutionToolEvidence && !hasDelegatedExecutionToolEvidence(delegatedResult)) {
955
1001
  try {
956
1002
  delegatedResult = await runDelegatedRequest([requestText, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":tool-evidence-retry");
@@ -969,8 +1015,9 @@ export class AgentRuntimeAdapter {
969
1015
  };
970
1016
  }
971
1017
  }
972
- if (targetRequiresExecutionToolEvidence && !hasDelegatedExecutionToolEvidence(delegatedResult)) {
973
- const output = `runtime_error=Delegated agent ${selectedBinding.agent.id} completed without tool execution evidence.`;
1018
+ if (selectedBinding.harnessRuntime.executionContract?.requiresPlan === true
1019
+ && !hasDelegatedPlanEvidence(delegatedResult)) {
1020
+ const output = "runtime_error=Delegated agent ended before producing required plan evidence.";
974
1021
  return {
975
1022
  toolOutput: output,
976
1023
  delegatedSubagentType: subagentType,
@@ -982,10 +1029,8 @@ export class AgentRuntimeAdapter {
982
1029
  },
983
1030
  };
984
1031
  }
985
- if (selectedBinding.harnessRuntime.executionContract?.requiresPlan === true
986
- && !hasDelegatedPlanEvidence(delegatedResult)
987
- && !hasDelegatedExecutionToolEvidence(delegatedResult)) {
988
- const output = "runtime_error=Delegated agent ended before producing required plan evidence.";
1032
+ if (targetRequiresExecutionToolEvidence && !hasDelegatedExecutionToolEvidence(delegatedResult)) {
1033
+ const output = `runtime_error=Delegated agent ${selectedBinding.agent.id} completed without tool execution evidence.`;
989
1034
  return {
990
1035
  toolOutput: output,
991
1036
  delegatedSubagentType: subagentType,
@@ -1008,12 +1053,66 @@ export class AgentRuntimeAdapter {
1008
1053
  const delegatedOutput = typeof compactDelegation.toolOutput === "string"
1009
1054
  ? [compactDelegation.toolOutput]
1010
1055
  : [];
1056
+ const delegatedPayload = (() => {
1057
+ if (delegatedOutput.length === 0) {
1058
+ return undefined;
1059
+ }
1060
+ try {
1061
+ const parsed = JSON.parse(delegatedOutput[0]);
1062
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
1063
+ ? parsed
1064
+ : undefined;
1065
+ }
1066
+ catch {
1067
+ return undefined;
1068
+ }
1069
+ })();
1070
+ const payloadStringArray = (key) => {
1071
+ const value = delegatedPayload?.[key];
1072
+ if (!Array.isArray(value)) {
1073
+ return undefined;
1074
+ }
1075
+ const items = value.filter((item) => typeof item === "string" && item.trim().length > 0);
1076
+ return items.length > 0 ? items : undefined;
1077
+ };
1078
+ const payloadReport = typeof delegatedPayload?.report === "string" && delegatedPayload.report.trim().length > 0
1079
+ ? delegatedPayload.report
1080
+ : undefined;
1011
1081
  const delegatedToolNames = Array.isArray(compactDelegation.delegatedResult?.metadata?.executedToolResults)
1012
1082
  ? compactDelegation.delegatedResult.metadata.executedToolResults
1013
1083
  .filter((toolResult) => toolResult?.toolName)
1014
1084
  .map((toolResult) => toolResult.toolName)
1015
1085
  : [];
1016
1086
  const state = compactDelegation.delegatedResult?.state === "failed" ? "failed" : "completed";
1087
+ const uniqueToolNames = [...new Set(delegatedToolNames)];
1088
+ const toolEvidence = uniqueToolNames.length > 0 ? uniqueToolNames.join(", ") : "none";
1089
+ const fallbackTodoTrace = [
1090
+ `1) TODO observed: delegated to ${delegatedSubagentType}.`,
1091
+ uniqueToolNames.includes("write_todos")
1092
+ ? "2) TODO evidence: delegated specialist invoked write_todos."
1093
+ : "2) TODO evidence missing: delegated specialist did not expose write_todos in returned metadata.",
1094
+ state === "failed"
1095
+ ? "3) TODO closed: delegated execution failed; blocker reported."
1096
+ : "3) TODO closed: delegated execution completed; synthesis returned.",
1097
+ ];
1098
+ const fallbackStepResults = [
1099
+ `1) Delegation step: task invoked ${delegatedSubagentType}.`,
1100
+ `2) Evidence step: delegated tool evidence = ${toolEvidence}.`,
1101
+ state === "failed"
1102
+ ? "3) Synthesis step: returned blocker report because delegated execution failed."
1103
+ : "3) Synthesis step: compact delegation report assembled from delegated output.",
1104
+ ];
1105
+ const fallbackSummary = [
1106
+ state === "failed"
1107
+ ? `子代理 ${delegatedSubagentType} 委托执行失败。`
1108
+ : `已完成子代理 ${delegatedSubagentType} 委托执行。`,
1109
+ ];
1110
+ const fallbackFindings = payloadReport
1111
+ ? ["子代理返回了结构化报告,详见 report。"]
1112
+ : delegatedOutput.length > 0
1113
+ ? delegatedOutput.slice(0, 3)
1114
+ : ["none"];
1115
+ const report = payloadReport ?? (delegatedOutput.join("\n") || "委托已完成,未返回附加报告。");
1017
1116
  return {
1018
1117
  status: state,
1019
1118
  routing: [
@@ -1026,18 +1125,18 @@ export class AgentRuntimeAdapter {
1026
1125
  ],
1027
1126
  execution: [
1028
1127
  `1) 调用 task 工具,目标子代理:${delegatedSubagentType}`,
1029
- delegatedToolNames.length > 0
1030
- ? `2) 子代理返回工具证据:${[...new Set(delegatedToolNames)].join(", ")}`
1128
+ uniqueToolNames.length > 0
1129
+ ? `2) 子代理返回工具证据:${toolEvidence}`
1031
1130
  : "2) 子代理返回文本结果。",
1032
1131
  "3) 产出主编排汇总并返回结构化结果。",
1033
1132
  ],
1034
- summary: [
1035
- `已完成子代理 ${delegatedSubagentType} 委托执行。`,
1036
- ],
1037
- findings: delegatedOutput.length > 0 ? delegatedOutput.slice(0, 3) : ["none"],
1133
+ todoTrace: payloadStringArray("todoTrace") ?? fallbackTodoTrace,
1134
+ stepResults: payloadStringArray("stepResults") ?? fallbackStepResults,
1135
+ summary: payloadStringArray("summary") ?? fallbackSummary,
1136
+ findings: payloadStringArray("findings") ?? fallbackFindings,
1038
1137
  blockers: state === "failed" ? ["子代理执行未能完成。"] : ["none"],
1039
1138
  nextActions: ["如需更深入,可继续追问该次委托的细节。"],
1040
- report: delegatedOutput.join("\n") || "委托已完成,未返回附加报告。",
1139
+ report,
1041
1140
  };
1042
1141
  }
1043
1142
  async *stream(binding, input, sessionId, history = [], options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.365",
3
+ "version": "0.0.367",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",