@botbotgo/agent-harness 0.0.386 → 0.0.388
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.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.388";
|
|
2
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.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.388";
|
|
2
2
|
export const AGENT_HARNESS_RELEASE_DATE = "2026-05-01";
|
|
@@ -184,16 +184,26 @@ function readToolMessageMetadata(value) {
|
|
|
184
184
|
: undefined;
|
|
185
185
|
return { name, toolCallId };
|
|
186
186
|
}
|
|
187
|
+
function canonicalToolName(value) {
|
|
188
|
+
return value
|
|
189
|
+
.trim()
|
|
190
|
+
.toLowerCase()
|
|
191
|
+
.replace(/^tool_call_/, "")
|
|
192
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
193
|
+
.replace(/^_+|_+$/g, "");
|
|
194
|
+
}
|
|
187
195
|
function hasPriorToolResultForToolName(input, toolName) {
|
|
188
196
|
if (!toolName) {
|
|
189
197
|
return false;
|
|
190
198
|
}
|
|
199
|
+
const expectedName = canonicalToolName(toolName);
|
|
191
200
|
if (Array.isArray(input)) {
|
|
192
201
|
return input.some((message) => {
|
|
193
202
|
if (mapMessageRole(message) !== "TOOL") {
|
|
194
203
|
return false;
|
|
195
204
|
}
|
|
196
|
-
|
|
205
|
+
const observedName = readToolMessageMetadata(message).name;
|
|
206
|
+
return typeof observedName === "string" && canonicalToolName(observedName) === expectedName;
|
|
197
207
|
});
|
|
198
208
|
}
|
|
199
209
|
if (typeof input === "object" && input !== null && Array.isArray(input.messages)) {
|
|
@@ -212,6 +222,12 @@ function isTodoPlanningToolName(name) {
|
|
|
212
222
|
|| name === "tool_call_write_todos"
|
|
213
223
|
|| name === "tool_call_read_todos";
|
|
214
224
|
}
|
|
225
|
+
function hasPriorNonPlanningToolResult(input, tools) {
|
|
226
|
+
const toolNames = tools
|
|
227
|
+
.map((tool) => readBoundToolName(tool))
|
|
228
|
+
.filter((name) => name && !isTodoPlanningToolName(name));
|
|
229
|
+
return toolNames.some((name) => hasPriorToolResultForToolName(input, name));
|
|
230
|
+
}
|
|
215
231
|
function shouldLimitToolsToPlanning(input) {
|
|
216
232
|
const text = stringifyNodeLlamaCppInput(input);
|
|
217
233
|
return text.includes("required visible planning contract")
|
|
@@ -520,20 +536,65 @@ function isLowSignalPlanningLine(value) {
|
|
|
520
536
|
|| /^#+\s*/.test(normalized)
|
|
521
537
|
|| /^(?:ok|okay|sure|understood|got it|plan|todo|steps?)\.?$/.test(normalized));
|
|
522
538
|
}
|
|
523
|
-
function
|
|
524
|
-
|
|
539
|
+
function normalizeDomainToolName(name) {
|
|
540
|
+
return name.startsWith("tool_call_") ? name.slice("tool_call_".length) : name;
|
|
541
|
+
}
|
|
542
|
+
function selectFallbackEvidenceToolName(input, tools) {
|
|
543
|
+
const prompt = stringifyNodeLlamaCppInput(input).toLowerCase();
|
|
544
|
+
const available = tools
|
|
545
|
+
.map((tool) => normalizeDomainToolName(readBoundToolName(tool)))
|
|
546
|
+
.filter((name) => name && !isTodoPlanningToolName(name) && !isTodoPlanningToolName(`tool_call_${name}`));
|
|
547
|
+
const hasTool = (name) => available.includes(name);
|
|
548
|
+
const rules = [
|
|
549
|
+
[/\b(k8s|kubernetes|kubectl|pod|pods|node|nodes|cluster)\b|集群|节点|调度/u, ["k8s_cluster_investigate", "k8s_cluster_triage", "kubectl_command"]],
|
|
550
|
+
[/\b(disk|cache|storage|workspace)\b|磁盘|缓存|占用/u, ["disk_space_investigate"]],
|
|
551
|
+
[/\b(agent config|agent configuration|repository structure|repo structure|code evidence|specialist)\b|agent\s*配置|配置结构|新增\s*specialist|代码层证据|改哪些文件/u, ["git_command", "codex_repo_analysis"]],
|
|
552
|
+
[/\b(stock|ticker|finance|market brief|aapl|wday)\b|股票|公开股票简报|金融|市场简报/u, ["finance_stock_report", "finance_quote_snapshot"]],
|
|
553
|
+
[/\b(test|qa|coverage|regression|playwright)\b|测试|覆盖率|回归|验证/u, ["git_command", "codex_repo_analysis", "playwright_capture"]],
|
|
554
|
+
[/\b(release|readiness|branch|tag|github actions|ci)\b|发版|分支|流水线|可发版/u, ["git_command", "gh_actions_command"]],
|
|
555
|
+
[/\b(youtube|transcript|brief|briefing|summary)\b|摘要|简报|讲稿/u, ["youtube_video_summary", "llamaindex_source_analysis", "finance_stock_report"]],
|
|
556
|
+
];
|
|
557
|
+
for (const [pattern, toolNames] of rules) {
|
|
558
|
+
if (!pattern.test(prompt)) {
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
const matched = toolNames.find((name) => hasTool(name));
|
|
562
|
+
if (matched) {
|
|
563
|
+
return matched;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return available[0] ?? null;
|
|
567
|
+
}
|
|
568
|
+
function buildToolAwareFallbackTodoContents(input, tools) {
|
|
569
|
+
const evidenceToolName = selectFallbackEvidenceToolName(input, tools);
|
|
570
|
+
if (!evidenceToolName) {
|
|
571
|
+
return [
|
|
572
|
+
"Identify the concrete evidence tool required for this request",
|
|
573
|
+
"Run the selected non-planning evidence tool and inspect its result",
|
|
574
|
+
"Update TODO status from the observed evidence",
|
|
575
|
+
"Return the final answer grounded in tool output",
|
|
576
|
+
];
|
|
577
|
+
}
|
|
578
|
+
return [
|
|
579
|
+
`Run ${evidenceToolName} for the requested evidence`,
|
|
580
|
+
`Inspect the ${evidenceToolName} result and extract concrete findings`,
|
|
581
|
+
"Update TODO status from the observed evidence",
|
|
582
|
+
"Return the final answer grounded in tool output",
|
|
583
|
+
];
|
|
584
|
+
}
|
|
585
|
+
function isGenericFallbackTodoContent(value) {
|
|
586
|
+
return /^(?:gather concrete evidence|inspect the most relevant runtime signals|analyze (?:the )?evidence|produce the final rca)/i.test(value.trim());
|
|
587
|
+
}
|
|
588
|
+
function buildFallbackPlanningToolCall(input, planningTools, allTools, rawText) {
|
|
589
|
+
const toolName = planningTools.map((tool) => readBoundToolName(tool)).find((name) => name === "write_todos" || name === "tool_call_write_todos");
|
|
525
590
|
if (!toolName) {
|
|
526
591
|
return null;
|
|
527
592
|
}
|
|
528
593
|
const modelPlannedItems = extractFallbackTodoContentsFromText(rawText);
|
|
529
|
-
const
|
|
594
|
+
const hasUsefulModelPlan = modelPlannedItems.length >= 2 && !modelPlannedItems.every(isGenericFallbackTodoContent);
|
|
595
|
+
const fallbackItems = hasUsefulModelPlan
|
|
530
596
|
? 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
|
-
];
|
|
597
|
+
: buildToolAwareFallbackTodoContents(input, allTools);
|
|
537
598
|
const todos = fallbackItems.map((content, index) => ({
|
|
538
599
|
content,
|
|
539
600
|
status: index === 0 ? "in_progress" : "pending",
|
|
@@ -548,6 +609,111 @@ function buildFallbackPlanningToolCall(input, tools, rawText) {
|
|
|
548
609
|
}],
|
|
549
610
|
});
|
|
550
611
|
}
|
|
612
|
+
function buildFallbackEvidenceToolArgs(toolName, input) {
|
|
613
|
+
const prompt = stringifyNodeLlamaCppInput(input);
|
|
614
|
+
const lower = prompt.toLowerCase();
|
|
615
|
+
if (toolName === "git_command") {
|
|
616
|
+
if (/\b(agent config|agent configuration|specialist)\b|agent\s*配置|配置结构|新增\s*specialist|代码层证据|改哪些文件/u.test(lower)) {
|
|
617
|
+
return {
|
|
618
|
+
args: ["ls-files", "config/agents", "config/agent-context.md", "config/runtime", "config/models.yaml"],
|
|
619
|
+
cwd: ".",
|
|
620
|
+
timeoutMs: 10000,
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
return { args: ["status", "--short"], cwd: ".", timeoutMs: 10000 };
|
|
624
|
+
}
|
|
625
|
+
if (toolName === "disk_space_investigate") {
|
|
626
|
+
return { targetPath: ".", cwd: ".", timeoutMs: 10000 };
|
|
627
|
+
}
|
|
628
|
+
if (toolName === "finance_stock_report") {
|
|
629
|
+
const isWorkday = /\b(wday|workday)\b|workday\s*股票/i.test(prompt);
|
|
630
|
+
const isApple = /\b(aapl|apple)\b|苹果/u.test(prompt);
|
|
631
|
+
const symbol = isWorkday ? "WDAY" : isApple ? "AAPL" : undefined;
|
|
632
|
+
const company = isWorkday ? "Workday Inc." : isApple ? "Apple Inc." : undefined;
|
|
633
|
+
return {
|
|
634
|
+
...(symbol ? { symbol } : {}),
|
|
635
|
+
...(company ? { company } : {}),
|
|
636
|
+
query: symbol ? `${company} ${symbol} stock briefing` : "public company stock briefing",
|
|
637
|
+
market: "us",
|
|
638
|
+
count: 5,
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
if (toolName === "codex_repo_analysis") {
|
|
642
|
+
return {
|
|
643
|
+
repoPath: ".",
|
|
644
|
+
question: "Analyze the repository structure and provide concrete file-level evidence for the requested code/configuration question.",
|
|
645
|
+
timeoutMs: 30000,
|
|
646
|
+
skipGitRepoCheck: true,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
if (toolName === "k8s_cluster_investigate") {
|
|
650
|
+
return { timeoutMs: 30000 };
|
|
651
|
+
}
|
|
652
|
+
if (toolName === "k8s_cluster_triage") {
|
|
653
|
+
return { timeoutMs: 30000 };
|
|
654
|
+
}
|
|
655
|
+
if (toolName === "gh_actions_command") {
|
|
656
|
+
return { args: ["list", "--limit", "5"], cwd: ".", timeoutMs: 30000 };
|
|
657
|
+
}
|
|
658
|
+
return {};
|
|
659
|
+
}
|
|
660
|
+
function buildFallbackEvidenceToolCall(input, tools) {
|
|
661
|
+
if (!hasPriorToolResultForToolName(input, "write_todos") && !hasPriorToolResultForToolName(input, "tool_call_write_todos")) {
|
|
662
|
+
return null;
|
|
663
|
+
}
|
|
664
|
+
const evidenceToolName = selectFallbackEvidenceToolName(input, tools);
|
|
665
|
+
if (!evidenceToolName) {
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
const boundToolName = tools
|
|
669
|
+
.map((tool) => readBoundToolName(tool))
|
|
670
|
+
.find((name) => normalizeDomainToolName(name) === evidenceToolName);
|
|
671
|
+
if (!boundToolName || hasPriorToolResultForToolName(input, boundToolName) || hasPriorToolResultForToolName(input, evidenceToolName)) {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
return new AIMessage({
|
|
675
|
+
content: "",
|
|
676
|
+
tool_calls: [{
|
|
677
|
+
id: `tool-${Math.random().toString(36).slice(2, 10)}`,
|
|
678
|
+
name: boundToolName,
|
|
679
|
+
args: buildFallbackEvidenceToolArgs(evidenceToolName, input),
|
|
680
|
+
type: "tool_call",
|
|
681
|
+
}],
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
function buildFallbackTodoCompletionToolCall(input, tools) {
|
|
685
|
+
const prompt = stringifyNodeLlamaCppInput(input);
|
|
686
|
+
if (/TODO completed:|\[x\]/i.test(prompt)) {
|
|
687
|
+
return null;
|
|
688
|
+
}
|
|
689
|
+
const planningToolName = tools.map((tool) => readBoundToolName(tool)).find((name) => name === "write_todos" || name === "tool_call_write_todos");
|
|
690
|
+
if (!planningToolName) {
|
|
691
|
+
return null;
|
|
692
|
+
}
|
|
693
|
+
const evidenceToolName = selectFallbackEvidenceToolName(input, tools);
|
|
694
|
+
if (!evidenceToolName) {
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
const hasEvidenceResult = hasPriorToolResultForToolName(input, evidenceToolName)
|
|
698
|
+
|| hasPriorToolResultForToolName(input, `tool_call_${evidenceToolName}`)
|
|
699
|
+
|| hasPriorNonPlanningToolResult(input, tools);
|
|
700
|
+
if (!hasEvidenceResult) {
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
const todos = buildToolAwareFallbackTodoContents(input, tools).map((content) => ({
|
|
704
|
+
content,
|
|
705
|
+
status: "completed",
|
|
706
|
+
}));
|
|
707
|
+
return new AIMessage({
|
|
708
|
+
content: "",
|
|
709
|
+
tool_calls: [{
|
|
710
|
+
id: `tool-${Math.random().toString(36).slice(2, 10)}`,
|
|
711
|
+
name: planningToolName,
|
|
712
|
+
args: { todos },
|
|
713
|
+
type: "tool_call",
|
|
714
|
+
}],
|
|
715
|
+
});
|
|
716
|
+
}
|
|
551
717
|
function formatBoundToolInstruction(tool) {
|
|
552
718
|
if (typeof tool !== "object" || tool === null) {
|
|
553
719
|
return null;
|
|
@@ -614,7 +780,17 @@ function createPromptedJsonToolBindableModel(model, boundTools = [], options = {
|
|
|
614
780
|
const parsedToolCall = normalizeParsedToolCall(extractToolCallPayload(text));
|
|
615
781
|
if (!parsedToolCall) {
|
|
616
782
|
if (forcePlanningToolCall) {
|
|
617
|
-
const fallbackToolCall = buildFallbackPlanningToolCall(input, effectiveBoundTools, text);
|
|
783
|
+
const fallbackToolCall = buildFallbackPlanningToolCall(input, effectiveBoundTools, boundTools, text);
|
|
784
|
+
if (fallbackToolCall) {
|
|
785
|
+
return fallbackToolCall;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
const fallbackCompletionToolCall = buildFallbackTodoCompletionToolCall(input, effectiveBoundTools);
|
|
790
|
+
if (fallbackCompletionToolCall) {
|
|
791
|
+
return fallbackCompletionToolCall;
|
|
792
|
+
}
|
|
793
|
+
const fallbackToolCall = buildFallbackEvidenceToolCall(input, effectiveBoundTools);
|
|
618
794
|
if (fallbackToolCall) {
|
|
619
795
|
return fallbackToolCall;
|
|
620
796
|
}
|
|
@@ -30,6 +30,30 @@ function extractTopLevelInitFields(value, reservedKeys) {
|
|
|
30
30
|
}
|
|
31
31
|
return Object.fromEntries(entries);
|
|
32
32
|
}
|
|
33
|
+
const NUMERIC_MODEL_INIT_FIELDS = new Set([
|
|
34
|
+
"maxRetries",
|
|
35
|
+
"maxTokens",
|
|
36
|
+
"numCtx",
|
|
37
|
+
"numGpu",
|
|
38
|
+
"numPredict",
|
|
39
|
+
"numThread",
|
|
40
|
+
"repeatPenalty",
|
|
41
|
+
"temperature",
|
|
42
|
+
"timeout",
|
|
43
|
+
"topK",
|
|
44
|
+
"topP",
|
|
45
|
+
]);
|
|
46
|
+
function normalizeModelInitFields(init) {
|
|
47
|
+
return Object.fromEntries(Object.entries(init).map(([key, value]) => {
|
|
48
|
+
if (NUMERIC_MODEL_INIT_FIELDS.has(key) && typeof value === "string") {
|
|
49
|
+
const trimmed = value.trim();
|
|
50
|
+
if (/^-?(?:\d+|\d*\.\d+)$/.test(trimmed)) {
|
|
51
|
+
return [key, Number(trimmed)];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return [key, value];
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
33
57
|
function assertNoInitBlock(value, kind, id) {
|
|
34
58
|
if (value.init !== undefined) {
|
|
35
59
|
throw new Error(`${kind} ${id} must define provider options at the top level; nested init is not supported`);
|
|
@@ -64,7 +88,7 @@ export function parseModelObject(object) {
|
|
|
64
88
|
assertNoInitBlock(value, "Model", object.id);
|
|
65
89
|
const provider = String(value.provider ?? "").trim();
|
|
66
90
|
const model = String((value.model ?? value.name) ?? "").trim();
|
|
67
|
-
const init = extractTopLevelInitFields(value, ["provider", "model", "name", "init", "clientRef", "fallbacks", "metadata"]) ?? {};
|
|
91
|
+
const init = normalizeModelInitFields(extractTopLevelInitFields(value, ["provider", "model", "name", "init", "clientRef", "fallbacks", "metadata"]) ?? {});
|
|
68
92
|
const fallbacks = Array.isArray(value.fallbacks)
|
|
69
93
|
? value.fallbacks
|
|
70
94
|
.map((item) => (typeof item === "object" && item && "ref" in item ? String(item.ref) : undefined))
|
|
@@ -86,7 +110,7 @@ export function parseEmbeddingModelObject(object) {
|
|
|
86
110
|
assertNoInitBlock(value, "EmbeddingModel", object.id);
|
|
87
111
|
const provider = String(value.provider ?? "").trim();
|
|
88
112
|
const model = String((value.model ?? value.name) ?? "").trim();
|
|
89
|
-
const init = extractTopLevelInitFields(value, ["provider", "model", "name", "init", "clientRef", "metadata"]) ?? {};
|
|
113
|
+
const init = normalizeModelInitFields(extractTopLevelInitFields(value, ["provider", "model", "name", "init", "clientRef", "metadata"]) ?? {});
|
|
90
114
|
return {
|
|
91
115
|
id: object.id,
|
|
92
116
|
provider,
|