@botbotgo/agent-harness 0.0.355 → 0.0.359
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.
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/runtime/adapter/compat/deepagent-compat.js +1 -14
- package/dist/runtime/adapter/flow/invocation-flow.js +16 -0
- package/dist/runtime/adapter/invocation-result.js +20 -2
- package/dist/runtime/agent-runtime-adapter.js +112 -8
- package/dist/runtime/parsing/output-content.js +11 -3
- package/dist/runtime/parsing/output-tool-args.d.ts +4 -0
- package/dist/runtime/parsing/output-tool-args.js +75 -8
- package/package.json +3 -3
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.359";
|
|
2
2
|
export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-25";
|
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.359";
|
|
2
2
|
export const AGENT_HARNESS_RELEASE_DATE = "2026-04-25";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
2
|
export function relativizeDeepAgentSkillSourcePaths(workspaceRoot, skillPaths) {
|
|
4
3
|
if (!workspaceRoot || !skillPaths) {
|
|
5
4
|
return skillPaths;
|
|
@@ -26,17 +25,5 @@ export function resolveDeepAgentSkillSourcePaths(options) {
|
|
|
26
25
|
return relativizeDeepAgentSkillSourcePaths(workspaceRoot, skillPaths) ?? skillPaths;
|
|
27
26
|
}
|
|
28
27
|
export function resolveDeepAgentSkillSourceRootPaths(options) {
|
|
29
|
-
|
|
30
|
-
if (!skillPaths) {
|
|
31
|
-
return skillPaths;
|
|
32
|
-
}
|
|
33
|
-
const sourceRoots = Array.from(new Set(skillPaths.map((skillPath) => {
|
|
34
|
-
const absolutePath = path.isAbsolute(skillPath) || !workspaceRoot
|
|
35
|
-
? skillPath
|
|
36
|
-
: path.resolve(workspaceRoot, skillPath);
|
|
37
|
-
return existsSync(path.join(absolutePath, "SKILL.md"))
|
|
38
|
-
? path.dirname(absolutePath)
|
|
39
|
-
: absolutePath;
|
|
40
|
-
})));
|
|
41
|
-
return relativizeDeepAgentSkillSourcePaths(workspaceRoot, sourceRoots) ?? sourceRoots;
|
|
28
|
+
return resolveDeepAgentSkillSourcePaths(options);
|
|
42
29
|
}
|
|
@@ -37,6 +37,9 @@ function isDelegationOnlyBinding(binding) {
|
|
|
37
37
|
function hasTaskDelegationEvidence(executedToolResults) {
|
|
38
38
|
return executedToolResults.some((item) => item.toolName === "task");
|
|
39
39
|
}
|
|
40
|
+
function hasPlanToolEvidence(executedToolResults) {
|
|
41
|
+
return executedToolResults.some((item) => item.toolName === "write_todos" || item.toolName === "read_todos");
|
|
42
|
+
}
|
|
40
43
|
function hasIncompleteTodos(value) {
|
|
41
44
|
if (!Array.isArray(value)) {
|
|
42
45
|
return false;
|
|
@@ -314,6 +317,19 @@ export async function executeRequestInvocation(options) {
|
|
|
314
317
|
if (!result) {
|
|
315
318
|
throw new Error("Agent invocation returned no result");
|
|
316
319
|
}
|
|
320
|
+
if (options.resumePayload === undefined
|
|
321
|
+
&& options.binding.harnessRuntime.executionContract?.requiresPlan === true
|
|
322
|
+
&& !hasPlanToolEvidence(executedToolResults)) {
|
|
323
|
+
const messages = Array.isArray(result.messages)
|
|
324
|
+
? result.messages
|
|
325
|
+
: undefined;
|
|
326
|
+
const recoveryBase = messages ? { messages } : request;
|
|
327
|
+
const recoveredRequest = appendToolRecoveryInstruction(recoveryBase, AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION);
|
|
328
|
+
const recoveredInvocation = await invokeOnce(recoveredRequest);
|
|
329
|
+
localOrUpstreamInvocation = recoveredInvocation;
|
|
330
|
+
result = recoveredInvocation.result;
|
|
331
|
+
executedToolResults.splice(0, executedToolResults.length, ...recoveredInvocation.executedToolResults);
|
|
332
|
+
}
|
|
317
333
|
if (options.resumePayload === undefined
|
|
318
334
|
&& options.binding.harnessRuntime.executionContract?.requiresPlan === true
|
|
319
335
|
&& hasIncompleteUpstreamPlan(result)
|
|
@@ -36,6 +36,17 @@ function hasIncompleteStateSnapshotPlan(stateSnapshot) {
|
|
|
36
36
|
return status === "pending" || status === "in_progress";
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
|
+
function hasStateSnapshotPlan(stateSnapshot) {
|
|
40
|
+
return typeof stateSnapshot === "object"
|
|
41
|
+
&& stateSnapshot !== null
|
|
42
|
+
&& Array.isArray(stateSnapshot.todos);
|
|
43
|
+
}
|
|
44
|
+
function hasPlanToolEvidence(executedToolResults) {
|
|
45
|
+
return executedToolResults.some((item) => item.toolName === "write_todos" || item.toolName === "read_todos");
|
|
46
|
+
}
|
|
47
|
+
function hasExecutionToolEvidence(executedToolResults) {
|
|
48
|
+
return executedToolResults.some((item) => item.isError !== true && item.toolName !== "write_todos" && item.toolName !== "read_todos");
|
|
49
|
+
}
|
|
39
50
|
function isPlaceholderTaskCompletion(value) {
|
|
40
51
|
const normalized = sanitizeVisibleText(value).trim();
|
|
41
52
|
return normalized === "Task completed";
|
|
@@ -357,6 +368,10 @@ export function finalizeRequestResult(params) {
|
|
|
357
368
|
const stateSnapshot = buildStateSnapshot(result);
|
|
358
369
|
const hasIncompleteRequiredPlan = binding?.harnessRuntime?.executionContract?.requiresPlan === true
|
|
359
370
|
&& hasIncompleteStateSnapshotPlan(stateSnapshot);
|
|
371
|
+
const hasMissingRequiredPlanEvidence = binding?.harnessRuntime?.executionContract?.requiresPlan === true
|
|
372
|
+
&& !hasStateSnapshotPlan(stateSnapshot)
|
|
373
|
+
&& !hasPlanToolEvidence(allExecutedToolResults)
|
|
374
|
+
&& !hasExecutionToolEvidence(allExecutedToolResults);
|
|
360
375
|
const serializedResult = JSON.stringify(result, null, 2);
|
|
361
376
|
let output = resolveDeterministicFinalOutput({
|
|
362
377
|
visibleOutput,
|
|
@@ -369,7 +384,10 @@ export function finalizeRequestResult(params) {
|
|
|
369
384
|
&& !visibleOutput
|
|
370
385
|
&& !preliminaryTerminalStatus
|
|
371
386
|
&& allExecutedToolResults.some((toolResult) => toolResult.isError !== true && toolResult.toolName !== "write_todos" && toolResult.toolName !== "read_todos");
|
|
372
|
-
if (
|
|
387
|
+
if (hasMissingRequiredPlanEvidence) {
|
|
388
|
+
output = "runtime_error=Agent ended before producing required plan evidence.";
|
|
389
|
+
}
|
|
390
|
+
else if (hasIncompleteRequiredPlan && !visibleOutput) {
|
|
373
391
|
output = "runtime_error=Agent ended while required plan still had unfinished work.";
|
|
374
392
|
}
|
|
375
393
|
else if (hasMissingRequiredFinalAnswer) {
|
|
@@ -386,7 +404,7 @@ export function finalizeRequestResult(params) {
|
|
|
386
404
|
agentId: bindingAgentId,
|
|
387
405
|
state: Array.isArray(result.__interrupt__) && result.__interrupt__.length > 0
|
|
388
406
|
? "waiting_for_approval"
|
|
389
|
-
: (hasIncompleteRequiredPlan && !hasSubstantiveFinalOutput) || hasMissingRequiredFinalAnswer
|
|
407
|
+
: hasMissingRequiredPlanEvidence || (hasIncompleteRequiredPlan && !hasSubstantiveFinalOutput) || hasMissingRequiredFinalAnswer
|
|
390
408
|
? "failed"
|
|
391
409
|
: hasTerminalToolBlocker
|
|
392
410
|
? "failed"
|
|
@@ -2,6 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { createAsyncSubAgentMiddleware, createFilesystemMiddleware, createMemoryMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSummarizationMiddleware, createSubAgentMiddleware, FilesystemBackend, StateBackend, } from "deepagents";
|
|
3
3
|
import { createAgent, humanInTheLoopMiddleware, todoListMiddleware } from "langchain";
|
|
4
4
|
import { sanitizeVisibleText, tryParseJson, wrapResolvedModel, } from "./parsing/output-parsing.js";
|
|
5
|
+
import { salvageJsonToolCalls } from "./parsing/output-tool-args.js";
|
|
5
6
|
import { extractMessageText } from "../utils/message-content.js";
|
|
6
7
|
import { AGENT_INTERRUPT_SENTINEL_PREFIX, buildDeepAgentCreateParams, buildDeepAgentSystemPromptWithCapabilityHierarchy, buildLangChainCreateParams, DEFAULT_DEEPAGENT_RECURSION_LIMIT, materializeModelExposedBuiltinMiddlewareTools, resolveLangChainInvocationConfig, resolveRunnableCheckpointer, resolveRunnableInterruptOn, shouldAttachDeepAgentBackend, shouldAttachDeepAgentCheckpointer, shouldAttachDeepAgentStore, } from "./agent-runtime-assembly.js";
|
|
7
8
|
import { resolveDeepAgentSkillSourcePaths, resolveDeepAgentSkillSourceRootPaths, } from "./adapter/compat/deepagent-compat.js";
|
|
@@ -105,12 +106,54 @@ function parseFirstJsonObject(value) {
|
|
|
105
106
|
}
|
|
106
107
|
return null;
|
|
107
108
|
}
|
|
109
|
+
function parseCompactRouterSelection(value, subagentNames) {
|
|
110
|
+
const trimmed = value.trim();
|
|
111
|
+
if (subagentNames.has(trimmed)) {
|
|
112
|
+
return { subagentType: trimmed };
|
|
113
|
+
}
|
|
114
|
+
const parsed = parseFirstJsonObject(trimmed);
|
|
115
|
+
const toolCall = salvageJsonToolCalls(trimmed).at(0);
|
|
116
|
+
const payload = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
|
|
117
|
+
? parsed
|
|
118
|
+
: toolCall
|
|
119
|
+
? { name: toolCall.name, arguments: toolCall.args }
|
|
120
|
+
: null;
|
|
121
|
+
if (!payload) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
const args = typeof payload.arguments === "object" && payload.arguments !== null && !Array.isArray(payload.arguments)
|
|
125
|
+
? payload.arguments
|
|
126
|
+
: typeof payload.args === "object" && payload.args !== null && !Array.isArray(payload.args)
|
|
127
|
+
? payload.args
|
|
128
|
+
: payload;
|
|
129
|
+
const subagentType = typeof payload.subagent_type === "string"
|
|
130
|
+
? payload.subagent_type.trim()
|
|
131
|
+
: typeof args.subagent_type === "string"
|
|
132
|
+
? args.subagent_type.trim()
|
|
133
|
+
: "";
|
|
134
|
+
if (subagentNames.has(subagentType)) {
|
|
135
|
+
return { subagentType };
|
|
136
|
+
}
|
|
137
|
+
const status = typeof payload.status === "string" ? payload.status.trim().toLowerCase() : "";
|
|
138
|
+
if (status === "refused") {
|
|
139
|
+
const reason = typeof payload.reason === "string" && payload.reason.trim()
|
|
140
|
+
? payload.reason.trim()
|
|
141
|
+
: "No configured subagent can handle the request.";
|
|
142
|
+
return { refusedReason: reason };
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
108
146
|
function isDelegationOnlyDeepAgentBinding(binding) {
|
|
109
147
|
return isDeepAgentBinding(binding)
|
|
110
148
|
&& getBindingSubagents(binding).length > 0
|
|
111
149
|
&& getBindingPrimaryTools(binding).length === 0
|
|
112
150
|
&& getBindingSkills(binding).length === 0;
|
|
113
151
|
}
|
|
152
|
+
function hasDelegatedPlanEvidence(result) {
|
|
153
|
+
const toolResults = result?.metadata?.executedToolResults;
|
|
154
|
+
return Array.isArray(toolResults)
|
|
155
|
+
&& toolResults.some((item) => item.toolName === "write_todos" || item.toolName === "read_todos");
|
|
156
|
+
}
|
|
114
157
|
export class AgentRuntimeAdapter {
|
|
115
158
|
options;
|
|
116
159
|
modelCache = new Map();
|
|
@@ -742,6 +785,7 @@ export class AgentRuntimeAdapter {
|
|
|
742
785
|
return null;
|
|
743
786
|
}
|
|
744
787
|
const subagents = getBindingSubagents(binding);
|
|
788
|
+
const subagentNames = new Set(subagents.map((subagent) => subagent.name));
|
|
745
789
|
const subagentCatalog = subagents
|
|
746
790
|
.map((subagent) => `- ${subagent.name}: ${subagent.description}`)
|
|
747
791
|
.join("\n");
|
|
@@ -772,26 +816,86 @@ export class AgentRuntimeAdapter {
|
|
|
772
816
|
requestId,
|
|
773
817
|
}),
|
|
774
818
|
})), resolveBindingTimeout(binding), "delegation router invoke", "invoke"));
|
|
775
|
-
const
|
|
776
|
-
|
|
777
|
-
|
|
819
|
+
const rawText = readModelText(raw);
|
|
820
|
+
let selection = parseCompactRouterSelection(rawText, subagentNames);
|
|
821
|
+
if (!selection) {
|
|
822
|
+
const retryPrompt = [
|
|
823
|
+
prompt,
|
|
824
|
+
"Your previous router output was invalid.",
|
|
825
|
+
"Previous output:",
|
|
826
|
+
rawText,
|
|
827
|
+
"Return only one JSON object now. Do not include prose, markdown, labels, or tool-call wrappers.",
|
|
828
|
+
].join("\n\n");
|
|
829
|
+
const retryRaw = await this.invokeWithProviderRetry(binding, () => this.withTimeout(() => model.invoke(retryPrompt, resolveLangChainInvocationConfig(binding, {
|
|
830
|
+
sessionId,
|
|
831
|
+
requestId,
|
|
832
|
+
context: options.context,
|
|
833
|
+
toolRuntimeContext: this.buildFunctionToolRuntimeContext(binding, {
|
|
834
|
+
...options,
|
|
835
|
+
sessionId,
|
|
836
|
+
requestId,
|
|
837
|
+
}),
|
|
838
|
+
})), resolveBindingTimeout(binding), "delegation router retry invoke", "invoke"));
|
|
839
|
+
selection = parseCompactRouterSelection(readModelText(retryRaw), subagentNames);
|
|
778
840
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
841
|
+
if (selection?.refusedReason) {
|
|
842
|
+
return {
|
|
843
|
+
toolOutput: selection.refusedReason,
|
|
844
|
+
delegatedResult: {
|
|
845
|
+
sessionId,
|
|
846
|
+
requestId,
|
|
847
|
+
agentId: binding.agent.id,
|
|
848
|
+
state: "failed",
|
|
849
|
+
output: selection.refusedReason,
|
|
850
|
+
finalMessageText: selection.refusedReason,
|
|
851
|
+
},
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
const subagentType = selection?.subagentType ?? "";
|
|
855
|
+
if (!subagentNames.has(subagentType)) {
|
|
783
856
|
return null;
|
|
784
857
|
}
|
|
785
858
|
const selectedBinding = this.options.bindingResolver(subagentType);
|
|
786
859
|
if (!selectedBinding) {
|
|
787
860
|
return null;
|
|
788
861
|
}
|
|
789
|
-
const
|
|
862
|
+
const runDelegatedRequest = (text, requestSuffix = "") => this.invoke(selectedBinding, text, sessionId, `${requestId}:${subagentType}${requestSuffix}`, undefined, [], {
|
|
790
863
|
context: options.context,
|
|
791
864
|
state: options.state,
|
|
792
865
|
files: options.files,
|
|
793
866
|
memoryContext: options.memoryContext,
|
|
794
867
|
});
|
|
868
|
+
let delegatedResult = await runDelegatedRequest(requestText);
|
|
869
|
+
const targetRequiresExecutionToolEvidence = getBindingPrimaryTools(selectedBinding).length > 0;
|
|
870
|
+
if (targetRequiresExecutionToolEvidence && !hasDelegatedExecutionToolEvidence(delegatedResult)) {
|
|
871
|
+
delegatedResult = await runDelegatedRequest([requestText, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":tool-evidence-retry");
|
|
872
|
+
}
|
|
873
|
+
if (targetRequiresExecutionToolEvidence && !hasDelegatedExecutionToolEvidence(delegatedResult)) {
|
|
874
|
+
const output = `runtime_error=Delegated agent ${selectedBinding.agent.id} completed without tool execution evidence.`;
|
|
875
|
+
return {
|
|
876
|
+
toolOutput: output,
|
|
877
|
+
delegatedResult: {
|
|
878
|
+
...delegatedResult,
|
|
879
|
+
state: "failed",
|
|
880
|
+
output,
|
|
881
|
+
finalMessageText: output,
|
|
882
|
+
},
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
if (selectedBinding.harnessRuntime.executionContract?.requiresPlan === true
|
|
886
|
+
&& !hasDelegatedPlanEvidence(delegatedResult)
|
|
887
|
+
&& !hasDelegatedExecutionToolEvidence(delegatedResult)) {
|
|
888
|
+
const output = "runtime_error=Delegated agent ended before producing required plan evidence.";
|
|
889
|
+
return {
|
|
890
|
+
toolOutput: output,
|
|
891
|
+
delegatedResult: {
|
|
892
|
+
...delegatedResult,
|
|
893
|
+
state: "failed",
|
|
894
|
+
output,
|
|
895
|
+
finalMessageText: output,
|
|
896
|
+
},
|
|
897
|
+
};
|
|
898
|
+
}
|
|
795
899
|
return { toolOutput: delegatedResult.output, delegatedResult };
|
|
796
900
|
}
|
|
797
901
|
async *stream(binding, input, sessionId, history = [], options = {}) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AIMessage } from "langchain";
|
|
2
|
-
import { salvageFunctionLikeToolCall, salvageJsonToolCalls, salvageToolArgs, isLikelyToolArgsObject, normalizeKnownToolArgs, tryParseJson } from "./output-tool-args.js";
|
|
2
|
+
import { salvageFunctionLikeToolCall, salvageJsonToolCalls, salvageLabeledToolCall, salvageToolArgs, isLikelyToolArgsObject, normalizeKnownToolArgs, tryParseJson } from "./output-tool-args.js";
|
|
3
3
|
function consumeLeadingFunctionLikeToolCall(value) {
|
|
4
4
|
const match = /^([A-Za-z_][A-Za-z0-9_]*)\(/.exec(value);
|
|
5
5
|
if (!match) {
|
|
@@ -59,6 +59,9 @@ function consumeLeadingFunctionLikeToolCall(value) {
|
|
|
59
59
|
function stripVisibleFunctionLikeToolCallText(value) {
|
|
60
60
|
let remaining = value.trim();
|
|
61
61
|
let removedLeadingCall = false;
|
|
62
|
+
if (salvageLabeledToolCall(remaining)) {
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
62
65
|
if (salvageJsonToolCalls(remaining).length > 0) {
|
|
63
66
|
return "";
|
|
64
67
|
}
|
|
@@ -88,6 +91,7 @@ function stripVisibleFunctionLikeToolCallText(value) {
|
|
|
88
91
|
export function sanitizeVisibleText(value) {
|
|
89
92
|
return stripVisibleFunctionLikeToolCallText(value
|
|
90
93
|
.replace(/<agent_memory>[\s\S]*?<\/agent_memory>/giu, "")
|
|
94
|
+
.replace(/<agent_memory>[\s\S]*$/giu, "")
|
|
91
95
|
.replace(/[A-Za-z0-9_]*Middleware\.after_model/g, "")
|
|
92
96
|
.replace(/todoListMiddleware\.after_model/g, "")
|
|
93
97
|
.replace(/__end__+/g, "")
|
|
@@ -499,10 +503,13 @@ function normalizeAgentMessage(value) {
|
|
|
499
503
|
const functionLikeToolCall = normalizedToolCalls.length === 0 && recoveredToolCalls.length === 0 && typeof normalizedContent === "string"
|
|
500
504
|
? salvageFunctionLikeToolCall(normalizedContent)
|
|
501
505
|
: null;
|
|
502
|
-
const
|
|
506
|
+
const labeledToolCall = normalizedToolCalls.length === 0 && recoveredToolCalls.length === 0 && !functionLikeToolCall && typeof normalizedContent === "string"
|
|
507
|
+
? salvageLabeledToolCall(normalizedContent)
|
|
508
|
+
: null;
|
|
509
|
+
const jsonToolCalls = normalizedToolCalls.length === 0 && recoveredToolCalls.length === 0 && !functionLikeToolCall && !labeledToolCall && typeof normalizedContent === "string"
|
|
503
510
|
? salvageJsonToolCalls(normalizedContent)
|
|
504
511
|
: [];
|
|
505
|
-
const hasRecoveredContentToolCalls = Boolean(functionLikeToolCall) || jsonToolCalls.length > 0;
|
|
512
|
+
const hasRecoveredContentToolCalls = Boolean(functionLikeToolCall) || Boolean(labeledToolCall) || jsonToolCalls.length > 0;
|
|
506
513
|
return new AIMessage({
|
|
507
514
|
content: hasRecoveredContentToolCalls ? "" : normalizedContent,
|
|
508
515
|
name: typeof typed.name === "string" ? typed.name : undefined,
|
|
@@ -513,6 +520,7 @@ function normalizeAgentMessage(value) {
|
|
|
513
520
|
...normalizedToolCalls,
|
|
514
521
|
...recoveredToolCalls,
|
|
515
522
|
...(functionLikeToolCall ? [{ name: functionLikeToolCall.name, args: functionLikeToolCall.args }] : []),
|
|
523
|
+
...(labeledToolCall ? [{ name: labeledToolCall.name, args: labeledToolCall.args }] : []),
|
|
516
524
|
...jsonToolCalls.map((toolCall) => ({ name: toolCall.name, args: toolCall.args })),
|
|
517
525
|
],
|
|
518
526
|
invalid_tool_calls: normalizedInvalidToolCalls.filter((toolCall) => toolCall.type !== "tool_call"),
|
|
@@ -3,6 +3,10 @@ export declare function salvageFunctionLikeToolCall(value: unknown): {
|
|
|
3
3
|
name: string;
|
|
4
4
|
args: Record<string, unknown>;
|
|
5
5
|
} | null;
|
|
6
|
+
export declare function salvageLabeledToolCall(value: unknown): {
|
|
7
|
+
name: string;
|
|
8
|
+
args: Record<string, unknown>;
|
|
9
|
+
} | null;
|
|
6
10
|
export declare function salvageToolArgs(value: unknown): Record<string, unknown> | null;
|
|
7
11
|
export declare function salvageJsonToolCalls(value: unknown): Array<{
|
|
8
12
|
name: string;
|
|
@@ -112,6 +112,73 @@ export function salvageFunctionLikeToolCall(value) {
|
|
|
112
112
|
}
|
|
113
113
|
return { name, args: normalizeKnownToolArgs(name, args) };
|
|
114
114
|
}
|
|
115
|
+
function normalizeFieldLabel(value) {
|
|
116
|
+
return value
|
|
117
|
+
.trim()
|
|
118
|
+
.toLowerCase()
|
|
119
|
+
.split("")
|
|
120
|
+
.filter((char) => {
|
|
121
|
+
const code = char.charCodeAt(0);
|
|
122
|
+
return (code >= 97 && code <= 122) || (code >= 48 && code <= 57);
|
|
123
|
+
})
|
|
124
|
+
.join("");
|
|
125
|
+
}
|
|
126
|
+
function splitLabeledLine(line) {
|
|
127
|
+
const separatorIndex = line.indexOf(":");
|
|
128
|
+
if (separatorIndex <= 0) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const label = normalizeFieldLabel(line.slice(0, separatorIndex));
|
|
132
|
+
const value = line.slice(separatorIndex + 1).trim();
|
|
133
|
+
return label ? { label, value } : null;
|
|
134
|
+
}
|
|
135
|
+
function isToolName(value) {
|
|
136
|
+
if (!value) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
const first = value.charCodeAt(0);
|
|
140
|
+
if (!((first >= 65 && first <= 90) || (first >= 97 && first <= 122) || first === 95)) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
for (let index = 1; index < value.length; index += 1) {
|
|
144
|
+
const code = value.charCodeAt(index);
|
|
145
|
+
if (!((code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code >= 48 && code <= 57) || code === 95)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
function isToolNameLabel(label) {
|
|
152
|
+
return label === "toolcall" || label === "tool" || label === "functioncall" || label === "function";
|
|
153
|
+
}
|
|
154
|
+
function isToolArgsLabel(label) {
|
|
155
|
+
return label === "arguments" || label === "args" || label === "parameters" || label === "input";
|
|
156
|
+
}
|
|
157
|
+
export function salvageLabeledToolCall(value) {
|
|
158
|
+
if (typeof value !== "string") {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const lines = value
|
|
162
|
+
.split("\n")
|
|
163
|
+
.map((line) => line.trim())
|
|
164
|
+
.filter(Boolean);
|
|
165
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
166
|
+
const toolLine = splitLabeledLine(lines[index]);
|
|
167
|
+
if (!toolLine || !isToolNameLabel(toolLine.label) || !isToolName(toolLine.value)) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
for (let argsIndex = index + 1; argsIndex < lines.length; argsIndex += 1) {
|
|
171
|
+
const argsLine = splitLabeledLine(lines[argsIndex]);
|
|
172
|
+
if (!argsLine || !isToolArgsLabel(argsLine.label)) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const argsText = [argsLine.value, ...lines.slice(argsIndex + 1)].filter(Boolean).join("\n").trim();
|
|
176
|
+
const args = salvageToolArgs(argsText) ?? {};
|
|
177
|
+
return { name: toolLine.value, args: normalizeKnownToolArgs(toolLine.value, args) };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
115
182
|
function extractBalancedJsonValue(value, openChar, closeChar) {
|
|
116
183
|
const start = value.indexOf(openChar);
|
|
117
184
|
if (start < 0)
|
|
@@ -306,16 +373,16 @@ export function salvageJsonToolCalls(value) {
|
|
|
306
373
|
return parsed;
|
|
307
374
|
}
|
|
308
375
|
}
|
|
309
|
-
const
|
|
310
|
-
if (
|
|
311
|
-
const parsed = tryParseJson(
|
|
376
|
+
const embeddedObject = extractBalancedJsonObject(trimmed);
|
|
377
|
+
if (embeddedObject) {
|
|
378
|
+
const parsed = tryParseJson(embeddedObject);
|
|
312
379
|
if (parsed) {
|
|
313
380
|
return parsed;
|
|
314
381
|
}
|
|
315
382
|
}
|
|
316
|
-
const
|
|
317
|
-
if (
|
|
318
|
-
const parsed = tryParseJson(
|
|
383
|
+
const embeddedArray = extractBalancedJsonArray(trimmed);
|
|
384
|
+
if (embeddedArray) {
|
|
385
|
+
const parsed = tryParseJson(embeddedArray);
|
|
319
386
|
if (parsed) {
|
|
320
387
|
return parsed;
|
|
321
388
|
}
|
|
@@ -334,7 +401,7 @@ function normalizeWriteTodosArgs(args) {
|
|
|
334
401
|
}
|
|
335
402
|
return {
|
|
336
403
|
...args,
|
|
337
|
-
todos: args.todos.map((todo) => {
|
|
404
|
+
todos: args.todos.map((todo, index) => {
|
|
338
405
|
if (typeof todo !== "object" || !todo || Array.isArray(todo)) {
|
|
339
406
|
return todo;
|
|
340
407
|
}
|
|
@@ -343,7 +410,7 @@ function normalizeWriteTodosArgs(args) {
|
|
|
343
410
|
? record.content
|
|
344
411
|
: typeof record.description === "string" && record.description.trim().length > 0
|
|
345
412
|
? record.description
|
|
346
|
-
:
|
|
413
|
+
: `Step ${index + 1}`;
|
|
347
414
|
const normalized = {};
|
|
348
415
|
if (content !== undefined)
|
|
349
416
|
normalized.content = content;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@botbotgo/agent-harness",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.359",
|
|
4
4
|
"description": "Workspace runtime for multi-agent applications",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"main": "./dist/index.js",
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"bin": {
|
|
11
|
-
"agent-harness": "
|
|
12
|
-
"botbotgo": "
|
|
11
|
+
"agent-harness": "dist/cli.js",
|
|
12
|
+
"botbotgo": "dist/cli.js"
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"dist"
|