@botbotgo/agent-harness 0.0.353 → 0.0.355
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 +2 -2
- package/dist/package-version.js +2 -2
- package/dist/resources/prompts/runtime/autonomous-investigation-recovery.md +10 -1
- package/dist/runtime/adapter/compat/deepagent-compat.d.ts +6 -0
- package/dist/runtime/adapter/compat/deepagent-compat.js +16 -0
- package/dist/runtime/adapter/flow/invocation-flow.js +40 -2
- package/dist/runtime/adapter/invocation-result.js +28 -5
- 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 +2 -1
- package/dist/runtime/agent-runtime-adapter.js +207 -6
- package/dist/runtime/agent-runtime-assembly.d.ts +1 -0
- package/dist/runtime/agent-runtime-assembly.js +17 -2
- package/dist/runtime/harness/run/stream-run.js +12 -2
- package/dist/runtime/parsing/output-content.js +4 -0
- package/dist/runtime/parsing/output-tool-args.js +44 -0
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
2
|
-
export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.355";
|
|
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.
|
|
2
|
-
export const AGENT_HARNESS_RELEASE_DATE = "2026-04-
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.355";
|
|
2
|
+
export const AGENT_HARNESS_RELEASE_DATE = "2026-04-25";
|
|
@@ -1 +1,10 @@
|
|
|
1
|
-
Do not stop at a plan or ask the user to choose the next obvious diagnostic step when the request is for deep investigation, root-cause analysis, or step-by-step execution. Do not ask for more background, scope, logs, or environment details until you have first exhausted the context and tools already available in this runtime.
|
|
1
|
+
Do not stop at a plan or ask the user to choose the next obvious diagnostic step when the request is for deep investigation, root-cause analysis, or step-by-step execution. Do not ask for more background, scope, logs, or environment details until you have first exhausted the context and tools already available in this runtime.
|
|
2
|
+
|
|
3
|
+
If no concrete execution has happened yet, start from the current workspace, shell, and attached runtime/tool context by default, then continue the investigation yourself with the next concrete tool call. If the user explicitly asked for a plan, or if the task is clearly non-trivial and multi-step, call write_todos first with concrete investigation/execution steps before any other tool call or final answer.
|
|
4
|
+
|
|
5
|
+
If a todo board already exists, do not restart planning and do not repeat the same clarification request. Use the current todo board and prior tool results to choose one of these terminally useful actions:
|
|
6
|
+
- If more evidence is genuinely needed and an available tool can get it, make the next concrete tool call.
|
|
7
|
+
- If the existing evidence is enough to answer, update the todo board to completed or blocked as appropriate, then provide the final answer grounded in the tool results.
|
|
8
|
+
- If the available tools cannot resolve the remaining work, update the todo board to blocked or failed and provide a blocker report with the evidence.
|
|
9
|
+
|
|
10
|
+
Never print a tool-call JSON object, function call, or tool name as prose when you intend to use a tool. Actually call the tool. Ask a blocking clarification question only after the available evidence is genuinely insufficient to continue.
|
|
@@ -11,3 +11,9 @@ export declare function resolveDeepAgentSkillSourcePaths(options: {
|
|
|
11
11
|
ownerId: string;
|
|
12
12
|
skillPaths?: string[];
|
|
13
13
|
}): string[] | undefined;
|
|
14
|
+
export declare function resolveDeepAgentSkillSourceRootPaths(options: {
|
|
15
|
+
workspaceRoot?: string;
|
|
16
|
+
runtimeRoot?: string;
|
|
17
|
+
ownerId: string;
|
|
18
|
+
skillPaths?: string[];
|
|
19
|
+
}): string[] | undefined;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
2
3
|
export function relativizeDeepAgentSkillSourcePaths(workspaceRoot, skillPaths) {
|
|
3
4
|
if (!workspaceRoot || !skillPaths) {
|
|
4
5
|
return skillPaths;
|
|
@@ -24,3 +25,18 @@ export function resolveDeepAgentSkillSourcePaths(options) {
|
|
|
24
25
|
}
|
|
25
26
|
return relativizeDeepAgentSkillSourcePaths(workspaceRoot, skillPaths) ?? skillPaths;
|
|
26
27
|
}
|
|
28
|
+
export function resolveDeepAgentSkillSourceRootPaths(options) {
|
|
29
|
+
const { workspaceRoot, skillPaths } = options;
|
|
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;
|
|
42
|
+
}
|
|
@@ -4,9 +4,10 @@ import { finalizeRequestResult } from "../invocation-result.js";
|
|
|
4
4
|
import { invokeRuntimeWithLocalTools } from "./invoke-runtime.js";
|
|
5
5
|
import { buildInvocationRequest } from "../model/invocation-request.js";
|
|
6
6
|
import { UPSTREAM_REQUEST_CONFIG_KEY, UPSTREAM_SESSION_CONFIG_KEY } from "../upstream-configurable-keys.js";
|
|
7
|
-
import { extractVisibleOutput, tryParseJson } from "../../parsing/output-parsing.js";
|
|
7
|
+
import { appendToolRecoveryInstruction, extractVisibleOutput, tryParseJson } from "../../parsing/output-parsing.js";
|
|
8
8
|
import { salvageJsonToolCalls } from "../../parsing/output-tool-args.js";
|
|
9
9
|
import { isEmptyFinalAiMessageError } from "../resilience.js";
|
|
10
|
+
import { AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION } from "../../prompts/runtime-prompts.js";
|
|
10
11
|
function readBindingExecutionParams(binding) {
|
|
11
12
|
const params = binding.execution?.params ?? binding.deepAgentParams ?? binding.langchainAgentParams;
|
|
12
13
|
return {
|
|
@@ -36,6 +37,29 @@ function isDelegationOnlyBinding(binding) {
|
|
|
36
37
|
function hasTaskDelegationEvidence(executedToolResults) {
|
|
37
38
|
return executedToolResults.some((item) => item.toolName === "task");
|
|
38
39
|
}
|
|
40
|
+
function hasIncompleteTodos(value) {
|
|
41
|
+
if (!Array.isArray(value)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return value.some((todo) => {
|
|
45
|
+
if (typeof todo !== "object" || todo === null) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const status = typeof todo.status === "string"
|
|
49
|
+
? todo.status.trim().toLowerCase()
|
|
50
|
+
: "";
|
|
51
|
+
return status === "pending" || status === "in_progress";
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function hasIncompleteUpstreamPlan(value) {
|
|
55
|
+
if (typeof value !== "object" || value === null) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const typed = value;
|
|
59
|
+
return hasIncompleteTodos(typed.todos)
|
|
60
|
+
|| hasIncompleteTodos(typed.stateSnapshot?.todos)
|
|
61
|
+
|| hasIncompleteTodos(typed.metadata?.stateSnapshot?.todos);
|
|
62
|
+
}
|
|
39
63
|
function hasNativeTaskDelegationIntent(value) {
|
|
40
64
|
if (typeof value !== "object" || value === null) {
|
|
41
65
|
return false;
|
|
@@ -285,11 +309,25 @@ export async function executeRequestInvocation(options) {
|
|
|
285
309
|
toolRuntimeContext: invokeOptions.toolRuntimeContext,
|
|
286
310
|
});
|
|
287
311
|
}
|
|
288
|
-
|
|
312
|
+
let result = localOrUpstreamInvocation.result;
|
|
289
313
|
const executedToolResults = [...localOrUpstreamInvocation.executedToolResults];
|
|
290
314
|
if (!result) {
|
|
291
315
|
throw new Error("Agent invocation returned no result");
|
|
292
316
|
}
|
|
317
|
+
if (options.resumePayload === undefined
|
|
318
|
+
&& options.binding.harnessRuntime.executionContract?.requiresPlan === true
|
|
319
|
+
&& hasIncompleteUpstreamPlan(result)
|
|
320
|
+
&& !extractVisibleOutput(result).trim()) {
|
|
321
|
+
const messages = Array.isArray(result.messages)
|
|
322
|
+
? result.messages
|
|
323
|
+
: undefined;
|
|
324
|
+
const recoveryBase = messages ? { messages } : request;
|
|
325
|
+
const recoveredRequest = appendToolRecoveryInstruction(recoveryBase, AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION);
|
|
326
|
+
const recoveredInvocation = await invokeOnce(recoveredRequest);
|
|
327
|
+
localOrUpstreamInvocation = recoveredInvocation;
|
|
328
|
+
result = recoveredInvocation.result;
|
|
329
|
+
executedToolResults.splice(0, executedToolResults.length, ...recoveredInvocation.executedToolResults);
|
|
330
|
+
}
|
|
293
331
|
try {
|
|
294
332
|
return finalizeRequestResult({
|
|
295
333
|
bindingAgentId: options.binding.agent.id,
|
|
@@ -57,6 +57,17 @@ function looksLikeToolBlocker(value) {
|
|
|
57
57
|
|| /invalid tool call/iu.test(normalized)
|
|
58
58
|
|| /tool call.*schema/iu.test(normalized);
|
|
59
59
|
}
|
|
60
|
+
function isSubstantiveFinalOutput(value) {
|
|
61
|
+
const normalized = sanitizeVisibleText(value).trim();
|
|
62
|
+
if (normalized.length < 80) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return !looksLikeLeakedToolCallText(normalized)
|
|
66
|
+
&& !looksLikeToolBlocker(normalized)
|
|
67
|
+
&& !looksLikeClarificationQuestion(normalized)
|
|
68
|
+
&& !looksLikeNonEvidenceApology(normalized)
|
|
69
|
+
&& !isLowSignalStructuredCompletion(normalized);
|
|
70
|
+
}
|
|
60
71
|
function normalizeToolOutputText(output) {
|
|
61
72
|
const directText = typeof output === "string"
|
|
62
73
|
? sanitizeVisibleText(output).trim()
|
|
@@ -343,18 +354,30 @@ export function finalizeRequestResult(params) {
|
|
|
343
354
|
&& !hasFinalMessageToolCalls(result)) {
|
|
344
355
|
throw new Error("empty_final_output");
|
|
345
356
|
}
|
|
357
|
+
const stateSnapshot = buildStateSnapshot(result);
|
|
358
|
+
const hasIncompleteRequiredPlan = binding?.harnessRuntime?.executionContract?.requiresPlan === true
|
|
359
|
+
&& hasIncompleteStateSnapshotPlan(stateSnapshot);
|
|
346
360
|
const serializedResult = JSON.stringify(result, null, 2);
|
|
347
|
-
|
|
361
|
+
let output = resolveDeterministicFinalOutput({
|
|
348
362
|
visibleOutput,
|
|
349
363
|
toolFallback,
|
|
350
364
|
executedToolResults: allExecutedToolResults,
|
|
351
365
|
})
|
|
352
366
|
|| (containsLikelySkillDocument(result) ? "" : serializedResult);
|
|
367
|
+
const preliminaryTerminalStatus = readTerminalExecutionStatus(output);
|
|
368
|
+
const hasMissingRequiredFinalAnswer = binding?.harnessRuntime?.executionContract?.requiresPlan === true
|
|
369
|
+
&& !visibleOutput
|
|
370
|
+
&& !preliminaryTerminalStatus
|
|
371
|
+
&& allExecutedToolResults.some((toolResult) => toolResult.isError !== true && toolResult.toolName !== "write_todos" && toolResult.toolName !== "read_todos");
|
|
372
|
+
if (hasIncompleteRequiredPlan && !visibleOutput) {
|
|
373
|
+
output = "runtime_error=Agent ended while required plan still had unfinished work.";
|
|
374
|
+
}
|
|
375
|
+
else if (hasMissingRequiredFinalAnswer) {
|
|
376
|
+
output = "runtime_error=Agent ended after tool execution without producing a final answer.";
|
|
377
|
+
}
|
|
353
378
|
const finalMessageText = sanitizeVisibleText(output);
|
|
354
379
|
const terminalStatus = structuredTerminalStatus ?? readTerminalExecutionStatus(finalMessageText);
|
|
355
|
-
const
|
|
356
|
-
const hasIncompleteRequiredPlan = binding?.harnessRuntime?.executionContract?.requiresPlan === true
|
|
357
|
-
&& hasIncompleteStateSnapshotPlan(stateSnapshot);
|
|
380
|
+
const hasSubstantiveFinalOutput = Boolean(visibleOutput) && isSubstantiveFinalOutput(finalMessageText);
|
|
358
381
|
const hasTerminalToolBlocker = looksLikeToolBlocker(finalMessageText);
|
|
359
382
|
const memoryCandidates = allExecutedToolResults.flatMap((toolResult) => toolResult.memoryCandidates ?? []);
|
|
360
383
|
return {
|
|
@@ -363,7 +386,7 @@ export function finalizeRequestResult(params) {
|
|
|
363
386
|
agentId: bindingAgentId,
|
|
364
387
|
state: Array.isArray(result.__interrupt__) && result.__interrupt__.length > 0
|
|
365
388
|
? "waiting_for_approval"
|
|
366
|
-
: hasIncompleteRequiredPlan
|
|
389
|
+
: (hasIncompleteRequiredPlan && !hasSubstantiveFinalOutput) || hasMissingRequiredFinalAnswer
|
|
367
390
|
? "failed"
|
|
368
391
|
: hasTerminalToolBlocker
|
|
369
392
|
? "failed"
|
|
@@ -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
|
}
|
|
@@ -2,7 +2,7 @@ import type { CompiledAgentBinding, MessageContent, RequestResult, RuntimeAdapte
|
|
|
2
2
|
import { type RuntimeStreamChunk } from "./parsing/stream-event-parsing.js";
|
|
3
3
|
import { AGENT_INTERRUPT_SENTINEL_PREFIX, buildDeepAgentCreateParams, buildLangChainCreateParams, DEFAULT_DEEPAGENT_RECURSION_LIMIT, materializeModelExposedBuiltinMiddlewareTools, resolveLangChainInvocationConfig, resolveRunnableCheckpointer, resolveRunnableInterruptOn } from "./agent-runtime-assembly.js";
|
|
4
4
|
import { RuntimeOperationTimeoutError } from "./adapter/runtime-shell.js";
|
|
5
|
-
export { materializeDeepAgentSkillSourcePaths, resolveDeepAgentSkillSourcePaths, relativizeDeepAgentSkillSourcePaths, } from "./adapter/compat/deepagent-compat.js";
|
|
5
|
+
export { materializeDeepAgentSkillSourcePaths, resolveDeepAgentSkillSourcePaths, resolveDeepAgentSkillSourceRootPaths, relativizeDeepAgentSkillSourcePaths, } from "./adapter/compat/deepagent-compat.js";
|
|
6
6
|
export { buildAuthOmittingFetch, normalizeOpenAICompatibleInit } from "./adapter/compat/openai-compatible.js";
|
|
7
7
|
export { buildToolNameMapping, createModelFacingToolNameCandidates, createModelFacingToolNameLookupCandidates, resolveModelFacingToolName, sanitizeToolNameForModel, } from "./adapter/tool/tool-name-mapping.js";
|
|
8
8
|
export { computeRemainingTimeoutMs, isRetryableProviderError, resolveBindingTimeout, resolveProviderRetryPolicy, resolveStreamIdleTimeout, resolveTimeoutMs, } from "./adapter/resilience.js";
|
|
@@ -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,9 +1,10 @@
|
|
|
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 { sanitizeVisibleText, 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
|
-
import { resolveDeepAgentSkillSourcePaths, } from "./adapter/compat/deepagent-compat.js";
|
|
7
|
+
import { resolveDeepAgentSkillSourcePaths, resolveDeepAgentSkillSourceRootPaths, } from "./adapter/compat/deepagent-compat.js";
|
|
7
8
|
import { EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION } from "./prompts/runtime-prompts.js";
|
|
8
9
|
import { buildToolNameMapping, } from "./adapter/tool/tool-name-mapping.js";
|
|
9
10
|
import { executeRequestInvocation } from "./adapter/flow/invocation-flow.js";
|
|
@@ -17,11 +18,11 @@ import { resolveAdapterTools } from "./adapter/tool-resolution.js";
|
|
|
17
18
|
import { resolveRuntimeStreamExecutionContext, } from "./adapter/flow/execution-context.js";
|
|
18
19
|
import { isRetryableProviderError } from "./adapter/resilience.js";
|
|
19
20
|
import { UPSTREAM_REQUEST_CONFIG_KEY, UPSTREAM_SESSION_CONFIG_KEY } from "./adapter/upstream-configurable-keys.js";
|
|
20
|
-
export { materializeDeepAgentSkillSourcePaths, resolveDeepAgentSkillSourcePaths, relativizeDeepAgentSkillSourcePaths, } from "./adapter/compat/deepagent-compat.js";
|
|
21
|
+
export { materializeDeepAgentSkillSourcePaths, resolveDeepAgentSkillSourcePaths, resolveDeepAgentSkillSourceRootPaths, relativizeDeepAgentSkillSourcePaths, } from "./adapter/compat/deepagent-compat.js";
|
|
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();
|
|
@@ -437,13 +509,17 @@ export class AgentRuntimeAdapter {
|
|
|
437
509
|
? resolveRunnableCheckpointer(this.options, binding)
|
|
438
510
|
: undefined;
|
|
439
511
|
const resolvedStore = shouldAttachDeepAgentStore(binding) ? this.options.storeResolver?.(binding) : undefined;
|
|
440
|
-
const resolvedBackend = shouldAttachDeepAgentBackend(binding) ? this.options.backendResolver?.(binding) : undefined;
|
|
441
512
|
const resolvedSkills = resolveDeepAgentSkillSourcePaths({
|
|
442
513
|
workspaceRoot: binding.harnessRuntime.workspaceRoot,
|
|
443
514
|
runtimeRoot: binding.harnessRuntime.runtimeRoot,
|
|
444
515
|
ownerId: binding.agent.id,
|
|
445
516
|
skillPaths: getBindingSkills(binding),
|
|
446
517
|
}) ?? [];
|
|
518
|
+
const resolvedBackend = shouldAttachDeepAgentBackend(binding)
|
|
519
|
+
? (this.options.backendResolver?.(binding) ?? (resolvedSkills.length > 0 ? this.resolveFilesystemBackend(binding, {
|
|
520
|
+
sessionId: options.sessionId ?? options.legacySessionId,
|
|
521
|
+
}) : undefined))
|
|
522
|
+
: undefined;
|
|
447
523
|
if (shouldUseConfigurableDeepAgentAssembly(binding)) {
|
|
448
524
|
return this.createConfigurableDeepAgentRunnable(binding, {
|
|
449
525
|
resolvedModel,
|
|
@@ -467,7 +543,15 @@ export class AgentRuntimeAdapter {
|
|
|
467
543
|
const subagents = inlineSubagents;
|
|
468
544
|
const middleware = [
|
|
469
545
|
...(builtinTools.todos === false ? [] : [todoListMiddleware()]),
|
|
470
|
-
...(input.resolvedSkills.length > 0 ? [createSkillsMiddleware({
|
|
546
|
+
...(input.resolvedSkills.length > 0 ? [createSkillsMiddleware({
|
|
547
|
+
backend,
|
|
548
|
+
sources: resolveDeepAgentSkillSourceRootPaths({
|
|
549
|
+
workspaceRoot: binding.harnessRuntime.workspaceRoot,
|
|
550
|
+
runtimeRoot: binding.harnessRuntime.runtimeRoot,
|
|
551
|
+
ownerId: binding.agent.id,
|
|
552
|
+
skillPaths: input.resolvedSkills,
|
|
553
|
+
}) ?? input.resolvedSkills,
|
|
554
|
+
})] : []),
|
|
471
555
|
...(builtinTools.filesystem === false ? [] : [createFilesystemMiddleware({ backend })]),
|
|
472
556
|
...(subagents.length > 0
|
|
473
557
|
? [createSubAgentMiddleware({
|
|
@@ -557,6 +641,36 @@ export class AgentRuntimeAdapter {
|
|
|
557
641
|
},
|
|
558
642
|
};
|
|
559
643
|
}
|
|
644
|
+
const compactDelegation = await this.tryDelegateWithCompactRouter(binding, input, sessionId, requestId, {
|
|
645
|
+
...options,
|
|
646
|
+
sessionId,
|
|
647
|
+
requestId,
|
|
648
|
+
});
|
|
649
|
+
if (compactDelegation) {
|
|
650
|
+
const output = typeof compactDelegation.toolOutput === "string"
|
|
651
|
+
? compactDelegation.toolOutput
|
|
652
|
+
: JSON.stringify(compactDelegation.toolOutput);
|
|
653
|
+
const delegatedToolResults = Array.isArray(compactDelegation.delegatedResult?.metadata?.executedToolResults)
|
|
654
|
+
? compactDelegation.delegatedResult.metadata.executedToolResults
|
|
655
|
+
: [];
|
|
656
|
+
return {
|
|
657
|
+
sessionId,
|
|
658
|
+
requestId,
|
|
659
|
+
agentId: binding.agent.id,
|
|
660
|
+
state: compactDelegation.delegatedResult?.state ?? "completed",
|
|
661
|
+
output: sanitizeVisibleText(output),
|
|
662
|
+
finalMessageText: sanitizeVisibleText(output),
|
|
663
|
+
metadata: {
|
|
664
|
+
executedToolResults: [
|
|
665
|
+
{
|
|
666
|
+
toolName: "task",
|
|
667
|
+
output: compactDelegation.toolOutput,
|
|
668
|
+
},
|
|
669
|
+
...delegatedToolResults,
|
|
670
|
+
],
|
|
671
|
+
},
|
|
672
|
+
};
|
|
673
|
+
}
|
|
560
674
|
const callRuntime = async (activeBinding, activeRequest) => {
|
|
561
675
|
return this.invokeWithProviderRetry(activeBinding, async () => {
|
|
562
676
|
const runnable = await this.create(activeBinding, { sessionId });
|
|
@@ -612,6 +726,74 @@ export class AgentRuntimeAdapter {
|
|
|
612
726
|
return invokeRequest();
|
|
613
727
|
}
|
|
614
728
|
}
|
|
729
|
+
async tryDelegateWithCompactRouter(binding, input, sessionId, requestId, options = {}) {
|
|
730
|
+
if (!isDelegationOnlyDeepAgentBinding(binding)) {
|
|
731
|
+
return null;
|
|
732
|
+
}
|
|
733
|
+
if (!this.options.bindingResolver) {
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
736
|
+
const primaryModel = getBindingPrimaryModel(binding);
|
|
737
|
+
if (!primaryModel) {
|
|
738
|
+
return null;
|
|
739
|
+
}
|
|
740
|
+
const requestText = extractMessageText(input).trim();
|
|
741
|
+
if (!requestText) {
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
744
|
+
const subagents = getBindingSubagents(binding);
|
|
745
|
+
const subagentCatalog = subagents
|
|
746
|
+
.map((subagent) => `- ${subagent.name}: ${subagent.description}`)
|
|
747
|
+
.join("\n");
|
|
748
|
+
const prompt = [
|
|
749
|
+
primaryModel.init?.think === false ? "/no_think" : "",
|
|
750
|
+
"You are selecting a subagent for a delegation-only agent.",
|
|
751
|
+
"Choose exactly one listed subagent when it can responsibly handle the request.",
|
|
752
|
+
"Return only JSON with this shape:",
|
|
753
|
+
"{\"subagent_type\":\"<listed subagent name>\"}",
|
|
754
|
+
"If no listed subagent can handle the request, return only:",
|
|
755
|
+
"{\"status\":\"refused\",\"reason\":\"No configured subagent can handle the request.\"}",
|
|
756
|
+
"Available subagents:",
|
|
757
|
+
subagentCatalog,
|
|
758
|
+
"User request:",
|
|
759
|
+
requestText,
|
|
760
|
+
].filter(Boolean).join("\n\n");
|
|
761
|
+
const model = await this.resolveModel(primaryModel);
|
|
762
|
+
if (typeof model.invoke !== "function") {
|
|
763
|
+
return null;
|
|
764
|
+
}
|
|
765
|
+
const raw = await this.invokeWithProviderRetry(binding, () => this.withTimeout(() => model.invoke(prompt, resolveLangChainInvocationConfig(binding, {
|
|
766
|
+
sessionId,
|
|
767
|
+
requestId,
|
|
768
|
+
context: options.context,
|
|
769
|
+
toolRuntimeContext: this.buildFunctionToolRuntimeContext(binding, {
|
|
770
|
+
...options,
|
|
771
|
+
sessionId,
|
|
772
|
+
requestId,
|
|
773
|
+
}),
|
|
774
|
+
})), resolveBindingTimeout(binding), "delegation router invoke", "invoke"));
|
|
775
|
+
const parsed = parseFirstJsonObject(readModelText(raw));
|
|
776
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
const subagentType = typeof parsed.subagent_type === "string"
|
|
780
|
+
? parsed.subagent_type
|
|
781
|
+
: "";
|
|
782
|
+
if (!subagents.some((subagent) => subagent.name === subagentType)) {
|
|
783
|
+
return null;
|
|
784
|
+
}
|
|
785
|
+
const selectedBinding = this.options.bindingResolver(subagentType);
|
|
786
|
+
if (!selectedBinding) {
|
|
787
|
+
return null;
|
|
788
|
+
}
|
|
789
|
+
const delegatedResult = await this.invoke(selectedBinding, requestText, sessionId, `${requestId}:${subagentType}`, undefined, [], {
|
|
790
|
+
context: options.context,
|
|
791
|
+
state: options.state,
|
|
792
|
+
files: options.files,
|
|
793
|
+
memoryContext: options.memoryContext,
|
|
794
|
+
});
|
|
795
|
+
return { toolOutput: delegatedResult.output, delegatedResult };
|
|
796
|
+
}
|
|
615
797
|
async *stream(binding, input, sessionId, history = [], options = {}) {
|
|
616
798
|
const directListing = await this.tryHandleDirectWorkspaceListing(binding, input, {
|
|
617
799
|
...options,
|
|
@@ -630,6 +812,25 @@ export class AgentRuntimeAdapter {
|
|
|
630
812
|
};
|
|
631
813
|
return;
|
|
632
814
|
}
|
|
815
|
+
const compactDelegation = await this.tryDelegateWithCompactRouter(binding, input, sessionId, options.requestId ?? sessionId, {
|
|
816
|
+
...options,
|
|
817
|
+
sessionId,
|
|
818
|
+
requestId: options.requestId,
|
|
819
|
+
});
|
|
820
|
+
if (compactDelegation) {
|
|
821
|
+
yield {
|
|
822
|
+
kind: "tool-result",
|
|
823
|
+
toolName: "task",
|
|
824
|
+
output: compactDelegation.toolOutput,
|
|
825
|
+
};
|
|
826
|
+
yield {
|
|
827
|
+
kind: "content",
|
|
828
|
+
content: typeof compactDelegation.toolOutput === "string"
|
|
829
|
+
? compactDelegation.toolOutput
|
|
830
|
+
: JSON.stringify(compactDelegation.toolOutput),
|
|
831
|
+
};
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
633
834
|
const invokeTimeoutMs = resolveBindingTimeout(binding);
|
|
634
835
|
const streamIdleTimeoutMs = resolveStreamIdleTimeout(binding);
|
|
635
836
|
const streamDeadlineAt = invokeTimeoutMs ? Date.now() + invokeTimeoutMs : undefined;
|
|
@@ -8,6 +8,7 @@ export declare function materializeModelExposedBuiltinMiddlewareTools(input: {
|
|
|
8
8
|
explicitToolNames?: string[];
|
|
9
9
|
modelExposed?: boolean | string[];
|
|
10
10
|
}): unknown[];
|
|
11
|
+
export declare function buildRuntimeTemporalContext(now?: Date): string;
|
|
11
12
|
export declare function buildDeepAgentSystemPromptWithCapabilityHierarchy(input: {
|
|
12
13
|
systemPrompt?: unknown;
|
|
13
14
|
subagents: Array<Pick<UpstreamSubagentConfig, "name" | "description"> | Pick<CompiledAsyncSubAgent, "name" | "description">>;
|
|
@@ -53,6 +53,21 @@ function buildSkillCatalog(skillPaths) {
|
|
|
53
53
|
};
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
+
export function buildRuntimeTemporalContext(now = new Date()) {
|
|
57
|
+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "system local time";
|
|
58
|
+
return [
|
|
59
|
+
"Runtime temporal context:",
|
|
60
|
+
`- Current date/time: ${now.toISOString()}`,
|
|
61
|
+
`- Local time zone: ${timeZone}`,
|
|
62
|
+
"Use the runtime date/time above as authoritative for interpreting current, latest, recent, today, tomorrow, yesterday, and tool-returned dates.",
|
|
63
|
+
"Do not reject tool evidence as future-dated solely because it is newer than the model's training data.",
|
|
64
|
+
].join("\n");
|
|
65
|
+
}
|
|
66
|
+
function appendRuntimeTemporalContext(systemPrompt) {
|
|
67
|
+
return [typeof systemPrompt === "string" ? systemPrompt : undefined, buildRuntimeTemporalContext()]
|
|
68
|
+
.filter((part) => typeof part === "string" && part.length > 0)
|
|
69
|
+
.join("\n\n");
|
|
70
|
+
}
|
|
56
71
|
export function buildDeepAgentSystemPromptWithCapabilityHierarchy(input) {
|
|
57
72
|
const basePrompt = typeof input.systemPrompt === "string" ? input.systemPrompt : undefined;
|
|
58
73
|
const skills = buildSkillCatalog(input.skills ?? []);
|
|
@@ -122,7 +137,7 @@ export function buildDeepAgentSystemPromptWithCapabilityHierarchy(input) {
|
|
|
122
137
|
: []),
|
|
123
138
|
"",
|
|
124
139
|
].join("\n");
|
|
125
|
-
return [basePrompt, catalogPrompt].filter((part) => typeof part === "string" && part.length > 0).join("\n\n");
|
|
140
|
+
return appendRuntimeTemporalContext([basePrompt, catalogPrompt].filter((part) => typeof part === "string" && part.length > 0).join("\n\n"));
|
|
126
141
|
}
|
|
127
142
|
export const buildDeepAgentSystemPromptWithSubagentCatalog = buildDeepAgentSystemPromptWithCapabilityHierarchy;
|
|
128
143
|
export function resolveRunnableCheckpointer(options, binding) {
|
|
@@ -175,7 +190,7 @@ export function buildLangChainCreateParams(input) {
|
|
|
175
190
|
...(legacyPassthrough ?? {}),
|
|
176
191
|
...(langchainPassthrough ?? {}),
|
|
177
192
|
...(input.passthroughOverride ?? {}),
|
|
178
|
-
systemPrompt: input.systemPromptOverride ?? executionParams.systemPrompt,
|
|
193
|
+
systemPrompt: appendRuntimeTemporalContext(input.systemPromptOverride ?? executionParams.systemPrompt),
|
|
179
194
|
model: input.resolvedModel,
|
|
180
195
|
tools: input.resolvedTools,
|
|
181
196
|
middleware: input.resolvedMiddleware,
|
|
@@ -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) {
|
|
@@ -1083,8 +1092,9 @@ export async function* streamHarnessRun(options) {
|
|
|
1083
1092
|
visibleOutput: assistantOutput,
|
|
1084
1093
|
executedToolResults,
|
|
1085
1094
|
});
|
|
1086
|
-
|
|
1087
|
-
|
|
1095
|
+
const terminalStructuredStatus = readTerminalExecutionStatus(deterministicToolEvidenceOutput);
|
|
1096
|
+
const canUseDeterministicToolEvidenceOutput = !currentPlanState || !planStateHasActiveItems(currentPlanState) || Boolean(terminalStructuredStatus);
|
|
1097
|
+
if (!assistantOutput && sawSuccessfulToolResult && deterministicToolEvidenceOutput && canUseDeterministicToolEvidenceOutput) {
|
|
1088
1098
|
if (terminalStructuredStatus && currentPlanState && planStateHasActiveItems(currentPlanState)) {
|
|
1089
1099
|
const reconciledPlanState = reconcilePlanStateToTerminalStatus(currentPlanState, mapTerminalStatusToPlanItemStatus(terminalStructuredStatus), new Date().toISOString());
|
|
1090
1100
|
const signature = buildPlanStateSignature(reconciledPlanState);
|
|
@@ -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 (salvageJsonToolCalls(remaining).length > 0) {
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
62
65
|
while (remaining.length > 0) {
|
|
63
66
|
const consumed = consumeLeadingFunctionLikeToolCall(remaining);
|
|
64
67
|
if (!consumed) {
|
|
@@ -84,6 +87,7 @@ function stripVisibleFunctionLikeToolCallText(value) {
|
|
|
84
87
|
}
|
|
85
88
|
export function sanitizeVisibleText(value) {
|
|
86
89
|
return stripVisibleFunctionLikeToolCallText(value
|
|
90
|
+
.replace(/<agent_memory>[\s\S]*?<\/agent_memory>/giu, "")
|
|
87
91
|
.replace(/[A-Za-z0-9_]*Middleware\.after_model/g, "")
|
|
88
92
|
.replace(/todoListMiddleware\.after_model/g, "")
|
|
89
93
|
.replace(/__end__+/g, "")
|
|
@@ -205,6 +205,43 @@ function closeJsonContainerSuffix(value) {
|
|
|
205
205
|
}
|
|
206
206
|
return `${trimmed}${stack.reverse().join("")}`;
|
|
207
207
|
}
|
|
208
|
+
function normalizePythonLikeJson(value) {
|
|
209
|
+
const trimmed = value.trim();
|
|
210
|
+
if (!trimmed.includes("'")) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
let output = "";
|
|
214
|
+
let inSingle = false;
|
|
215
|
+
let inDouble = false;
|
|
216
|
+
let escaping = false;
|
|
217
|
+
for (const char of trimmed) {
|
|
218
|
+
if (escaping) {
|
|
219
|
+
output += char;
|
|
220
|
+
escaping = false;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (char === "\\") {
|
|
224
|
+
output += char;
|
|
225
|
+
escaping = true;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (char === "\"" && !inSingle) {
|
|
229
|
+
inDouble = !inDouble;
|
|
230
|
+
output += char;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (char === "'" && !inDouble) {
|
|
234
|
+
inSingle = !inSingle;
|
|
235
|
+
output += "\"";
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
output += char;
|
|
239
|
+
}
|
|
240
|
+
if (inSingle || inDouble) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
return output;
|
|
244
|
+
}
|
|
208
245
|
export function salvageToolArgs(value) {
|
|
209
246
|
if (typeof value === "object" && value && !Array.isArray(value)) {
|
|
210
247
|
return value;
|
|
@@ -255,6 +292,13 @@ export function salvageJsonToolCalls(value) {
|
|
|
255
292
|
if (direct) {
|
|
256
293
|
return direct;
|
|
257
294
|
}
|
|
295
|
+
const pythonLike = normalizePythonLikeJson(trimmed);
|
|
296
|
+
if (pythonLike) {
|
|
297
|
+
const parsed = tryParseJson(pythonLike);
|
|
298
|
+
if (parsed) {
|
|
299
|
+
return parsed;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
258
302
|
const closed = closeJsonContainerSuffix(trimmed);
|
|
259
303
|
if (closed) {
|
|
260
304
|
const parsed = tryParseJson(closed);
|