@botbotgo/agent-harness 0.0.380 → 0.0.382
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.
|
|
2
|
-
export declare const AGENT_HARNESS_RELEASE_DATE = "2026-
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.382";
|
|
2
|
+
export declare const AGENT_HARNESS_RELEASE_DATE = "2026-05-01";
|
package/dist/package-version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
2
|
-
export const AGENT_HARNESS_RELEASE_DATE = "2026-
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.382";
|
|
2
|
+
export const AGENT_HARNESS_RELEASE_DATE = "2026-05-01";
|
|
@@ -47,12 +47,32 @@ function renderMarkdownTable(block) {
|
|
|
47
47
|
const columnCount = Math.max(header.length, ...body.map((row) => row.length));
|
|
48
48
|
const normalizedRows = [header, ...body].map((row) => Array.from({ length: columnCount }, (_, index) => renderInlineConsoleMarkdown(row[index] ?? "")));
|
|
49
49
|
const widths = Array.from({ length: columnCount }, (_, index) => Math.max(...normalizedRows.map((row) => stripAnsi(row[index] ?? "").length), 0));
|
|
50
|
+
const renderedWidth = widths.reduce((sum, width) => sum + width, 0) + (columnCount * 3) + 1;
|
|
51
|
+
if (renderedWidth > 120 || columnCount > 5) {
|
|
52
|
+
return renderMarkdownTableAsList(header, body);
|
|
53
|
+
}
|
|
50
54
|
const renderRow = (row) => `| ${row
|
|
51
55
|
.map((cell, index) => cell.padEnd(widths[index] + (cell.length - stripAnsi(cell).length)))
|
|
52
56
|
.join(" | ")} |`;
|
|
53
57
|
const separator = `|-${widths.map((width) => "-".repeat(Math.max(3, width))).join("-|-")}-|`;
|
|
54
58
|
return [renderRow(normalizedRows[0] ?? []), separator, ...normalizedRows.slice(1).map(renderRow)].join("\n");
|
|
55
59
|
}
|
|
60
|
+
function renderMarkdownTableAsList(header, body) {
|
|
61
|
+
const normalizedHeader = header.map((cell) => cell.trim()).filter(Boolean);
|
|
62
|
+
return body
|
|
63
|
+
.map((row, rowIndex) => {
|
|
64
|
+
const title = renderInlineConsoleMarkdown(row[0]?.trim() || `Row ${rowIndex + 1}`);
|
|
65
|
+
const details = normalizedHeader.slice(1).flatMap((label, index) => {
|
|
66
|
+
const value = row[index + 1]?.trim();
|
|
67
|
+
if (!value) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
return ` • ${renderInlineConsoleMarkdown(label)}: ${renderInlineConsoleMarkdown(value)}`;
|
|
71
|
+
});
|
|
72
|
+
return [`• ${title}`, ...details].join("\n");
|
|
73
|
+
})
|
|
74
|
+
.join("\n\n");
|
|
75
|
+
}
|
|
56
76
|
export function markdownToHtml(markdown) {
|
|
57
77
|
const normalized = markdown.replace(/\r\n/g, "\n");
|
|
58
78
|
const blocks = normalized.split(/\n\n+/);
|
|
@@ -96,7 +116,10 @@ export function markdownToConsole(markdown) {
|
|
|
96
116
|
if (!markdown.includes("\n")) {
|
|
97
117
|
return renderInlineConsoleMarkdown(markdown.trim());
|
|
98
118
|
}
|
|
99
|
-
const normalized = markdown
|
|
119
|
+
const normalized = markdown
|
|
120
|
+
.replace(/\r\n/g, "\n")
|
|
121
|
+
.replace(/^(#{1,6}\s+.+)$/gm, "\n$1\n")
|
|
122
|
+
.replace(/^(\s*[-*_=])(?:\s*\1){2,}\s*$/gm, "\n$&\n");
|
|
100
123
|
const blocks = normalized.split(/\n\n+/);
|
|
101
124
|
const rendered = [];
|
|
102
125
|
for (const block of blocks) {
|
|
@@ -128,7 +151,8 @@ export function markdownToConsole(markdown) {
|
|
|
128
151
|
const level = match?.[1].length ?? 1;
|
|
129
152
|
const title = renderInlineConsoleMarkdown(match?.[2] ?? trimmed);
|
|
130
153
|
const underline = level === 1 ? "=" : "-";
|
|
131
|
-
|
|
154
|
+
const underlineWidth = Math.min(72, Math.max(3, title.replace(/\u001b\[[0-9;]*m/g, "").length));
|
|
155
|
+
rendered.push(`\u001b[1m${title}\u001b[0m\n\u001b[2m${underline.repeat(underlineWidth)}\u001b[0m`);
|
|
132
156
|
continue;
|
|
133
157
|
}
|
|
134
158
|
if (trimmed.split("\n").every((line) => /^\s*[-*]\s+/.test(line))) {
|
|
@@ -159,7 +183,7 @@ export function markdownToConsole(markdown) {
|
|
|
159
183
|
.join("\n"));
|
|
160
184
|
continue;
|
|
161
185
|
}
|
|
162
|
-
if (/^([-*_])(?:\s*\1){2,}$/.test(trimmed)) {
|
|
186
|
+
if (/^([-*_=])(?:\s*\1){2,}$/.test(trimmed)) {
|
|
163
187
|
rendered.push(`\u001b[2m${"─".repeat(24)}\u001b[0m`);
|
|
164
188
|
continue;
|
|
165
189
|
}
|
|
@@ -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
|
-
|
|
1030
|
-
|
|
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;
|