@botbotgo/agent-harness 0.0.346 → 0.0.348
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/contracts/runtime-requests.d.ts +1 -0
- package/dist/contracts/workspace.d.ts +4 -0
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/resource/backend/workspace-scoped-backend.d.ts +9 -2
- package/dist/resource/backend/workspace-scoped-backend.js +42 -22
- package/dist/resources/prompts/runtime/delegated-task-failure-recovery.md +8 -0
- package/dist/runtime/adapter/flow/stream-runtime.js +52 -18
- package/dist/runtime/adapter/invocation-result.js +49 -5
- package/dist/runtime/adapter/local-tool-invocation.js +5 -0
- package/dist/runtime/adapter/model/model-providers.js +108 -12
- package/dist/runtime/adapter/stream-event-projection.js +3 -1
- package/dist/runtime/adapter/terminal-status.d.ts +4 -0
- package/dist/runtime/adapter/terminal-status.js +67 -0
- package/dist/runtime/agent-runtime-adapter.js +51 -37
- package/dist/runtime/agent-runtime-assembly.d.ts +10 -0
- package/dist/runtime/agent-runtime-assembly.js +68 -0
- package/dist/runtime/harness/run/stream-run.js +17 -31
- package/dist/runtime/parsing/output-recovery.d.ts +2 -1
- package/dist/runtime/parsing/output-recovery.js +2 -25
- package/dist/runtime/prompts/runtime-prompts.d.ts +1 -0
- package/dist/runtime/prompts/runtime-prompts.js +1 -0
- package/dist/workspace/agent-binding-compiler.js +11 -0
- package/dist/workspace/framework-contract-validation.js +126 -26
- package/dist/workspace/object-loader.js +3 -0
- package/package.json +1 -1
|
@@ -13,8 +13,16 @@ const NODE_LLAMA_CPP_TOOL_CALL_INSTRUCTION = [
|
|
|
13
13
|
"If you need a tool, respond with only one JSON object.",
|
|
14
14
|
'Use this exact shape: {"name":"tool_name","arguments":{"key":"value"}}',
|
|
15
15
|
"Do not add markdown, prose, or code fences unless the output is wrapped inside <tool_call>...</tool_call>.",
|
|
16
|
+
"When the latest user message explicitly requests a tool call or provides a tool-call JSON object, call that tool instead of answering locally.",
|
|
17
|
+
"If the conversation already contains TOOL_RESULT for the requested work, answer from that result instead of repeating the same tool call.",
|
|
16
18
|
"If no tool is needed, answer normally.",
|
|
17
19
|
].join("\n");
|
|
20
|
+
const PROMPTED_JSON_FINAL_TOOL_CALL_REMINDER = [
|
|
21
|
+
"Final tool-call rule:",
|
|
22
|
+
"If the correct next step is a tool call, return exactly one JSON object and no prose.",
|
|
23
|
+
"If a TOOL_RESULT is already present for the requested work, do not repeat that tool call; answer normally.",
|
|
24
|
+
'Shape: {"name":"tool_name","arguments":{}}',
|
|
25
|
+
].join("\n");
|
|
18
26
|
function readModelText(value) {
|
|
19
27
|
if (typeof value === "string") {
|
|
20
28
|
return value.trim();
|
|
@@ -175,6 +183,23 @@ function readToolMessageMetadata(value) {
|
|
|
175
183
|
: undefined;
|
|
176
184
|
return { name, toolCallId };
|
|
177
185
|
}
|
|
186
|
+
function hasPriorToolResultForToolName(input, toolName) {
|
|
187
|
+
if (!toolName) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
if (Array.isArray(input)) {
|
|
191
|
+
return input.some((message) => {
|
|
192
|
+
if (mapMessageRole(message) !== "TOOL") {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
return readToolMessageMetadata(message).name === toolName;
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (typeof input === "object" && input !== null && Array.isArray(input.messages)) {
|
|
199
|
+
return hasPriorToolResultForToolName(input.messages, toolName);
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
178
203
|
function normalizeReadFileToolContent(name, content) {
|
|
179
204
|
if (name !== "read_file") {
|
|
180
205
|
return content;
|
|
@@ -274,22 +299,75 @@ function extractToolCallPayload(text) {
|
|
|
274
299
|
if (direct) {
|
|
275
300
|
return direct;
|
|
276
301
|
}
|
|
277
|
-
const fenced = trimmed
|
|
302
|
+
const fenced = extractFencePayload(trimmed);
|
|
278
303
|
if (fenced) {
|
|
279
304
|
const parsed = tryParseJson(fenced);
|
|
280
305
|
if (parsed) {
|
|
281
306
|
return parsed;
|
|
282
307
|
}
|
|
283
308
|
}
|
|
284
|
-
const xml = trimmed
|
|
309
|
+
const xml = extractTaggedContent(trimmed, "tool_call");
|
|
285
310
|
if (xml) {
|
|
286
311
|
const parsed = tryParseJson(xml);
|
|
287
312
|
if (parsed) {
|
|
288
313
|
return parsed;
|
|
289
314
|
}
|
|
290
315
|
}
|
|
316
|
+
const toolCallsXml = extractTaggedContent(trimmed, "tool_calls");
|
|
317
|
+
if (toolCallsXml) {
|
|
318
|
+
const parsed = tryParseJson(toolCallsXml);
|
|
319
|
+
if (parsed) {
|
|
320
|
+
return parsed;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const codeBlock = extractToolCodePayload(trimmed);
|
|
324
|
+
if (codeBlock) {
|
|
325
|
+
return codeBlock;
|
|
326
|
+
}
|
|
291
327
|
return null;
|
|
292
328
|
}
|
|
329
|
+
function extractFencePayload(text) {
|
|
330
|
+
const start = text.indexOf("```");
|
|
331
|
+
if (start < 0) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
const contentStart = text.indexOf("\n", start + 3);
|
|
335
|
+
const bodyStart = contentStart >= 0 ? contentStart + 1 : start + 3;
|
|
336
|
+
const end = text.indexOf("```", bodyStart);
|
|
337
|
+
if (end < 0) {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
return text.slice(bodyStart, end).trim();
|
|
341
|
+
}
|
|
342
|
+
function extractTaggedContent(text, tagName) {
|
|
343
|
+
const lowerText = text.toLowerCase();
|
|
344
|
+
const openTag = `<${tagName}>`;
|
|
345
|
+
const closeTag = `</${tagName}>`;
|
|
346
|
+
const start = lowerText.indexOf(openTag);
|
|
347
|
+
if (start < 0) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
const bodyStart = start + openTag.length;
|
|
351
|
+
const end = lowerText.indexOf(closeTag, bodyStart);
|
|
352
|
+
if (end < 0) {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
return text.slice(bodyStart, end).trim();
|
|
356
|
+
}
|
|
357
|
+
function extractToolCodePayload(text) {
|
|
358
|
+
const name = extractTaggedContent(text, "tool_code");
|
|
359
|
+
if (!name) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
const rawArgs = extractTaggedContent(text, "tool_args");
|
|
363
|
+
const parsedArgs = rawArgs ? tryParseJson(rawArgs.trim()) : null;
|
|
364
|
+
const args = typeof parsedArgs === "object" && parsedArgs !== null && !Array.isArray(parsedArgs)
|
|
365
|
+
? parsedArgs
|
|
366
|
+
: Array.isArray(parsedArgs)
|
|
367
|
+
? { args: parsedArgs }
|
|
368
|
+
: {};
|
|
369
|
+
return { name, arguments: args };
|
|
370
|
+
}
|
|
293
371
|
function normalizeParsedToolCall(payload) {
|
|
294
372
|
if (typeof payload !== "object" || payload === null) {
|
|
295
373
|
return null;
|
|
@@ -305,7 +383,9 @@ function normalizeParsedToolCall(payload) {
|
|
|
305
383
|
return null;
|
|
306
384
|
}
|
|
307
385
|
const argsCandidate = typed.arguments ?? typed.args ?? typed.parameters ?? typed.input ?? functionPayload?.arguments ?? {};
|
|
308
|
-
const args =
|
|
386
|
+
const args = Array.isArray(argsCandidate)
|
|
387
|
+
? { args: argsCandidate }
|
|
388
|
+
: salvageToolArgs(argsCandidate) ?? {};
|
|
309
389
|
return { name, args };
|
|
310
390
|
}
|
|
311
391
|
function formatBoundToolInstruction(tool) {
|
|
@@ -325,16 +405,16 @@ function formatBoundToolInstruction(tool) {
|
|
|
325
405
|
`Arguments JSON schema: ${JSON.stringify(schema)}`,
|
|
326
406
|
].filter(Boolean).join("\n");
|
|
327
407
|
}
|
|
328
|
-
function
|
|
408
|
+
function withPromptedJsonToolPrompt(input, tools) {
|
|
329
409
|
const toolInstructions = tools.map((tool) => formatBoundToolInstruction(tool)).filter((value) => Boolean(value));
|
|
330
410
|
if (toolInstructions.length === 0) {
|
|
331
411
|
return stringifyNodeLlamaCppInput(input);
|
|
332
412
|
}
|
|
333
413
|
const systemContent = `${NODE_LLAMA_CPP_TOOL_CALL_INSTRUCTION}\n\n${toolInstructions.join("\n\n")}`;
|
|
334
414
|
const prompt = stringifyNodeLlamaCppInput(input);
|
|
335
|
-
return [systemContent, prompt].filter(Boolean).join("\n\n");
|
|
415
|
+
return [systemContent, prompt, PROMPTED_JSON_FINAL_TOOL_CALL_REMINDER].filter(Boolean).join("\n\n");
|
|
336
416
|
}
|
|
337
|
-
function
|
|
417
|
+
function createPromptedJsonToolBindableModel(model, boundTools = []) {
|
|
338
418
|
return new Proxy(model, {
|
|
339
419
|
has(target, prop) {
|
|
340
420
|
if (prop === "bindTools" || prop === "invoke" || prop === "stream" || prop === "withConfig") {
|
|
@@ -344,11 +424,11 @@ function createNodeLlamaCppToolBindableModel(model, boundTools = []) {
|
|
|
344
424
|
},
|
|
345
425
|
get(target, prop, receiver) {
|
|
346
426
|
if (prop === "bindTools") {
|
|
347
|
-
return (tools) =>
|
|
427
|
+
return (tools) => createPromptedJsonToolBindableModel(target, tools);
|
|
348
428
|
}
|
|
349
429
|
if (prop === "invoke") {
|
|
350
430
|
return async (input, config) => {
|
|
351
|
-
const rawResult = await target.invoke(boundTools.length > 0 ?
|
|
431
|
+
const rawResult = await target.invoke(boundTools.length > 0 ? withPromptedJsonToolPrompt(input, boundTools) : input, config);
|
|
352
432
|
if (boundTools.length === 0) {
|
|
353
433
|
return rawResult;
|
|
354
434
|
}
|
|
@@ -357,6 +437,9 @@ function createNodeLlamaCppToolBindableModel(model, boundTools = []) {
|
|
|
357
437
|
if (!parsedToolCall) {
|
|
358
438
|
return rawResult;
|
|
359
439
|
}
|
|
440
|
+
if (hasPriorToolResultForToolName(input, parsedToolCall.name)) {
|
|
441
|
+
return rawResult;
|
|
442
|
+
}
|
|
360
443
|
return new AIMessage({
|
|
361
444
|
content: "",
|
|
362
445
|
tool_calls: [{
|
|
@@ -377,7 +460,7 @@ function createNodeLlamaCppToolBindableModel(model, boundTools = []) {
|
|
|
377
460
|
};
|
|
378
461
|
}
|
|
379
462
|
if (prop === "withConfig" && typeof target.withConfig === "function") {
|
|
380
|
-
return (config) =>
|
|
463
|
+
return (config) => createPromptedJsonToolBindableModel(target.withConfig(config), boundTools);
|
|
381
464
|
}
|
|
382
465
|
const member = Reflect.get(target, prop, receiver);
|
|
383
466
|
return typeof member === "function" ? member.bind(target) : member;
|
|
@@ -409,7 +492,7 @@ async function createNodeLlamaCppModel(model) {
|
|
|
409
492
|
}
|
|
410
493
|
try {
|
|
411
494
|
const { ChatLlamaCpp } = await import("@langchain/community/chat_models/llama_cpp");
|
|
412
|
-
return
|
|
495
|
+
return createPromptedJsonToolBindableModel(await ChatLlamaCpp.initialize({
|
|
413
496
|
...model.init,
|
|
414
497
|
modelPath,
|
|
415
498
|
}));
|
|
@@ -423,10 +506,23 @@ export async function createResolvedModel(model, modelResolver) {
|
|
|
423
506
|
return modelResolver(model.id);
|
|
424
507
|
}
|
|
425
508
|
if (model.provider === "ollama") {
|
|
426
|
-
|
|
509
|
+
const { toolCallingMode, ...init } = model.init ?? {};
|
|
510
|
+
const resolved = new ChatOllama({ model: model.model, ...init });
|
|
511
|
+
if (toolCallingMode === "prompted-json") {
|
|
512
|
+
return createPromptedJsonToolBindableModel(resolved);
|
|
513
|
+
}
|
|
514
|
+
return createProviderToolMessageCompatModel(resolved);
|
|
427
515
|
}
|
|
428
516
|
if (model.provider === "openai-compatible") {
|
|
429
|
-
|
|
517
|
+
const { toolCallingMode, ...init } = model.init ?? {};
|
|
518
|
+
const resolved = new ChatOpenAI({
|
|
519
|
+
model: model.model,
|
|
520
|
+
...normalizeOpenAICompatibleInit(init),
|
|
521
|
+
});
|
|
522
|
+
if (toolCallingMode === "prompted-json") {
|
|
523
|
+
return createPromptedJsonToolBindableModel(resolved);
|
|
524
|
+
}
|
|
525
|
+
return createProviderToolMessageCompatModel(resolved);
|
|
430
526
|
}
|
|
431
527
|
if (model.provider === "openai") {
|
|
432
528
|
return createProviderToolMessageCompatModel(new ChatOpenAI({ model: model.model, ...model.init }));
|
|
@@ -350,7 +350,9 @@ export function projectRuntimeStreamEvent(params) {
|
|
|
350
350
|
? state.lastCompletedTaskDelegationFindings
|
|
351
351
|
: "";
|
|
352
352
|
const effectiveToolOutput = salvagedTaskErrorFindings || toolResult.output;
|
|
353
|
-
const effectiveToolIsError = salvagedTaskErrorFindings
|
|
353
|
+
const effectiveToolIsError = salvagedTaskErrorFindings
|
|
354
|
+
? false
|
|
355
|
+
: toolResult.isError === true;
|
|
354
356
|
const isSuccessfulTaskResult = toolResult.toolName === "task" && effectiveToolIsError !== true;
|
|
355
357
|
const isDelegatedExecutionTool = (isDelegatedAgentEvent || state.openToolCapableTaskDelegations > 0)
|
|
356
358
|
&& toolResult.toolName !== "write_todos"
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RequestState, TerminalExecutionStatus } from "../../contracts/types.js";
|
|
2
|
+
export declare function readTerminalExecutionStatus(value: unknown): TerminalExecutionStatus | null;
|
|
3
|
+
export declare function mapTerminalStatusToRequestState(status: TerminalExecutionStatus | null): RequestState;
|
|
4
|
+
export declare function mapTerminalStatusToPlanItemStatus(status: TerminalExecutionStatus): "completed" | "failed";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const TERMINAL_STATUSES = new Set(["completed", "blocked", "failed", "refused"]);
|
|
2
|
+
function normalizeTerminalStatus(value) {
|
|
3
|
+
if (typeof value !== "string") {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
const normalized = value.trim().toLowerCase();
|
|
7
|
+
return TERMINAL_STATUSES.has(normalized)
|
|
8
|
+
? normalized
|
|
9
|
+
: null;
|
|
10
|
+
}
|
|
11
|
+
function readStatusLine(value) {
|
|
12
|
+
for (const line of value.split("\n")) {
|
|
13
|
+
const [key, ...rest] = line.split(":");
|
|
14
|
+
if (key?.trim().toLowerCase() !== "status") {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const statusValue = rest.join(":").trim().split(/\s+/)[0];
|
|
18
|
+
const status = normalizeTerminalStatus(statusValue);
|
|
19
|
+
if (status) {
|
|
20
|
+
return status;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
export function readTerminalExecutionStatus(value) {
|
|
26
|
+
const direct = normalizeTerminalStatus(value);
|
|
27
|
+
if (direct) {
|
|
28
|
+
return direct;
|
|
29
|
+
}
|
|
30
|
+
if (typeof value === "string") {
|
|
31
|
+
try {
|
|
32
|
+
return readTerminalExecutionStatus(JSON.parse(value));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return readStatusLine(value);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (typeof value !== "object" || value === null) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
for (let index = value.length - 1; index >= 0; index -= 1) {
|
|
43
|
+
const status = readTerminalExecutionStatus(value[index]);
|
|
44
|
+
if (status) {
|
|
45
|
+
return status;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const typed = value;
|
|
51
|
+
return (readTerminalExecutionStatus(typed.status)
|
|
52
|
+
?? readTerminalExecutionStatus(typed.structuredResponse)
|
|
53
|
+
?? readTerminalExecutionStatus(typed.messages)
|
|
54
|
+
?? readTerminalExecutionStatus(typed.content)
|
|
55
|
+
?? readTerminalExecutionStatus(typed.kwargs?.content)
|
|
56
|
+
?? readTerminalExecutionStatus(typed.lc_kwargs?.content)
|
|
57
|
+
?? readTerminalExecutionStatus(typed.output)
|
|
58
|
+
?? readTerminalExecutionStatus(typed.data));
|
|
59
|
+
}
|
|
60
|
+
export function mapTerminalStatusToRequestState(status) {
|
|
61
|
+
return status === "blocked" || status === "failed" || status === "refused"
|
|
62
|
+
? "failed"
|
|
63
|
+
: "completed";
|
|
64
|
+
}
|
|
65
|
+
export function mapTerminalStatusToPlanItemStatus(status) {
|
|
66
|
+
return status === "completed" ? "completed" : "failed";
|
|
67
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import {
|
|
2
|
+
import { createAsyncSubAgentMiddleware, createFilesystemMiddleware, createMemoryMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSummarizationMiddleware, createSubAgentMiddleware, FilesystemBackend, StateBackend, } from "deepagents";
|
|
3
3
|
import { createAgent, humanInTheLoopMiddleware, todoListMiddleware } from "langchain";
|
|
4
4
|
import { wrapResolvedModel, } from "./parsing/output-parsing.js";
|
|
5
|
-
import { AGENT_INTERRUPT_SENTINEL_PREFIX, buildDeepAgentCreateParams, buildLangChainCreateParams, DEFAULT_DEEPAGENT_RECURSION_LIMIT, materializeModelExposedBuiltinMiddlewareTools, resolveLangChainInvocationConfig, resolveRunnableCheckpointer, resolveRunnableInterruptOn, shouldAttachDeepAgentBackend, shouldAttachDeepAgentCheckpointer, shouldAttachDeepAgentStore, } from "./agent-runtime-assembly.js";
|
|
5
|
+
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
6
|
import { resolveDeepAgentSkillSourcePaths, } from "./adapter/compat/deepagent-compat.js";
|
|
7
|
+
import { EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION } from "./prompts/runtime-prompts.js";
|
|
7
8
|
import { buildToolNameMapping, } from "./adapter/tool/tool-name-mapping.js";
|
|
8
9
|
import { executeRequestInvocation } from "./adapter/flow/invocation-flow.js";
|
|
9
10
|
import { streamRuntimeExecution } from "./adapter/flow/stream-runtime.js";
|
|
@@ -21,8 +22,22 @@ export { buildAuthOmittingFetch, normalizeOpenAICompatibleInit } from "./adapter
|
|
|
21
22
|
export { buildToolNameMapping, createModelFacingToolNameCandidates, createModelFacingToolNameLookupCandidates, resolveModelFacingToolName, sanitizeToolNameForModel, } from "./adapter/tool/tool-name-mapping.js";
|
|
22
23
|
export { computeRemainingTimeoutMs, isRetryableProviderError, resolveBindingTimeout, resolveProviderRetryPolicy, resolveStreamIdleTimeout, resolveTimeoutMs, } from "./adapter/resilience.js";
|
|
23
24
|
import { getBindingAdapterKind, getBindingBuiltinToolsConfig, getBindingDeepAgentSubagents, getBindingExecutionParams, getBindingExecutionKind, getBindingFilesystemConfig, getBindingMemorySources, getBindingPrimaryModel, getBindingSkills, getBindingToolCount, getBindingPrimaryTools, getBindingSystemPrompt, isDeepAgentBinding, isLangChainBinding, } from "./support/compiled-binding.js";
|
|
25
|
+
class DelegatedExecutionNoToolEvidenceError extends Error {
|
|
26
|
+
constructor(agentId) {
|
|
27
|
+
super(`Delegated agent ${agentId} completed without tool execution evidence.`);
|
|
28
|
+
this.name = "DelegatedExecutionNoToolEvidenceError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function hasDelegatedExecutionToolEvidence(result) {
|
|
32
|
+
const executedToolResults = Array.isArray(result.metadata?.executedToolResults)
|
|
33
|
+
? result.metadata.executedToolResults
|
|
34
|
+
: [];
|
|
35
|
+
return executedToolResults.some((toolResult) => (toolResult.isError !== true
|
|
36
|
+
&& toolResult.toolName !== "write_todos"
|
|
37
|
+
&& toolResult.toolName !== "read_todos"));
|
|
38
|
+
}
|
|
24
39
|
function shouldUseConfigurableDeepAgentAssembly(binding) {
|
|
25
|
-
return
|
|
40
|
+
return getBindingExecutionKind(binding) === "deepagent";
|
|
26
41
|
}
|
|
27
42
|
export class AgentRuntimeAdapter {
|
|
28
43
|
options;
|
|
@@ -319,9 +334,18 @@ export class AgentRuntimeAdapter {
|
|
|
319
334
|
const childSessionId = `${sessionId}:delegated:${resolvedSubagent.name}`;
|
|
320
335
|
const childRequestId = `${requestId}:delegated:${resolvedSubagent.name}:${Date.now().toString(36)}`;
|
|
321
336
|
try {
|
|
322
|
-
const
|
|
337
|
+
const invokeOptions = {
|
|
323
338
|
...(typeof config?.context === "object" && config.context ? { context: config.context } : {}),
|
|
324
|
-
}
|
|
339
|
+
};
|
|
340
|
+
const runDelegatedRequest = (text, requestSuffix = "") => this.invoke(targetBinding, text, childSessionId, `${childRequestId}${requestSuffix}`, undefined, [], invokeOptions);
|
|
341
|
+
let result = await runDelegatedRequest(requestText);
|
|
342
|
+
const targetRequiresExecutionToolEvidence = getBindingPrimaryTools(targetBinding).length > 0;
|
|
343
|
+
if (targetRequiresExecutionToolEvidence && !hasDelegatedExecutionToolEvidence(result)) {
|
|
344
|
+
result = await runDelegatedRequest([requestText, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":tool-evidence-retry");
|
|
345
|
+
if (!hasDelegatedExecutionToolEvidence(result)) {
|
|
346
|
+
throw new DelegatedExecutionNoToolEvidenceError(targetBinding.agent.id);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
325
349
|
return wrapRequestResultAsSubagentResponse({
|
|
326
350
|
output: result.output,
|
|
327
351
|
structuredResponse: result.structuredResponse,
|
|
@@ -331,9 +355,7 @@ export class AgentRuntimeAdapter {
|
|
|
331
355
|
const message = error instanceof Error && error.message.trim().length > 0
|
|
332
356
|
? error.message.trim()
|
|
333
357
|
: "delegated execution failed";
|
|
334
|
-
|
|
335
|
-
output: `Blocked: ${message}`,
|
|
336
|
-
});
|
|
358
|
+
throw new Error(message);
|
|
337
359
|
}
|
|
338
360
|
},
|
|
339
361
|
},
|
|
@@ -422,18 +444,6 @@ export class AgentRuntimeAdapter {
|
|
|
422
444
|
ownerId: binding.agent.id,
|
|
423
445
|
skillPaths: getBindingSkills(binding),
|
|
424
446
|
}) ?? [];
|
|
425
|
-
const deepAgentConfig = buildDeepAgentCreateParams({
|
|
426
|
-
binding,
|
|
427
|
-
resolvedModel,
|
|
428
|
-
resolvedTools: [...resolvedTools, ...builtinMiddlewareTools],
|
|
429
|
-
resolvedMiddleware,
|
|
430
|
-
resolvedSubagents,
|
|
431
|
-
resolvedCheckpointer,
|
|
432
|
-
resolvedStore,
|
|
433
|
-
resolvedBackend,
|
|
434
|
-
resolvedInterruptOn,
|
|
435
|
-
resolvedSkills,
|
|
436
|
-
});
|
|
437
447
|
if (shouldUseConfigurableDeepAgentAssembly(binding)) {
|
|
438
448
|
return this.createConfigurableDeepAgentRunnable(binding, {
|
|
439
449
|
resolvedModel,
|
|
@@ -441,36 +451,33 @@ export class AgentRuntimeAdapter {
|
|
|
441
451
|
resolvedMiddleware,
|
|
442
452
|
resolvedSubagents,
|
|
443
453
|
resolvedInterruptOn,
|
|
454
|
+
resolvedCheckpointer,
|
|
455
|
+
resolvedStore,
|
|
444
456
|
resolvedBackend,
|
|
445
457
|
resolvedSkills,
|
|
446
458
|
});
|
|
447
459
|
}
|
|
448
|
-
|
|
460
|
+
throw new Error(`Agent ${binding.agent.id} has no supported deepagent assembly path`);
|
|
449
461
|
}
|
|
450
462
|
createConfigurableDeepAgentRunnable(binding, input) {
|
|
451
463
|
const builtinTools = getBindingBuiltinToolsConfig(binding) ?? {};
|
|
452
464
|
const backend = (input.resolvedBackend ?? new StateBackend({}));
|
|
453
465
|
const inlineSubagents = input.resolvedSubagents.filter((subagent) => !("graphId" in subagent));
|
|
454
466
|
const asyncSubagents = input.resolvedSubagents.filter((subagent) => "graphId" in subagent);
|
|
455
|
-
const subagents = inlineSubagents
|
|
456
|
-
? inlineSubagents
|
|
457
|
-
: [{
|
|
458
|
-
...GENERAL_PURPOSE_SUBAGENT,
|
|
459
|
-
model: input.resolvedModel,
|
|
460
|
-
tools: input.resolvedTools,
|
|
461
|
-
skills: input.resolvedSkills,
|
|
462
|
-
}, ...inlineSubagents];
|
|
467
|
+
const subagents = inlineSubagents;
|
|
463
468
|
const middleware = [
|
|
464
469
|
...(builtinTools.todos === false ? [] : [todoListMiddleware()]),
|
|
465
470
|
...(input.resolvedSkills.length > 0 ? [createSkillsMiddleware({ backend, sources: input.resolvedSkills })] : []),
|
|
466
471
|
...(builtinTools.filesystem === false ? [] : [createFilesystemMiddleware({ backend })]),
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
472
|
+
...(subagents.length > 0
|
|
473
|
+
? [createSubAgentMiddleware({
|
|
474
|
+
defaultModel: input.resolvedModel,
|
|
475
|
+
defaultTools: input.resolvedTools,
|
|
476
|
+
defaultInterruptOn: input.resolvedInterruptOn,
|
|
477
|
+
subagents: subagents,
|
|
478
|
+
generalPurposeAgent: false,
|
|
479
|
+
})]
|
|
480
|
+
: []),
|
|
474
481
|
createSummarizationMiddleware({
|
|
475
482
|
model: input.resolvedModel,
|
|
476
483
|
backend,
|
|
@@ -487,10 +494,17 @@ export class AgentRuntimeAdapter {
|
|
|
487
494
|
: undefined;
|
|
488
495
|
return createAgent({
|
|
489
496
|
model: input.resolvedModel,
|
|
490
|
-
systemPrompt:
|
|
497
|
+
systemPrompt: buildDeepAgentSystemPromptWithCapabilityHierarchy({
|
|
498
|
+
systemPrompt: getBindingSystemPrompt(binding),
|
|
499
|
+
subagents: input.resolvedSubagents,
|
|
500
|
+
skills: input.resolvedSkills,
|
|
501
|
+
tools: getBindingPrimaryTools(binding),
|
|
502
|
+
}),
|
|
491
503
|
tools: input.resolvedTools,
|
|
492
504
|
middleware: middleware,
|
|
493
505
|
name: binding.agent.id,
|
|
506
|
+
...(input.resolvedCheckpointer !== undefined ? { checkpointer: input.resolvedCheckpointer } : {}),
|
|
507
|
+
...(input.resolvedStore !== undefined ? { store: input.resolvedStore } : {}),
|
|
494
508
|
...(responseFormat !== undefined ? { responseFormat: responseFormat } : {}),
|
|
495
509
|
});
|
|
496
510
|
}
|
|
@@ -8,6 +8,16 @@ export declare function materializeModelExposedBuiltinMiddlewareTools(input: {
|
|
|
8
8
|
explicitToolNames?: string[];
|
|
9
9
|
modelExposed?: boolean | string[];
|
|
10
10
|
}): unknown[];
|
|
11
|
+
export declare function buildDeepAgentSystemPromptWithCapabilityHierarchy(input: {
|
|
12
|
+
systemPrompt?: unknown;
|
|
13
|
+
subagents: Array<Pick<UpstreamSubagentConfig, "name" | "description"> | Pick<CompiledAsyncSubAgent, "name" | "description">>;
|
|
14
|
+
skills?: string[];
|
|
15
|
+
tools?: Array<{
|
|
16
|
+
name: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
}>;
|
|
19
|
+
}): unknown;
|
|
20
|
+
export declare const buildDeepAgentSystemPromptWithSubagentCatalog: typeof buildDeepAgentSystemPromptWithCapabilityHierarchy;
|
|
11
21
|
export declare function resolveRunnableCheckpointer(options: RuntimeAdapterOptions, binding: CompiledAgentBinding): unknown;
|
|
12
22
|
export declare function resolveRunnableInterruptOn(binding: CompiledAgentBinding): Record<string, {
|
|
13
23
|
allowedDecisions: import("./adapter/tool/interrupt-policy.js").InterruptDecision[];
|
|
@@ -2,6 +2,7 @@ import { MemorySaver } from "@langchain/langgraph";
|
|
|
2
2
|
import { UPSTREAM_REQUEST_CONFIG_KEY, UPSTREAM_SESSION_CONFIG_KEY } from "./adapter/upstream-configurable-keys.js";
|
|
3
3
|
import { asStructuredExecutableTool } from "./adapter/tool/resolved-tool.js";
|
|
4
4
|
import { compileInterruptOn } from "./adapter/tool/interrupt-policy.js";
|
|
5
|
+
import { readSkillMetadata } from "./skills/skill-metadata.js";
|
|
5
6
|
import { getBindingBackendConfig, getBindingExecutionKind, getBindingExecutionParams, getBindingInterruptCompatibilityRules, getBindingMemorySources, getBindingMiddlewareConfigs, getBindingPrimaryTools, getBindingSkills, getBindingStoreConfig, } from "./support/compiled-binding.js";
|
|
6
7
|
export const AGENT_INTERRUPT_SENTINEL_PREFIX = "__agent_harness_interrupt__:";
|
|
7
8
|
export const DEFAULT_DEEPAGENT_RECURSION_LIMIT = 100;
|
|
@@ -37,6 +38,67 @@ export function materializeModelExposedBuiltinMiddlewareTools(input) {
|
|
|
37
38
|
}
|
|
38
39
|
return tools;
|
|
39
40
|
}
|
|
41
|
+
function formatCapabilityLine(item) {
|
|
42
|
+
const description = typeof item.description === "string" && item.description.length > 0
|
|
43
|
+
? `: ${item.description}`
|
|
44
|
+
: "";
|
|
45
|
+
return `- ${JSON.stringify(item.name)}${description}`;
|
|
46
|
+
}
|
|
47
|
+
function buildSkillCatalog(skillPaths) {
|
|
48
|
+
return skillPaths.map((skillPath) => {
|
|
49
|
+
const metadata = readSkillMetadata(skillPath);
|
|
50
|
+
return {
|
|
51
|
+
name: metadata.name,
|
|
52
|
+
...(metadata.description ? { description: metadata.description } : {}),
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function buildDeepAgentSystemPromptWithCapabilityHierarchy(input) {
|
|
57
|
+
const basePrompt = typeof input.systemPrompt === "string" ? input.systemPrompt : undefined;
|
|
58
|
+
const skills = buildSkillCatalog(input.skills ?? []);
|
|
59
|
+
const tools = input.tools ?? [];
|
|
60
|
+
if (input.subagents.length === 0 && skills.length === 0 && tools.length === 0) {
|
|
61
|
+
return input.systemPrompt;
|
|
62
|
+
}
|
|
63
|
+
const catalogPrompt = [
|
|
64
|
+
"Capability selection hierarchy:",
|
|
65
|
+
"1. If the current request fits an available subagent, delegate with the task tool before using local skills or raw tools.",
|
|
66
|
+
"2. If you are the selected agent and an available skill fits the request, read and follow that skill before calling raw tools.",
|
|
67
|
+
"3. Use raw tools as execution primitives for the selected agent or skill, or when no listed skill applies.",
|
|
68
|
+
"Keep each selection inside the selected capability's described responsibility boundary.",
|
|
69
|
+
"If no listed subagent, skill, or tool can responsibly handle the request, do not invent a path. Return a terminal response with status \"refused\" and explain the missing capability.",
|
|
70
|
+
"If a selected capability cannot complete after using its available tools, return a terminal response with status \"blocked\" or \"failed\" and include the blocker evidence.",
|
|
71
|
+
...(input.subagents.length > 0
|
|
72
|
+
? [
|
|
73
|
+
"",
|
|
74
|
+
"Available subagents for task delegation:",
|
|
75
|
+
...input.subagents.map(formatCapabilityLine),
|
|
76
|
+
"",
|
|
77
|
+
"When using the task tool, set subagent_type to exactly one of the listed subagent names. Do not create, translate, alias, or modify subagent names.",
|
|
78
|
+
]
|
|
79
|
+
: [
|
|
80
|
+
"",
|
|
81
|
+
"No configured specialist subagents are available for this agent. Do not use task delegation for role-internal work; select a skill first, then tools.",
|
|
82
|
+
]),
|
|
83
|
+
...(skills.length > 0
|
|
84
|
+
? [
|
|
85
|
+
"",
|
|
86
|
+
"Available skills for this agent:",
|
|
87
|
+
...skills.map(formatCapabilityLine),
|
|
88
|
+
]
|
|
89
|
+
: []),
|
|
90
|
+
...(tools.length > 0
|
|
91
|
+
? [
|
|
92
|
+
"",
|
|
93
|
+
"Raw tools available to this agent:",
|
|
94
|
+
...tools.map(formatCapabilityLine),
|
|
95
|
+
]
|
|
96
|
+
: []),
|
|
97
|
+
"",
|
|
98
|
+
].join("\n");
|
|
99
|
+
return [basePrompt, catalogPrompt].filter((part) => typeof part === "string" && part.length > 0).join("\n\n");
|
|
100
|
+
}
|
|
101
|
+
export const buildDeepAgentSystemPromptWithSubagentCatalog = buildDeepAgentSystemPromptWithCapabilityHierarchy;
|
|
40
102
|
export function resolveRunnableCheckpointer(options, binding) {
|
|
41
103
|
return options.checkpointerResolver ? options.checkpointerResolver(binding) : new MemorySaver();
|
|
42
104
|
}
|
|
@@ -146,6 +208,12 @@ export function buildDeepAgentCreateParams(input) {
|
|
|
146
208
|
]);
|
|
147
209
|
return {
|
|
148
210
|
...upstreamParams,
|
|
211
|
+
systemPrompt: buildDeepAgentSystemPromptWithSubagentCatalog({
|
|
212
|
+
systemPrompt: upstreamParams.systemPrompt,
|
|
213
|
+
subagents: input.resolvedSubagents,
|
|
214
|
+
skills: input.resolvedSkills,
|
|
215
|
+
tools: getBindingPrimaryTools(input.binding),
|
|
216
|
+
}),
|
|
149
217
|
skills: input.resolvedSkills,
|
|
150
218
|
model: input.resolvedModel,
|
|
151
219
|
tools: input.resolvedTools,
|