@botbotgo/agent-harness 0.0.380 → 0.0.381

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.380";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.381";
2
2
  export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-30";
@@ -1,2 +1,2 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.380";
1
+ export const AGENT_HARNESS_VERSION = "0.0.381";
2
2
  export const AGENT_HARNESS_RELEASE_DATE = "2026-04-30";
@@ -489,6 +489,65 @@ function normalizeParsedToolCall(payload) {
489
489
  : salvageToolArgs(argsCandidate) ?? {};
490
490
  return { name, args };
491
491
  }
492
+ function extractFallbackTodoContentsFromText(text) {
493
+ const normalized = text.trim();
494
+ if (!normalized) {
495
+ return [];
496
+ }
497
+ const candidates = normalized
498
+ .split(/\r?\n/)
499
+ .map((line) => line
500
+ .replace(/^\s*(?:[-*+]\s+|\d+[.)]\s+|\[[ x~!-]\]\s+)/i, "")
501
+ .replace(/^\s*(?:todo|step|task)\s*\d*\s*[:.-]\s*/i, "")
502
+ .trim())
503
+ .filter((line) => line.length >= 12
504
+ && !/^(?:status|summary|findings|blockers|next actions)\s*[::]?$/i.test(line)
505
+ && !/\b(?:plan|todo|steps?)\s*[::]\s*$/i.test(line)
506
+ && !isLowSignalPlanningLine(line));
507
+ const seen = new Set();
508
+ return candidates.filter((line) => {
509
+ const key = line.toLowerCase();
510
+ if (seen.has(key)) {
511
+ return false;
512
+ }
513
+ seen.add(key);
514
+ return true;
515
+ }).slice(0, 6);
516
+ }
517
+ function isLowSignalPlanningLine(value) {
518
+ const normalized = value.trim().toLowerCase();
519
+ return (normalized.length < 12
520
+ || /^#+\s*/.test(normalized)
521
+ || /^(?:ok|okay|sure|understood|got it|plan|todo|steps?)\.?$/.test(normalized));
522
+ }
523
+ function buildFallbackPlanningToolCall(input, tools, rawText) {
524
+ const toolName = tools.map((tool) => readBoundToolName(tool)).find((name) => name === "write_todos" || name === "tool_call_write_todos");
525
+ if (!toolName) {
526
+ return null;
527
+ }
528
+ const modelPlannedItems = extractFallbackTodoContentsFromText(rawText);
529
+ const fallbackItems = modelPlannedItems.length >= 2
530
+ ? modelPlannedItems
531
+ : [
532
+ "Gather concrete evidence for the requested investigation",
533
+ "Inspect the most relevant runtime signals and tool outputs",
534
+ "Analyze the evidence to identify root cause and impact",
535
+ "Produce the final RCA report with blockers and next actions",
536
+ ];
537
+ const todos = fallbackItems.map((content, index) => ({
538
+ content,
539
+ status: index === 0 ? "in_progress" : "pending",
540
+ }));
541
+ return new AIMessage({
542
+ content: "",
543
+ tool_calls: [{
544
+ id: `tool-${Math.random().toString(36).slice(2, 10)}`,
545
+ name: toolName,
546
+ args: { todos },
547
+ type: "tool_call",
548
+ }],
549
+ });
550
+ }
492
551
  function formatBoundToolInstruction(tool) {
493
552
  if (typeof tool !== "object" || tool === null) {
494
553
  return null;
@@ -554,6 +613,12 @@ function createPromptedJsonToolBindableModel(model, boundTools = [], options = {
554
613
  const text = readModelText(rawResult);
555
614
  const parsedToolCall = normalizeParsedToolCall(extractToolCallPayload(text));
556
615
  if (!parsedToolCall) {
616
+ if (forcePlanningToolCall) {
617
+ const fallbackToolCall = buildFallbackPlanningToolCall(input, effectiveBoundTools, text);
618
+ if (fallbackToolCall) {
619
+ return fallbackToolCall;
620
+ }
621
+ }
557
622
  return rawResult;
558
623
  }
559
624
  if (hasPriorToolResultForToolName(input, parsedToolCall.name)) {
@@ -48,6 +48,23 @@ function isSubstantiveTerminalAssistantOutput(value) {
48
48
  }
49
49
  return true;
50
50
  }
51
+ function inferPlanItemStatusFromTerminalAssistantOutput(value) {
52
+ const terminalStatus = readTerminalExecutionStatus(value);
53
+ if (terminalStatus) {
54
+ return mapTerminalStatusToPlanItemStatus(terminalStatus);
55
+ }
56
+ const normalized = sanitizeVisibleText(value).trim().toLowerCase();
57
+ if (!normalized) {
58
+ return null;
59
+ }
60
+ if (normalized.startsWith("runtime_error=")
61
+ || /\bterminated\b/i.test(normalized)
62
+ || /\b(?:blocked|blocker|failed|failure|refused|unable to complete|could not complete)\b/i.test(normalized)
63
+ || /(?:执行失败|未能完成|无法完成|阻塞|失败)/u.test(normalized)) {
64
+ return "failed";
65
+ }
66
+ return isSubstantiveTerminalAssistantOutput(value) ? "completed" : null;
67
+ }
51
68
  function reconcilePlanStateToTerminalStatus(planState, status, updatedAt) {
52
69
  const items = planState.items.map((item) => ({
53
70
  ...item,
@@ -1026,8 +1043,9 @@ export async function* streamHarnessRun(options) {
1026
1043
  }
1027
1044
  }
1028
1045
  currentPlanState = await refreshPlanStateFromPersistence(options, currentPlanState);
1029
- if (isSubstantiveTerminalAssistantOutput(assistantOutput) && planStateHasActiveItems(currentPlanState)) {
1030
- const reconciledPlanState = reconcilePlanStateToTerminalStatus(currentPlanState, "completed", new Date().toISOString());
1046
+ const terminalAssistantPlanItemStatus = inferPlanItemStatusFromTerminalAssistantOutput(assistantOutput);
1047
+ if (terminalAssistantPlanItemStatus && planStateHasActiveItems(currentPlanState)) {
1048
+ const reconciledPlanState = reconcilePlanStateToTerminalStatus(currentPlanState, terminalAssistantPlanItemStatus, new Date().toISOString());
1031
1049
  const signature = buildPlanStateSignature(reconciledPlanState);
1032
1050
  if (signature !== lastPlanStateSignature) {
1033
1051
  const previousPlanState = currentPlanState;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.380",
3
+ "version": "0.0.381",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",