@botbotgo/agent-harness 0.0.134 → 0.0.135
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/README.md +58 -0
- package/README.zh.md +47 -0
- package/dist/contracts/workspace.d.ts +10 -0
- package/dist/extensions.js +8 -0
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/resource/mcp-tool-support.d.ts +4 -0
- package/dist/resource/mcp-tool-support.js +112 -35
- package/dist/resource/resource-impl.js +198 -6
- package/dist/runtime/adapter/runtime-shell.d.ts +3 -1
- package/dist/runtime/adapter/runtime-shell.js +2 -1
- package/dist/runtime/adapter/tool/tool-arguments.js +1 -0
- package/dist/runtime/adapter/tool/tool-hitl.js +3 -0
- package/dist/runtime/agent-runtime-adapter.d.ts +6 -0
- package/dist/runtime/agent-runtime-adapter.js +32 -2
- package/dist/runtime/harness.js +2 -0
- package/dist/workspace/agent-binding-compiler.d.ts +2 -2
- package/dist/workspace/agent-binding-compiler.js +78 -6
- package/dist/workspace/compile.js +16 -0
- package/dist/workspace/object-loader.js +100 -3
- package/dist/workspace/resource-compilers.js +6 -0
- package/dist/workspace/tool-hydration.js +87 -13
- package/dist/workspace/yaml-object-reader.js +39 -12
- package/package.json +1 -1
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
2
3
|
import { createRequire } from "node:module";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { stat } from "node:fs/promises";
|
|
5
6
|
import { readFile } from "node:fs/promises";
|
|
6
7
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
8
|
import { CompositeBackend, LocalShellBackend, StateBackend, StoreBackend } from "deepagents";
|
|
8
|
-
import { getBindingBackendConfig } from "../runtime/support/compiled-binding.js";
|
|
9
|
+
import { getBindingBackendConfig, getBindingExecutionView, getBindingPrimaryModel } from "../runtime/support/compiled-binding.js";
|
|
10
|
+
import { resolveCompiledEmbeddingModelRef } from "../runtime/support/embedding-models.js";
|
|
9
11
|
import { createRuntimeEnv } from "../runtime/support/runtime-env.js";
|
|
10
12
|
import { isSupportedToolModulePath, loadToolModuleDefinition } from "../tool-modules.js";
|
|
11
13
|
import { createMcpToolResolver, } from "./mcp-tool-support.js";
|
|
@@ -273,6 +275,142 @@ function createWorkspaceProviderResolvers(workspace, factory) {
|
|
|
273
275
|
.map((provider) => factory(provider))
|
|
274
276
|
.filter((resolver) => Boolean(resolver));
|
|
275
277
|
}
|
|
278
|
+
function asObject(value) {
|
|
279
|
+
return typeof value === "object" && value !== null ? value : undefined;
|
|
280
|
+
}
|
|
281
|
+
function asStringRecord(value) {
|
|
282
|
+
const record = asObject(value);
|
|
283
|
+
if (!record) {
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
return Object.fromEntries(Object.entries(record).filter((entry) => typeof entry[1] === "string"));
|
|
287
|
+
}
|
|
288
|
+
function asStringArray(value) {
|
|
289
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
290
|
+
}
|
|
291
|
+
const FUNCTION_TOOL_SUBPROCESS_RUNNER_SOURCE = `
|
|
292
|
+
import { pathToFileURL } from "node:url";
|
|
293
|
+
|
|
294
|
+
const TOOL_DEFINITION_MARKER = "__agent_harness_tool_definition__";
|
|
295
|
+
|
|
296
|
+
function isToolDefinitionObject(value) {
|
|
297
|
+
return typeof value === "object" && value !== null && value[TOOL_DEFINITION_MARKER] === true;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function loadToolDefinition(imported, implementationName) {
|
|
301
|
+
for (const [exportName, value] of Object.entries(imported)) {
|
|
302
|
+
if (exportName === "default" || !isToolDefinitionObject(value)) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const resolvedName = typeof value.name === "string" && value.name.trim() ? value.name.trim() : exportName;
|
|
306
|
+
if (resolvedName === implementationName) {
|
|
307
|
+
return value;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
throw new Error(\`Tool module must export a tool({...}) definition named \${implementationName}.\`);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const [modulePath, implementationName] = process.argv.slice(1);
|
|
314
|
+
const imported = await import(pathToFileURL(modulePath).href);
|
|
315
|
+
const tool = loadToolDefinition(imported, implementationName);
|
|
316
|
+
let stdin = "";
|
|
317
|
+
for await (const chunk of process.stdin) {
|
|
318
|
+
stdin += String(chunk);
|
|
319
|
+
}
|
|
320
|
+
const payload = stdin.trim() ? JSON.parse(stdin) : {};
|
|
321
|
+
const input = tool.schema && typeof tool.schema.parse === "function"
|
|
322
|
+
? tool.schema.parse(payload.input ?? {})
|
|
323
|
+
: (payload.input ?? {});
|
|
324
|
+
const result = await tool.invoke(input, payload.context ?? {});
|
|
325
|
+
process.stdout.write(typeof result === "string" ? result : JSON.stringify(result));
|
|
326
|
+
`;
|
|
327
|
+
function resolveFunctionToolSubprocessConfig(tool, workspaceRoot, isolatedSourcePath) {
|
|
328
|
+
const execution = asObject(tool.config?.execution);
|
|
329
|
+
if (tool.subprocess !== true) {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
const resolvedExecution = execution ?? {};
|
|
333
|
+
const entry = typeof resolvedExecution.entry === "string" && resolvedExecution.entry.trim()
|
|
334
|
+
? path.resolve(workspaceRoot, resolvedExecution.entry)
|
|
335
|
+
: undefined;
|
|
336
|
+
const command = typeof resolvedExecution.command === "string" && resolvedExecution.command.trim()
|
|
337
|
+
? resolvedExecution.command
|
|
338
|
+
: entry
|
|
339
|
+
? process.execPath
|
|
340
|
+
: process.execPath;
|
|
341
|
+
if (!command) {
|
|
342
|
+
throw new Error(`Tool ${tool.id} subprocess execution requires config.execution.command or config.execution.entry`);
|
|
343
|
+
}
|
|
344
|
+
const args = [
|
|
345
|
+
...(entry ? [entry] : ["--input-type=module", "--eval", FUNCTION_TOOL_SUBPROCESS_RUNNER_SOURCE, isolatedSourcePath, tool.implementationName ?? tool.id]),
|
|
346
|
+
...asStringArray(resolvedExecution.args),
|
|
347
|
+
];
|
|
348
|
+
const cwd = typeof resolvedExecution.cwd === "string" && resolvedExecution.cwd.trim()
|
|
349
|
+
? path.resolve(workspaceRoot, resolvedExecution.cwd)
|
|
350
|
+
: workspaceRoot;
|
|
351
|
+
const timeoutMs = Number.isFinite(resolvedExecution.timeoutMs) ? Number(resolvedExecution.timeoutMs) : undefined;
|
|
352
|
+
return {
|
|
353
|
+
command,
|
|
354
|
+
args,
|
|
355
|
+
cwd,
|
|
356
|
+
env: asStringRecord(resolvedExecution.env),
|
|
357
|
+
timeoutMs,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
async function runFunctionToolInSubprocess(config, input, context) {
|
|
361
|
+
return await new Promise((resolve, reject) => {
|
|
362
|
+
const child = spawn(config.command, config.args, {
|
|
363
|
+
cwd: config.cwd,
|
|
364
|
+
env: {
|
|
365
|
+
...process.env,
|
|
366
|
+
...(config.env ?? {}),
|
|
367
|
+
},
|
|
368
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
369
|
+
});
|
|
370
|
+
let stdout = "";
|
|
371
|
+
let stderr = "";
|
|
372
|
+
let settled = false;
|
|
373
|
+
let timeout;
|
|
374
|
+
const finish = (fn) => {
|
|
375
|
+
if (settled) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
settled = true;
|
|
379
|
+
if (timeout) {
|
|
380
|
+
clearTimeout(timeout);
|
|
381
|
+
}
|
|
382
|
+
fn();
|
|
383
|
+
};
|
|
384
|
+
if (config.timeoutMs && config.timeoutMs > 0) {
|
|
385
|
+
timeout = setTimeout(() => {
|
|
386
|
+
child.kill("SIGTERM");
|
|
387
|
+
finish(() => reject(new Error(`Subprocess tool timed out after ${config.timeoutMs}ms`)));
|
|
388
|
+
}, config.timeoutMs);
|
|
389
|
+
}
|
|
390
|
+
child.stdout.setEncoding("utf8");
|
|
391
|
+
child.stderr.setEncoding("utf8");
|
|
392
|
+
child.stdout.on("data", (chunk) => {
|
|
393
|
+
stdout += chunk;
|
|
394
|
+
});
|
|
395
|
+
child.stderr.on("data", (chunk) => {
|
|
396
|
+
stderr += chunk;
|
|
397
|
+
});
|
|
398
|
+
child.on("error", (error) => {
|
|
399
|
+
finish(() => reject(error));
|
|
400
|
+
});
|
|
401
|
+
child.on("close", (code, signal) => {
|
|
402
|
+
finish(() => {
|
|
403
|
+
if (code === 0) {
|
|
404
|
+
resolve(stdout.trim());
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
const message = stderr.trim() || stdout.trim() || `Subprocess tool exited with code ${code ?? "unknown"}${signal ? ` (${signal})` : ""}`;
|
|
408
|
+
reject(new Error(message));
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
child.stdin.end(JSON.stringify({ input, context }));
|
|
412
|
+
});
|
|
413
|
+
}
|
|
276
414
|
async function loadFunctionToolModule(tool) {
|
|
277
415
|
const cacheKey = `${tool.sourcePath}:${tool.implementationName ?? tool.id}`;
|
|
278
416
|
const cached = functionToolModuleCache.get(cacheKey);
|
|
@@ -285,7 +423,7 @@ async function loadFunctionToolModule(tool) {
|
|
|
285
423
|
const imported = await import(pathToFileURL(isolatedSourcePath).href);
|
|
286
424
|
const implementationName = tool.implementationName ?? tool.id;
|
|
287
425
|
const loaded = loadToolModuleDefinition(imported, implementationName);
|
|
288
|
-
return { invoke: loaded.invoke, schema: loaded.schema, description: loaded.description };
|
|
426
|
+
return { invoke: loaded.invoke, schema: loaded.schema, description: loaded.description, isolatedSourcePath, implementationName };
|
|
289
427
|
})();
|
|
290
428
|
functionToolModuleCache.set(cacheKey, loading);
|
|
291
429
|
return loading;
|
|
@@ -294,7 +432,7 @@ function createFunctionToolResolver(workspace) {
|
|
|
294
432
|
const functionTools = new Map(Array.from(workspace.tools.values())
|
|
295
433
|
.filter((tool) => tool.type === "function" && isSupportedToolModulePath(tool.sourcePath))
|
|
296
434
|
.map((tool) => [tool.id, tool]));
|
|
297
|
-
return (toolIds) => toolIds.flatMap((toolId) => {
|
|
435
|
+
return (toolIds, binding) => toolIds.flatMap((toolId) => {
|
|
298
436
|
const tool = functionTools.get(toolId);
|
|
299
437
|
if (!tool) {
|
|
300
438
|
return [];
|
|
@@ -307,12 +445,63 @@ function createFunctionToolResolver(workspace) {
|
|
|
307
445
|
const loaded = await loadFunctionToolModule(tool);
|
|
308
446
|
const parsedInput = loaded.schema.parse(input ?? {});
|
|
309
447
|
const toolPackageRoot = await findPackageRoot(tool.sourcePath);
|
|
310
|
-
|
|
448
|
+
const bindingTool = binding
|
|
449
|
+
? getBindingExecutionView(binding).primaryTools.find((candidate) => candidate.id === tool.id || candidate.name === tool.name)
|
|
450
|
+
: undefined;
|
|
451
|
+
const effectiveEmbeddingModelRef = bindingTool?.embeddingModelRef ?? tool.embeddingModelRef;
|
|
452
|
+
const effectiveSubprocess = bindingTool?.subprocess ?? tool.subprocess;
|
|
453
|
+
const effectiveConfig = bindingTool?.config ?? tool.config;
|
|
454
|
+
const effectiveHitl = bindingTool?.hitl ?? tool.hitl;
|
|
455
|
+
const effectiveRetryable = bindingTool?.retryable ?? tool.retryable;
|
|
456
|
+
const embeddingModel = effectiveEmbeddingModelRef
|
|
457
|
+
? resolveCompiledEmbeddingModelRef(workspace, effectiveEmbeddingModelRef)
|
|
458
|
+
: undefined;
|
|
459
|
+
const model = binding ? getBindingPrimaryModel(binding) : undefined;
|
|
460
|
+
const toolContext = {
|
|
311
461
|
appRoot: workspace.workspaceRoot,
|
|
312
462
|
toolId: tool.id,
|
|
313
463
|
toolPath: tool.sourcePath,
|
|
314
464
|
toolPackageRoot,
|
|
315
|
-
|
|
465
|
+
embeddingModel,
|
|
466
|
+
model,
|
|
467
|
+
runtime: {
|
|
468
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
469
|
+
runRoot: binding?.harnessRuntime.runRoot,
|
|
470
|
+
},
|
|
471
|
+
agent: binding
|
|
472
|
+
? {
|
|
473
|
+
id: binding.agent.id,
|
|
474
|
+
executionMode: binding.agent.executionMode,
|
|
475
|
+
description: binding.agent.description,
|
|
476
|
+
sourcePath: binding.agent.sourcePath,
|
|
477
|
+
}
|
|
478
|
+
: undefined,
|
|
479
|
+
tool: {
|
|
480
|
+
id: tool.id,
|
|
481
|
+
name: tool.name,
|
|
482
|
+
type: tool.type,
|
|
483
|
+
description: tool.description,
|
|
484
|
+
sourcePath: tool.sourcePath,
|
|
485
|
+
packageRoot: toolPackageRoot,
|
|
486
|
+
config: effectiveConfig,
|
|
487
|
+
embeddingModelRef: effectiveEmbeddingModelRef,
|
|
488
|
+
inputSchemaRef: tool.inputSchemaRef,
|
|
489
|
+
retryable: effectiveRetryable,
|
|
490
|
+
hitl: effectiveHitl,
|
|
491
|
+
},
|
|
492
|
+
};
|
|
493
|
+
const subprocessConfig = resolveFunctionToolSubprocessConfig({
|
|
494
|
+
...tool,
|
|
495
|
+
config: effectiveConfig,
|
|
496
|
+
embeddingModelRef: effectiveEmbeddingModelRef,
|
|
497
|
+
hitl: effectiveHitl,
|
|
498
|
+
retryable: effectiveRetryable,
|
|
499
|
+
subprocess: effectiveSubprocess,
|
|
500
|
+
}, workspace.workspaceRoot, loaded.isolatedSourcePath);
|
|
501
|
+
if (subprocessConfig) {
|
|
502
|
+
return runFunctionToolInSubprocess(subprocessConfig, parsedInput, toolContext);
|
|
503
|
+
}
|
|
504
|
+
return loaded.invoke(parsedInput, toolContext);
|
|
316
505
|
},
|
|
317
506
|
},
|
|
318
507
|
];
|
|
@@ -431,7 +620,10 @@ export function createResourceToolResolver(workspace, options = {}) {
|
|
|
431
620
|
];
|
|
432
621
|
const deduped = new Map();
|
|
433
622
|
for (const tool of resolved) {
|
|
434
|
-
|
|
623
|
+
const name = String(tool.name);
|
|
624
|
+
if (!deduped.has(name)) {
|
|
625
|
+
deduped.set(name, tool);
|
|
626
|
+
}
|
|
435
627
|
}
|
|
436
628
|
return Array.from(deduped.values());
|
|
437
629
|
};
|
|
@@ -5,7 +5,9 @@ export declare class RuntimeOperationTimeoutError extends Error {
|
|
|
5
5
|
readonly stage: "stream" | "invoke";
|
|
6
6
|
constructor(operation: string, timeoutMs: number, stage?: "stream" | "invoke");
|
|
7
7
|
}
|
|
8
|
-
export declare function invokeWithProviderRetry<T>(binding: CompiledAgentBinding, operation: () => Promise<T
|
|
8
|
+
export declare function invokeWithProviderRetry<T>(binding: CompiledAgentBinding, operation: () => Promise<T>, options?: {
|
|
9
|
+
onRetry?: (attempt: number, error: unknown) => void | Promise<void>;
|
|
10
|
+
}): Promise<T>;
|
|
9
11
|
export declare function withRuntimeTimeout<T>(producer: () => T | Promise<T>, timeoutMs: number | undefined, operation: string, stage?: "stream" | "invoke"): Promise<T>;
|
|
10
12
|
export declare function iterateWithTimeout<T>(iterable: AsyncIterable<T>, timeoutMs: number | undefined, operation: string, deadlineAt?: number, deadlineTimeoutMs?: number): AsyncGenerator<T>;
|
|
11
13
|
export declare function materializeModelStream(streamFactory: (input: unknown, config?: Record<string, unknown>) => Promise<AsyncIterable<unknown>>, input: unknown, config?: Record<string, unknown>): Promise<{
|
|
@@ -15,7 +15,7 @@ export class RuntimeOperationTimeoutError extends Error {
|
|
|
15
15
|
this.name = "RuntimeOperationTimeoutError";
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
export async function invokeWithProviderRetry(binding, operation) {
|
|
18
|
+
export async function invokeWithProviderRetry(binding, operation, options = {}) {
|
|
19
19
|
const retryPolicy = resolveProviderRetryPolicy(binding);
|
|
20
20
|
let lastError;
|
|
21
21
|
for (let attempt = 1; attempt <= retryPolicy.maxAttempts; attempt += 1) {
|
|
@@ -27,6 +27,7 @@ export async function invokeWithProviderRetry(binding, operation) {
|
|
|
27
27
|
if (attempt >= retryPolicy.maxAttempts || !isRetryableProviderError(binding, error)) {
|
|
28
28
|
throw error;
|
|
29
29
|
}
|
|
30
|
+
await options.onRetry?.(attempt, error);
|
|
30
31
|
if (retryPolicy.backoffMs > 0) {
|
|
31
32
|
await sleep(retryPolicy.backoffMs);
|
|
32
33
|
}
|
|
@@ -37,6 +37,7 @@ export function normalizeToolArgsForSchema(args, schema) {
|
|
|
37
37
|
const aliasesByExpected = {
|
|
38
38
|
city: ["location", "locality", "place"],
|
|
39
39
|
location: ["city", "city_name"],
|
|
40
|
+
query: ["question", "prompt", "text", "request"],
|
|
40
41
|
};
|
|
41
42
|
const aliases = aliasesByExpected[expectedKey] ?? [];
|
|
42
43
|
const aliasKey = aliases.find((candidate) => candidate in args);
|
|
@@ -107,6 +107,9 @@ function wrapToolWithDedupe(resolvedTool, compiledTool) {
|
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
109
|
function shouldConstrainWorkspacePaths(compiledTool) {
|
|
110
|
+
if (compiledTool.type === "mcp") {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
110
113
|
const normalized = `${compiledTool.name} ${compiledTool.description}`.toLowerCase();
|
|
111
114
|
if (/(web[_ .-]?search|fetch[_ .-]?url|https?:\/\/|\burl\b)/.test(normalized)) {
|
|
112
115
|
return false;
|
|
@@ -26,6 +26,11 @@ export declare function buildLangChainCreateParams(input: {
|
|
|
26
26
|
passthroughOverride?: Record<string, unknown>;
|
|
27
27
|
systemPromptOverride?: string;
|
|
28
28
|
}): Record<string, unknown>;
|
|
29
|
+
export declare function resolveLangChainInvocationConfig(binding: CompiledAgentBinding, options: {
|
|
30
|
+
threadId: string;
|
|
31
|
+
runId: string;
|
|
32
|
+
context?: Record<string, unknown>;
|
|
33
|
+
}): Record<string, unknown>;
|
|
29
34
|
export declare function buildDeepAgentCreateParams(input: {
|
|
30
35
|
binding: CompiledAgentBinding;
|
|
31
36
|
resolvedModel: unknown;
|
|
@@ -54,6 +59,7 @@ export declare class AgentRuntimeAdapter {
|
|
|
54
59
|
private createModelFallbackRunnable;
|
|
55
60
|
private applyStrictToolJsonInstruction;
|
|
56
61
|
private resolveModel;
|
|
62
|
+
private invalidateBindingRuntimeCaches;
|
|
57
63
|
private resolveTools;
|
|
58
64
|
private getToolNameMapping;
|
|
59
65
|
private resolveFilesystemBackend;
|
|
@@ -89,6 +89,24 @@ export function buildLangChainCreateParams(input) {
|
|
|
89
89
|
store: input.resolvedStore,
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
|
+
export function resolveLangChainInvocationConfig(binding, options) {
|
|
93
|
+
const langchainPassthrough = typeof binding.harnessRuntime.langchain?.passthrough === "object" && binding.harnessRuntime.langchain?.passthrough
|
|
94
|
+
? binding.harnessRuntime.langchain.passthrough
|
|
95
|
+
: undefined;
|
|
96
|
+
const config = {
|
|
97
|
+
configurable: {
|
|
98
|
+
thread_id: options.threadId,
|
|
99
|
+
run_id: options.runId,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
if (typeof langchainPassthrough?.recursionLimit === "number") {
|
|
103
|
+
config.recursionLimit = langchainPassthrough.recursionLimit;
|
|
104
|
+
}
|
|
105
|
+
if (options.context) {
|
|
106
|
+
config.context = options.context;
|
|
107
|
+
}
|
|
108
|
+
return config;
|
|
109
|
+
}
|
|
92
110
|
export function buildDeepAgentCreateParams(input) {
|
|
93
111
|
const executionKind = getBindingExecutionKind(input.binding);
|
|
94
112
|
if (executionKind !== "deepagent" || !getBindingExecutionParams(input.binding)) {
|
|
@@ -155,7 +173,11 @@ export class AgentRuntimeAdapter {
|
|
|
155
173
|
});
|
|
156
174
|
}
|
|
157
175
|
async invokeWithProviderRetry(binding, operation) {
|
|
158
|
-
return invokeWithProviderRetryHelper(binding, operation
|
|
176
|
+
return invokeWithProviderRetryHelper(binding, operation, {
|
|
177
|
+
onRetry: async () => {
|
|
178
|
+
this.invalidateBindingRuntimeCaches(binding);
|
|
179
|
+
},
|
|
180
|
+
});
|
|
159
181
|
}
|
|
160
182
|
async withTimeout(producer, timeoutMs, operation, stage = operation.includes("stream") ? "stream" : "invoke") {
|
|
161
183
|
return withRuntimeTimeout(producer, timeoutMs, operation, stage);
|
|
@@ -190,6 +212,10 @@ export class AgentRuntimeAdapter {
|
|
|
190
212
|
throw error;
|
|
191
213
|
}
|
|
192
214
|
}
|
|
215
|
+
invalidateBindingRuntimeCaches(binding) {
|
|
216
|
+
this.runnableCache.delete(binding);
|
|
217
|
+
this.modelCache.clear();
|
|
218
|
+
}
|
|
193
219
|
resolveTools(tools, binding) {
|
|
194
220
|
return resolveAdapterTools({
|
|
195
221
|
tools,
|
|
@@ -418,7 +444,11 @@ export class AgentRuntimeAdapter {
|
|
|
418
444
|
const callRuntime = async (activeBinding, activeRequest) => {
|
|
419
445
|
return this.invokeWithProviderRetry(activeBinding, async () => {
|
|
420
446
|
const runnable = await this.create(activeBinding);
|
|
421
|
-
return (await this.withTimeout(() => runnable.invoke(activeRequest,
|
|
447
|
+
return (await this.withTimeout(() => runnable.invoke(activeRequest, resolveLangChainInvocationConfig(activeBinding, {
|
|
448
|
+
threadId,
|
|
449
|
+
runId,
|
|
450
|
+
context: options.context,
|
|
451
|
+
})), resolveBindingTimeout(activeBinding), "agent invoke", "invoke"));
|
|
422
452
|
});
|
|
423
453
|
};
|
|
424
454
|
const callRuntimeWithToolParseRecovery = async (activeRequest) => {
|
package/dist/runtime/harness.js
CHANGED
|
@@ -21,6 +21,7 @@ import { dropPendingRunSlot, enqueuePendingRunSlot } from "./harness/run/run-que
|
|
|
21
21
|
import { getDefaultRuntimeEntryAgentId, resolveSelectedAgentId, routeAgentId } from "./harness/run/routing.js";
|
|
22
22
|
import { resolveStoreFromConfig, } from "./harness/run/resources.js";
|
|
23
23
|
import { createToolMcpServerFromTools, serveToolsOverStdioFromHarness } from "../mcp.js";
|
|
24
|
+
import { closeMcpClientsForWorkspace } from "../resource/mcp-tool-support.js";
|
|
24
25
|
import { getBindingRuntimeExecutionMode, } from "./support/compiled-binding.js";
|
|
25
26
|
import { bindingSupportsRunningReplay, getWorkspaceBinding, resolveWorkspaceAgentTools, } from "./harness/bindings.js";
|
|
26
27
|
import { describeWorkspaceInventory, getAgentInventoryRecord, listAgentSkills as listWorkspaceAgentSkills, } from "./harness/system/inventory.js";
|
|
@@ -584,6 +585,7 @@ export class AgentHarnessRuntime {
|
|
|
584
585
|
await Promise.allSettled(Array.from(this.backgroundTasks));
|
|
585
586
|
await this.threadMemorySync?.close();
|
|
586
587
|
await this.mem0IngestionSync?.close();
|
|
588
|
+
await closeMcpClientsForWorkspace(this.workspace);
|
|
587
589
|
this.initialized = false;
|
|
588
590
|
}
|
|
589
591
|
async stop() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CompiledAgentBinding, CompiledTool, ParsedAgentObject, ParsedModelObject, ParsedToolObject, WorkspaceObject } from "../contracts/types.js";
|
|
1
|
+
import type { CompiledAgentBinding, CompiledTool, ParsedAgentObject, ParsedAgentToolBinding, ParsedModelObject, ParsedToolObject, WorkspaceObject } from "../contracts/types.js";
|
|
2
2
|
export declare function compileAgentSkills(workspaceRoot: string, agent: ParsedAgentObject, parentSkills?: string[]): string[];
|
|
3
|
-
export declare function requireTools(tools: Map<string, ParsedToolObject>,
|
|
3
|
+
export declare function requireTools(tools: Map<string, ParsedToolObject>, bindings: ParsedAgentToolBinding[], ownerId: string): CompiledTool[];
|
|
4
4
|
export declare function compileBinding(workspaceRoot: string, agent: ParsedAgentObject, agents: Map<string, ParsedAgentObject>, referencedSubagentIds: Set<string>, refs: Map<string, WorkspaceObject | ParsedAgentObject>, models: Map<string, ParsedModelObject>, tools: Map<string, ParsedToolObject>): CompiledAgentBinding;
|
|
@@ -74,13 +74,85 @@ function resolveAgentRuntimeName(agent) {
|
|
|
74
74
|
}
|
|
75
75
|
return baseName;
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
function asObject(value) {
|
|
78
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
79
|
+
? value
|
|
80
|
+
: undefined;
|
|
81
|
+
}
|
|
82
|
+
function mergeConfigValue(base, override) {
|
|
83
|
+
if (override === undefined) {
|
|
84
|
+
return base;
|
|
85
|
+
}
|
|
86
|
+
if (Array.isArray(base) && Array.isArray(override)) {
|
|
87
|
+
return override;
|
|
88
|
+
}
|
|
89
|
+
if (typeof base === "object" && base && typeof override === "object" && override && !Array.isArray(base) && !Array.isArray(override)) {
|
|
90
|
+
const merged = { ...base };
|
|
91
|
+
for (const [key, value] of Object.entries(override)) {
|
|
92
|
+
merged[key] = key in merged ? mergeConfigValue(merged[key], value) : value;
|
|
93
|
+
}
|
|
94
|
+
return merged;
|
|
95
|
+
}
|
|
96
|
+
return override;
|
|
97
|
+
}
|
|
98
|
+
function mergeConfigObjects(base, override) {
|
|
99
|
+
const merged = mergeConfigValue(base, override);
|
|
100
|
+
return typeof merged === "object" && merged && !Array.isArray(merged)
|
|
101
|
+
? merged
|
|
102
|
+
: undefined;
|
|
103
|
+
}
|
|
104
|
+
function parseHitlOverride(value) {
|
|
105
|
+
const record = asObject(value);
|
|
106
|
+
if (!record) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
const enabled = record.enabled === true;
|
|
110
|
+
const allow = Array.isArray(record.allow)
|
|
111
|
+
? record.allow.filter((item) => item === "approve" || item === "edit" || item === "reject")
|
|
112
|
+
: undefined;
|
|
113
|
+
return {
|
|
114
|
+
enabled,
|
|
115
|
+
allow: allow && allow.length > 0 ? allow : undefined,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function applyToolBindingOverrides(tool, binding) {
|
|
119
|
+
const overrides = binding.overrides;
|
|
120
|
+
if (!overrides || Object.keys(overrides).length === 0) {
|
|
121
|
+
return tool;
|
|
122
|
+
}
|
|
123
|
+
const overrideConfig = mergeConfigObjects(asObject(overrides.config), asObject(overrides.execution)
|
|
124
|
+
? {
|
|
125
|
+
execution: asObject(overrides.execution),
|
|
126
|
+
}
|
|
127
|
+
: undefined);
|
|
128
|
+
return {
|
|
129
|
+
...tool,
|
|
130
|
+
...(typeof overrides.name === "string" ? { name: overrides.name } : {}),
|
|
131
|
+
...(typeof overrides.description === "string" ? { description: overrides.description } : {}),
|
|
132
|
+
...(typeof overrides.implementationName === "string" ? { implementationName: overrides.implementationName } : {}),
|
|
133
|
+
...(typeof overrides.inputSchemaRef === "string" ? { inputSchemaRef: overrides.inputSchemaRef } : {}),
|
|
134
|
+
...(typeof overrides.embeddingModelRef === "string" ? { embeddingModelRef: overrides.embeddingModelRef } : {}),
|
|
135
|
+
...(typeof overrides.backendOperation === "string" ? { backendOperation: overrides.backendOperation } : {}),
|
|
136
|
+
...(typeof overrides.mcpRef === "string" ? { mcpRef: overrides.mcpRef } : {}),
|
|
137
|
+
...(typeof overrides.subprocess === "boolean" ? { subprocess: overrides.subprocess } : {}),
|
|
138
|
+
...(typeof overrides.retryable === "boolean" ? { retryable: overrides.retryable } : {}),
|
|
139
|
+
...(overrides.hitl !== undefined ? { hitl: parseHitlOverride(overrides.hitl) } : {}),
|
|
140
|
+
...(overrideConfig ? { config: mergeConfigObjects(tool.config, overrideConfig) } : {}),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function getAgentToolBindings(agent) {
|
|
144
|
+
if (agent.toolBindings && agent.toolBindings.length > 0) {
|
|
145
|
+
return agent.toolBindings;
|
|
146
|
+
}
|
|
147
|
+
return agent.toolRefs.map((ref) => ({ ref }));
|
|
148
|
+
}
|
|
149
|
+
export function requireTools(tools, bindings, ownerId) {
|
|
150
|
+
const compiled = bindings.flatMap((binding) => {
|
|
151
|
+
const targets = resolveToolTargets(tools, binding.ref);
|
|
80
152
|
if (targets.length === 0) {
|
|
81
|
-
throw new Error(`Agent ${ownerId} references missing tool ${ref}`);
|
|
153
|
+
throw new Error(`Agent ${ownerId} references missing tool ${binding.ref}`);
|
|
82
154
|
}
|
|
83
|
-
return targets.flatMap((tool) => compileTool(tool, tools));
|
|
155
|
+
return targets.flatMap((tool) => compileTool(applyToolBindingOverrides(tool, binding), tools));
|
|
84
156
|
});
|
|
85
157
|
const deduped = new Map();
|
|
86
158
|
for (const tool of compiled) {
|
|
@@ -138,7 +210,7 @@ function compileSubagents(agent, agents, workspaceRoot, models, tools, compiledA
|
|
|
138
210
|
function compileExecutionCore(agent, models, tools) {
|
|
139
211
|
return {
|
|
140
212
|
model: requireModel(models, agent.modelRef, agent.id),
|
|
141
|
-
tools: requireTools(tools, agent
|
|
213
|
+
tools: requireTools(tools, getAgentToolBindings(agent), agent.id),
|
|
142
214
|
systemPrompt: resolveSystemPrompt(agent),
|
|
143
215
|
responseFormat: resolveResponseFormat(agent),
|
|
144
216
|
contextSchema: resolveContextSchema(agent),
|
|
@@ -42,6 +42,17 @@ function validateWorkspaceResources(embeddings, mcpServers, models, vectorStores
|
|
|
42
42
|
models.forEach((model) => validateModelObject(model, models));
|
|
43
43
|
vectorStores.forEach((vectorStore) => validateVectorStoreObject(vectorStore));
|
|
44
44
|
tools.forEach((tool) => validateToolObject(tool, tools));
|
|
45
|
+
tools.forEach((tool) => {
|
|
46
|
+
if (!tool.embeddingModelRef) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const embeddingModelId = tool.embeddingModelRef.startsWith("embedding-model/")
|
|
50
|
+
? tool.embeddingModelRef.slice("embedding-model/".length)
|
|
51
|
+
: tool.embeddingModelRef;
|
|
52
|
+
if (!embeddings.has(embeddingModelId)) {
|
|
53
|
+
throw new Error(`Tool ${tool.id} references missing embedding model ${tool.embeddingModelRef}`);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
45
56
|
agents.forEach(validateAgent);
|
|
46
57
|
validateTopology(agents);
|
|
47
58
|
tools.forEach((tool) => {
|
|
@@ -91,6 +102,11 @@ export async function loadWorkspace(workspaceRoot, options = {}) {
|
|
|
91
102
|
loaded.refs.set(`agent/${agent.id}`, agent);
|
|
92
103
|
}
|
|
93
104
|
const { embeddings, mcpServers, models, vectorStores, tools } = collectParsedResources(loaded.refs);
|
|
105
|
+
for (const agent of loaded.agents) {
|
|
106
|
+
for (const tool of agent.inlineTools ?? []) {
|
|
107
|
+
tools.set(tool.id, tool);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
94
110
|
await hydrateAgentMcpTools(loaded.agents, mcpServers, tools);
|
|
95
111
|
const toolSourceRefs = collectToolSourceRefs(tools, loaded.agents, options);
|
|
96
112
|
await ensureResourceSources(toolSourceRefs, workspaceRoot);
|