@botbotgo/agent-harness 0.0.353 → 0.0.354
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/model/model-providers.js +13 -7
- package/dist/runtime/adapter/resilience.js +6 -0
- package/dist/runtime/agent-runtime-adapter.d.ts +1 -0
- package/dist/runtime/agent-runtime-adapter.js +191 -2
- package/dist/runtime/harness/run/stream-run.js +9 -0
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.354";
|
|
2
2
|
export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-24";
|
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.354";
|
|
2
2
|
export const AGENT_HARNESS_RELEASE_DATE = "2026-04-24";
|
|
@@ -23,6 +23,7 @@ const PROMPTED_JSON_FINAL_TOOL_CALL_REMINDER = [
|
|
|
23
23
|
"If a TOOL_RESULT is already present for the requested work, do not repeat that tool call; answer normally.",
|
|
24
24
|
'Shape: {"name":"tool_name","arguments":{}}',
|
|
25
25
|
].join("\n");
|
|
26
|
+
const NO_THINK_CONTROL_TOKEN = "/no_think";
|
|
26
27
|
function readModelText(value) {
|
|
27
28
|
if (typeof value === "string") {
|
|
28
29
|
return value.trim();
|
|
@@ -455,16 +456,21 @@ function formatBoundToolInstruction(tool) {
|
|
|
455
456
|
`Arguments JSON schema: ${JSON.stringify(schema)}`,
|
|
456
457
|
].filter(Boolean).join("\n");
|
|
457
458
|
}
|
|
458
|
-
function withPromptedJsonToolPrompt(input, tools) {
|
|
459
|
+
function withPromptedJsonToolPrompt(input, tools, options = {}) {
|
|
459
460
|
const toolInstructions = tools.map((tool) => formatBoundToolInstruction(tool)).filter((value) => Boolean(value));
|
|
460
461
|
if (toolInstructions.length === 0) {
|
|
461
462
|
return stringifyNodeLlamaCppInput(input);
|
|
462
463
|
}
|
|
463
464
|
const systemContent = `${NODE_LLAMA_CPP_TOOL_CALL_INSTRUCTION}\n\n${toolInstructions.join("\n\n")}`;
|
|
464
465
|
const prompt = stringifyNodeLlamaCppInput(input);
|
|
465
|
-
return [
|
|
466
|
+
return [
|
|
467
|
+
options.suppressThinking ? NO_THINK_CONTROL_TOKEN : "",
|
|
468
|
+
systemContent,
|
|
469
|
+
prompt,
|
|
470
|
+
PROMPTED_JSON_FINAL_TOOL_CALL_REMINDER,
|
|
471
|
+
].filter(Boolean).join("\n\n");
|
|
466
472
|
}
|
|
467
|
-
function createPromptedJsonToolBindableModel(model, boundTools = []) {
|
|
473
|
+
function createPromptedJsonToolBindableModel(model, boundTools = [], options = {}) {
|
|
468
474
|
return new Proxy(model, {
|
|
469
475
|
has(target, prop) {
|
|
470
476
|
if (prop === "bindTools" || prop === "invoke" || prop === "stream" || prop === "withConfig") {
|
|
@@ -474,11 +480,11 @@ function createPromptedJsonToolBindableModel(model, boundTools = []) {
|
|
|
474
480
|
},
|
|
475
481
|
get(target, prop, receiver) {
|
|
476
482
|
if (prop === "bindTools") {
|
|
477
|
-
return (tools) => createPromptedJsonToolBindableModel(target, tools);
|
|
483
|
+
return (tools) => createPromptedJsonToolBindableModel(target, tools, options);
|
|
478
484
|
}
|
|
479
485
|
if (prop === "invoke") {
|
|
480
486
|
return async (input, config) => {
|
|
481
|
-
const rawResult = await target.invoke(boundTools.length > 0 ? withPromptedJsonToolPrompt(input, boundTools) : input, config);
|
|
487
|
+
const rawResult = await target.invoke(boundTools.length > 0 ? withPromptedJsonToolPrompt(input, boundTools, options) : input, config);
|
|
482
488
|
if (boundTools.length === 0) {
|
|
483
489
|
return rawResult;
|
|
484
490
|
}
|
|
@@ -510,7 +516,7 @@ function createPromptedJsonToolBindableModel(model, boundTools = []) {
|
|
|
510
516
|
};
|
|
511
517
|
}
|
|
512
518
|
if (prop === "withConfig" && typeof target.withConfig === "function") {
|
|
513
|
-
return (config) => createPromptedJsonToolBindableModel(target.withConfig(config), boundTools);
|
|
519
|
+
return (config) => createPromptedJsonToolBindableModel(target.withConfig(config), boundTools, options);
|
|
514
520
|
}
|
|
515
521
|
const member = Reflect.get(target, prop, receiver);
|
|
516
522
|
return typeof member === "function" ? member.bind(target) : member;
|
|
@@ -559,7 +565,7 @@ export async function createResolvedModel(model, modelResolver) {
|
|
|
559
565
|
const { toolCallingMode, ...init } = model.init ?? {};
|
|
560
566
|
const resolved = new ChatOllama({ model: model.model, ...init });
|
|
561
567
|
if (toolCallingMode === "prompted-json") {
|
|
562
|
-
return createPromptedJsonToolBindableModel(resolved);
|
|
568
|
+
return createPromptedJsonToolBindableModel(resolved, [], { suppressThinking: init.think === false });
|
|
563
569
|
}
|
|
564
570
|
return createProviderToolMessageCompatModel(resolved);
|
|
565
571
|
}
|
|
@@ -42,6 +42,9 @@ export function isEmptyFinalAiMessageError(error) {
|
|
|
42
42
|
const message = error instanceof Error ? error.message : String(error);
|
|
43
43
|
return message.toLowerCase().startsWith("empty_final_ai_message:");
|
|
44
44
|
}
|
|
45
|
+
function isRuntimeOperationTimeoutError(error) {
|
|
46
|
+
return typeof error === "object" && error !== null && error.name === "RuntimeOperationTimeoutError";
|
|
47
|
+
}
|
|
45
48
|
function isRetryableHttpStatus(status) {
|
|
46
49
|
return typeof status === "number" && Number.isInteger(status) && status >= 500 && status <= 599;
|
|
47
50
|
}
|
|
@@ -84,6 +87,9 @@ export function resolveProviderRetryPolicy(binding) {
|
|
|
84
87
|
};
|
|
85
88
|
}
|
|
86
89
|
export function isRetryableProviderError(binding, error) {
|
|
90
|
+
if (isRuntimeOperationTimeoutError(error)) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
87
93
|
if (isEmptyFinalAiMessageError(error)) {
|
|
88
94
|
return true;
|
|
89
95
|
}
|
|
@@ -57,6 +57,7 @@ export declare class AgentRuntimeAdapter {
|
|
|
57
57
|
files?: Record<string, unknown>;
|
|
58
58
|
memoryContext?: string;
|
|
59
59
|
}): Promise<RequestResult>;
|
|
60
|
+
private tryDelegateWithCompactRouter;
|
|
60
61
|
stream(binding: CompiledAgentBinding, input: MessageContent, sessionId: string, history?: TranscriptMessage[], options?: {
|
|
61
62
|
context?: Record<string, unknown>;
|
|
62
63
|
state?: Record<string, unknown>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
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
|
-
import { wrapResolvedModel, } from "./parsing/output-parsing.js";
|
|
4
|
+
import { tryParseJson, wrapResolvedModel, } from "./parsing/output-parsing.js";
|
|
5
|
+
import { extractMessageText } from "../utils/message-content.js";
|
|
5
6
|
import { AGENT_INTERRUPT_SENTINEL_PREFIX, buildDeepAgentCreateParams, buildDeepAgentSystemPromptWithCapabilityHierarchy, buildLangChainCreateParams, DEFAULT_DEEPAGENT_RECURSION_LIMIT, materializeModelExposedBuiltinMiddlewareTools, resolveLangChainInvocationConfig, resolveRunnableCheckpointer, resolveRunnableInterruptOn, shouldAttachDeepAgentBackend, shouldAttachDeepAgentCheckpointer, shouldAttachDeepAgentStore, } from "./agent-runtime-assembly.js";
|
|
6
7
|
import { resolveDeepAgentSkillSourcePaths, } from "./adapter/compat/deepagent-compat.js";
|
|
7
8
|
import { EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION } from "./prompts/runtime-prompts.js";
|
|
@@ -21,7 +22,7 @@ export { materializeDeepAgentSkillSourcePaths, resolveDeepAgentSkillSourcePaths,
|
|
|
21
22
|
export { buildAuthOmittingFetch, normalizeOpenAICompatibleInit } from "./adapter/compat/openai-compatible.js";
|
|
22
23
|
export { buildToolNameMapping, createModelFacingToolNameCandidates, createModelFacingToolNameLookupCandidates, resolveModelFacingToolName, sanitizeToolNameForModel, } from "./adapter/tool/tool-name-mapping.js";
|
|
23
24
|
export { computeRemainingTimeoutMs, isRetryableProviderError, resolveBindingTimeout, resolveProviderRetryPolicy, resolveStreamIdleTimeout, resolveTimeoutMs, } from "./adapter/resilience.js";
|
|
24
|
-
import { getBindingAdapterKind, getBindingBuiltinToolsConfig, getBindingDeepAgentSubagents, getBindingExecutionParams, getBindingExecutionKind, getBindingFilesystemConfig, getBindingMemorySources, getBindingPrimaryModel, getBindingSkills, getBindingToolCount, getBindingPrimaryTools, getBindingSystemPrompt, isDeepAgentBinding, isLangChainBinding, } from "./support/compiled-binding.js";
|
|
25
|
+
import { getBindingAdapterKind, getBindingBuiltinToolsConfig, getBindingDeepAgentSubagents, getBindingExecutionParams, getBindingExecutionKind, getBindingFilesystemConfig, getBindingMemorySources, getBindingPrimaryModel, getBindingSkills, getBindingSubagents, getBindingToolCount, getBindingPrimaryTools, getBindingSystemPrompt, isDeepAgentBinding, isLangChainBinding, } from "./support/compiled-binding.js";
|
|
25
26
|
class DelegatedExecutionNoToolEvidenceError extends Error {
|
|
26
27
|
constructor(agentId) {
|
|
27
28
|
super(`Delegated agent ${agentId} completed without tool execution evidence.`);
|
|
@@ -39,6 +40,77 @@ function hasDelegatedExecutionToolEvidence(result) {
|
|
|
39
40
|
function shouldUseConfigurableDeepAgentAssembly(binding) {
|
|
40
41
|
return getBindingExecutionKind(binding) === "deepagent";
|
|
41
42
|
}
|
|
43
|
+
function readModelText(value) {
|
|
44
|
+
if (typeof value === "string") {
|
|
45
|
+
return value.trim();
|
|
46
|
+
}
|
|
47
|
+
if (typeof value !== "object" || value === null) {
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
const content = value.content;
|
|
51
|
+
if (typeof content === "string") {
|
|
52
|
+
return content.trim();
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(content)) {
|
|
55
|
+
return content
|
|
56
|
+
.map((part) => {
|
|
57
|
+
if (typeof part === "string")
|
|
58
|
+
return part;
|
|
59
|
+
if (typeof part === "object" && part !== null && typeof part.text === "string") {
|
|
60
|
+
return part.text;
|
|
61
|
+
}
|
|
62
|
+
return "";
|
|
63
|
+
})
|
|
64
|
+
.join("")
|
|
65
|
+
.trim();
|
|
66
|
+
}
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
function parseFirstJsonObject(value) {
|
|
70
|
+
let depth = 0;
|
|
71
|
+
let start = -1;
|
|
72
|
+
let inString = false;
|
|
73
|
+
let escaping = false;
|
|
74
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
75
|
+
const char = value[index];
|
|
76
|
+
if (inString) {
|
|
77
|
+
if (escaping) {
|
|
78
|
+
escaping = false;
|
|
79
|
+
}
|
|
80
|
+
else if (char === "\\") {
|
|
81
|
+
escaping = true;
|
|
82
|
+
}
|
|
83
|
+
else if (char === "\"") {
|
|
84
|
+
inString = false;
|
|
85
|
+
}
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (char === "\"") {
|
|
89
|
+
inString = true;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (char === "{") {
|
|
93
|
+
if (depth === 0) {
|
|
94
|
+
start = index;
|
|
95
|
+
}
|
|
96
|
+
depth += 1;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (char === "}" && depth > 0) {
|
|
100
|
+
depth -= 1;
|
|
101
|
+
if (depth === 0 && start >= 0) {
|
|
102
|
+
return tryParseJson(value.slice(start, index + 1));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
function isDelegationOnlyDeepAgentBinding(binding) {
|
|
109
|
+
return isDeepAgentBinding(binding)
|
|
110
|
+
&& getBindingSubagents(binding).length > 0
|
|
111
|
+
&& getBindingPrimaryTools(binding).length === 0
|
|
112
|
+
&& getBindingSkills(binding).length === 0;
|
|
113
|
+
}
|
|
42
114
|
export class AgentRuntimeAdapter {
|
|
43
115
|
options;
|
|
44
116
|
modelCache = new Map();
|
|
@@ -557,6 +629,36 @@ export class AgentRuntimeAdapter {
|
|
|
557
629
|
},
|
|
558
630
|
};
|
|
559
631
|
}
|
|
632
|
+
const compactDelegation = await this.tryDelegateWithCompactRouter(binding, input, sessionId, requestId, {
|
|
633
|
+
...options,
|
|
634
|
+
sessionId,
|
|
635
|
+
requestId,
|
|
636
|
+
});
|
|
637
|
+
if (compactDelegation) {
|
|
638
|
+
const output = typeof compactDelegation.toolOutput === "string"
|
|
639
|
+
? compactDelegation.toolOutput
|
|
640
|
+
: JSON.stringify(compactDelegation.toolOutput);
|
|
641
|
+
const delegatedToolResults = Array.isArray(compactDelegation.delegatedResult?.metadata?.executedToolResults)
|
|
642
|
+
? compactDelegation.delegatedResult.metadata.executedToolResults
|
|
643
|
+
: [];
|
|
644
|
+
return {
|
|
645
|
+
sessionId,
|
|
646
|
+
requestId,
|
|
647
|
+
agentId: binding.agent.id,
|
|
648
|
+
state: "completed",
|
|
649
|
+
output,
|
|
650
|
+
finalMessageText: output,
|
|
651
|
+
metadata: {
|
|
652
|
+
executedToolResults: [
|
|
653
|
+
{
|
|
654
|
+
toolName: "task",
|
|
655
|
+
output: compactDelegation.toolOutput,
|
|
656
|
+
},
|
|
657
|
+
...delegatedToolResults,
|
|
658
|
+
],
|
|
659
|
+
},
|
|
660
|
+
};
|
|
661
|
+
}
|
|
560
662
|
const callRuntime = async (activeBinding, activeRequest) => {
|
|
561
663
|
return this.invokeWithProviderRetry(activeBinding, async () => {
|
|
562
664
|
const runnable = await this.create(activeBinding, { sessionId });
|
|
@@ -612,6 +714,74 @@ export class AgentRuntimeAdapter {
|
|
|
612
714
|
return invokeRequest();
|
|
613
715
|
}
|
|
614
716
|
}
|
|
717
|
+
async tryDelegateWithCompactRouter(binding, input, sessionId, requestId, options = {}) {
|
|
718
|
+
if (!isDelegationOnlyDeepAgentBinding(binding)) {
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
if (!this.options.bindingResolver) {
|
|
722
|
+
return null;
|
|
723
|
+
}
|
|
724
|
+
const primaryModel = getBindingPrimaryModel(binding);
|
|
725
|
+
if (!primaryModel) {
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
const requestText = extractMessageText(input).trim();
|
|
729
|
+
if (!requestText) {
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
732
|
+
const subagents = getBindingSubagents(binding);
|
|
733
|
+
const subagentCatalog = subagents
|
|
734
|
+
.map((subagent) => `- ${subagent.name}: ${subagent.description}`)
|
|
735
|
+
.join("\n");
|
|
736
|
+
const prompt = [
|
|
737
|
+
primaryModel.init?.think === false ? "/no_think" : "",
|
|
738
|
+
"You are selecting a subagent for a delegation-only agent.",
|
|
739
|
+
"Choose exactly one listed subagent when it can responsibly handle the request.",
|
|
740
|
+
"Return only JSON with this shape:",
|
|
741
|
+
"{\"subagent_type\":\"<listed subagent name>\"}",
|
|
742
|
+
"If no listed subagent can handle the request, return only:",
|
|
743
|
+
"{\"status\":\"refused\",\"reason\":\"No configured subagent can handle the request.\"}",
|
|
744
|
+
"Available subagents:",
|
|
745
|
+
subagentCatalog,
|
|
746
|
+
"User request:",
|
|
747
|
+
requestText,
|
|
748
|
+
].filter(Boolean).join("\n\n");
|
|
749
|
+
const model = await this.resolveModel(primaryModel);
|
|
750
|
+
if (typeof model.invoke !== "function") {
|
|
751
|
+
return null;
|
|
752
|
+
}
|
|
753
|
+
const raw = await this.withTimeout(() => model.invoke(prompt, resolveLangChainInvocationConfig(binding, {
|
|
754
|
+
sessionId,
|
|
755
|
+
requestId,
|
|
756
|
+
context: options.context,
|
|
757
|
+
toolRuntimeContext: this.buildFunctionToolRuntimeContext(binding, {
|
|
758
|
+
...options,
|
|
759
|
+
sessionId,
|
|
760
|
+
requestId,
|
|
761
|
+
}),
|
|
762
|
+
})), resolveBindingTimeout(binding), "delegation router invoke", "invoke");
|
|
763
|
+
const parsed = parseFirstJsonObject(readModelText(raw));
|
|
764
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
765
|
+
return null;
|
|
766
|
+
}
|
|
767
|
+
const subagentType = typeof parsed.subagent_type === "string"
|
|
768
|
+
? parsed.subagent_type
|
|
769
|
+
: "";
|
|
770
|
+
if (!subagents.some((subagent) => subagent.name === subagentType)) {
|
|
771
|
+
return null;
|
|
772
|
+
}
|
|
773
|
+
const selectedBinding = this.options.bindingResolver(subagentType);
|
|
774
|
+
if (!selectedBinding) {
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
const delegatedResult = await this.invoke(selectedBinding, requestText, sessionId, `${requestId}:${subagentType}`, undefined, [], {
|
|
778
|
+
context: options.context,
|
|
779
|
+
state: options.state,
|
|
780
|
+
files: options.files,
|
|
781
|
+
memoryContext: options.memoryContext,
|
|
782
|
+
});
|
|
783
|
+
return { toolOutput: delegatedResult.output, delegatedResult };
|
|
784
|
+
}
|
|
615
785
|
async *stream(binding, input, sessionId, history = [], options = {}) {
|
|
616
786
|
const directListing = await this.tryHandleDirectWorkspaceListing(binding, input, {
|
|
617
787
|
...options,
|
|
@@ -630,6 +800,25 @@ export class AgentRuntimeAdapter {
|
|
|
630
800
|
};
|
|
631
801
|
return;
|
|
632
802
|
}
|
|
803
|
+
const compactDelegation = await this.tryDelegateWithCompactRouter(binding, input, sessionId, options.requestId ?? sessionId, {
|
|
804
|
+
...options,
|
|
805
|
+
sessionId,
|
|
806
|
+
requestId: options.requestId,
|
|
807
|
+
});
|
|
808
|
+
if (compactDelegation) {
|
|
809
|
+
yield {
|
|
810
|
+
kind: "tool-result",
|
|
811
|
+
toolName: "task",
|
|
812
|
+
output: compactDelegation.toolOutput,
|
|
813
|
+
};
|
|
814
|
+
yield {
|
|
815
|
+
kind: "content",
|
|
816
|
+
content: typeof compactDelegation.toolOutput === "string"
|
|
817
|
+
? compactDelegation.toolOutput
|
|
818
|
+
: JSON.stringify(compactDelegation.toolOutput),
|
|
819
|
+
};
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
633
822
|
const invokeTimeoutMs = resolveBindingTimeout(binding);
|
|
634
823
|
const streamIdleTimeoutMs = resolveStreamIdleTimeout(binding);
|
|
635
824
|
const streamDeadlineAt = invokeTimeoutMs ? Date.now() + invokeTimeoutMs : undefined;
|
|
@@ -517,6 +517,15 @@ function createProfileStepCommentary(step) {
|
|
|
517
517
|
if (step.kind === "memory") {
|
|
518
518
|
return `Checking memory ${name}.`;
|
|
519
519
|
}
|
|
520
|
+
if (step.kind === "agent" && step.action === "invoke") {
|
|
521
|
+
return "Running model invocation.";
|
|
522
|
+
}
|
|
523
|
+
if (step.kind === "agent" && step.action === "start") {
|
|
524
|
+
return "Starting runtime stream.";
|
|
525
|
+
}
|
|
526
|
+
if (step.kind === "agent" && step.action === "startup") {
|
|
527
|
+
return `Preparing ${name}.`;
|
|
528
|
+
}
|
|
520
529
|
return null;
|
|
521
530
|
}
|
|
522
531
|
function isOpenAICompatibleStreamingCompatibilityError(binding, error) {
|