@botbotgo/agent-harness 0.0.80 → 0.0.82
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/api.d.ts +2 -1
- package/dist/api.js +3 -0
- package/dist/contracts/types.d.ts +5 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/persistence/file-store.d.ts +32 -4
- package/dist/persistence/file-store.js +197 -6
- package/dist/persistence/sqlite-store.d.ts +32 -4
- package/dist/persistence/sqlite-store.js +174 -9
- package/dist/persistence/types.d.ts +64 -3
- package/dist/runtime/agent-runtime-adapter.d.ts +5 -0
- package/dist/runtime/agent-runtime-adapter.js +93 -19
- package/dist/runtime/harness.d.ts +9 -1
- package/dist/runtime/harness.js +267 -65
- package/dist/runtime/health-monitor.js +1 -1
- package/dist/runtime/runtime-record-maintenance.js +2 -0
- package/dist/workspace/object-loader.js +133 -7
- package/dist/workspace/support/workspace-ref-utils.d.ts +3 -0
- package/dist/workspace/support/workspace-ref-utils.js +30 -1
- package/package.json +2 -2
|
@@ -4,11 +4,29 @@ import { readdir, readFile } from "node:fs/promises";
|
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
5
|
import { parseAllDocuments } from "yaml";
|
|
6
6
|
import { resolveIsolatedResourceModulePath } from "../resource/isolation.js";
|
|
7
|
-
import { resolveResourcePackageRoot } from "../resource/sources.js";
|
|
7
|
+
import { isExternalSourceLocator, resolveResourcePackageRoot } from "../resource/sources.js";
|
|
8
8
|
import { discoverToolModuleDefinitions, isSupportedToolModulePath } from "../tool-modules.js";
|
|
9
9
|
import { fileExists, listFilesRecursive, readYamlOrJson } from "../utils/fs.js";
|
|
10
10
|
const MODEL_FILENAMES = ["models.yaml", "models.yml"];
|
|
11
11
|
const CONVENTIONAL_OBJECT_DIRECTORIES = ["tools"];
|
|
12
|
+
const MODULE_AGENT_FILENAMES = ["agent.yaml", "agent.yml"];
|
|
13
|
+
const MODULE_TOOL_FILENAMES = ["tool.yaml", "tool.yml"];
|
|
14
|
+
const MODULE_TOOL_ENTRY_FILENAMES = ["index.mjs", "index.js", "index.cjs"];
|
|
15
|
+
function moduleCollectionRoot(root, kind) {
|
|
16
|
+
return path.join(root, "modules", kind);
|
|
17
|
+
}
|
|
18
|
+
function isModuleDefinitionPath(sourcePath, kind) {
|
|
19
|
+
const normalized = sourcePath.split(path.sep).join("/");
|
|
20
|
+
const suffix = kind === "agents" ? "/agent.yaml" : "/tool.yaml";
|
|
21
|
+
const altSuffix = kind === "agents" ? "/agent.yml" : "/tool.yml";
|
|
22
|
+
return normalized.includes(`/modules/${kind}/`) && (normalized.endsWith(suffix) || normalized.endsWith(altSuffix));
|
|
23
|
+
}
|
|
24
|
+
function moduleRootForSourcePath(sourcePath, kind) {
|
|
25
|
+
if (!isModuleDefinitionPath(sourcePath, kind)) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return path.dirname(sourcePath);
|
|
29
|
+
}
|
|
12
30
|
function conventionalConfigRoot(root) {
|
|
13
31
|
if (path.basename(root) === "config" && existsSync(root) && statSync(root).isDirectory()) {
|
|
14
32
|
return root;
|
|
@@ -236,6 +254,36 @@ function cloneConfigValue(value) {
|
|
|
236
254
|
}
|
|
237
255
|
return value;
|
|
238
256
|
}
|
|
257
|
+
function isModuleRelativePathCandidate(value) {
|
|
258
|
+
return !path.isAbsolute(value) && !isExternalSourceLocator(value) && !value.includes("://");
|
|
259
|
+
}
|
|
260
|
+
function resolveModuleRelativePath(value, moduleRoot) {
|
|
261
|
+
if (!moduleRoot || !isModuleRelativePathCandidate(value)) {
|
|
262
|
+
return value;
|
|
263
|
+
}
|
|
264
|
+
return path.resolve(moduleRoot, value);
|
|
265
|
+
}
|
|
266
|
+
function normalizeModulePromptConfig(value, moduleRoot) {
|
|
267
|
+
if (!moduleRoot || typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
268
|
+
return value;
|
|
269
|
+
}
|
|
270
|
+
const typed = { ...value };
|
|
271
|
+
if (typeof typed.path === "string") {
|
|
272
|
+
typed.path = resolveModuleRelativePath(typed.path, moduleRoot);
|
|
273
|
+
}
|
|
274
|
+
return typed;
|
|
275
|
+
}
|
|
276
|
+
function normalizeModuleAgentConfig(config, moduleRoot) {
|
|
277
|
+
if (!moduleRoot) {
|
|
278
|
+
return config;
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
...config,
|
|
282
|
+
...(config.systemPrompt !== undefined
|
|
283
|
+
? { systemPrompt: normalizeModulePromptConfig(config.systemPrompt, moduleRoot) }
|
|
284
|
+
: {}),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
239
287
|
function readPassthroughConfig(item, consumedKeys) {
|
|
240
288
|
const passthrough = Object.fromEntries(Object.entries(item)
|
|
241
289
|
.filter(([key]) => !consumedKeys.includes(key))
|
|
@@ -277,7 +325,10 @@ function readExecutionObjectArray(item, key) {
|
|
|
277
325
|
function readSharedAgentConfig(config) {
|
|
278
326
|
const middleware = readMiddlewareArray(config.middleware);
|
|
279
327
|
return {
|
|
280
|
-
...(typeof config.systemPrompt === "string"
|
|
328
|
+
...((typeof config.systemPrompt === "string" && config.systemPrompt)
|
|
329
|
+
|| (typeof config.systemPrompt === "object" && config.systemPrompt && !Array.isArray(config.systemPrompt))
|
|
330
|
+
? { systemPrompt: cloneConfigValue(config.systemPrompt) }
|
|
331
|
+
: {}),
|
|
281
332
|
...((typeof config.checkpointer === "object" && config.checkpointer) || typeof config.checkpointer === "boolean"
|
|
282
333
|
? { checkpointer: config.checkpointer }
|
|
283
334
|
: {}),
|
|
@@ -349,8 +400,9 @@ function readDeepAgentConfig(item) {
|
|
|
349
400
|
};
|
|
350
401
|
}
|
|
351
402
|
export function parseAgentItem(item, sourcePath) {
|
|
403
|
+
const moduleRoot = moduleRootForSourcePath(sourcePath, "agents");
|
|
352
404
|
const subagentRefs = readExecutionRefArray(item, "subagents");
|
|
353
|
-
const subagentPathRefs = readExecutionPathArray(item, "subagents");
|
|
405
|
+
const subagentPathRefs = readExecutionPathArray(item, "subagents").map((entry) => resolveModuleRelativePath(entry, moduleRoot));
|
|
354
406
|
const executionMode = String(resolveExecutionBackend(item) ?? "deepagent");
|
|
355
407
|
const runtime = readRuntimeConfig(item);
|
|
356
408
|
return {
|
|
@@ -364,12 +416,12 @@ export function parseAgentItem(item, sourcePath) {
|
|
|
364
416
|
runRoot: typeof runtime?.runRoot === "string" ? runtime.runRoot : undefined,
|
|
365
417
|
toolRefs: readExecutionRefArray(item, "tools"),
|
|
366
418
|
mcpServers: readExecutionObjectArray(item, "mcpServers"),
|
|
367
|
-
skillPathRefs: readExecutionPathArray(item, "skills"),
|
|
368
|
-
memorySources: readExecutionPathArray(item, "memory"),
|
|
419
|
+
skillPathRefs: readExecutionPathArray(item, "skills").map((entry) => resolveModuleRelativePath(entry, moduleRoot)),
|
|
420
|
+
memorySources: readExecutionPathArray(item, "memory").map((entry) => resolveModuleRelativePath(entry, moduleRoot)),
|
|
369
421
|
subagentRefs,
|
|
370
422
|
subagentPathRefs,
|
|
371
|
-
langchainAgentConfig: readLangchainAgentConfig(item),
|
|
372
|
-
deepAgentConfig: readDeepAgentConfig(item),
|
|
423
|
+
langchainAgentConfig: normalizeModuleAgentConfig(readLangchainAgentConfig(item), moduleRoot),
|
|
424
|
+
deepAgentConfig: normalizeModuleAgentConfig(readDeepAgentConfig(item), moduleRoot),
|
|
373
425
|
sourcePath,
|
|
374
426
|
};
|
|
375
427
|
}
|
|
@@ -465,6 +517,23 @@ async function loadConfigAgentsForRoot(configRoot, mergedAgents) {
|
|
|
465
517
|
mergeAgentRecord(mergedAgents, item, sourcePath);
|
|
466
518
|
}
|
|
467
519
|
}
|
|
520
|
+
async function loadModuleAgentsForRoot(root, mergedAgents) {
|
|
521
|
+
const modulesRoot = moduleCollectionRoot(root, "agents");
|
|
522
|
+
if (!(await fileExists(modulesRoot))) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const entries = await readdir(modulesRoot, { withFileTypes: true });
|
|
526
|
+
for (const entry of entries.filter((candidate) => candidate.isDirectory()).sort((left, right) => left.name.localeCompare(right.name))) {
|
|
527
|
+
const moduleRoot = path.join(modulesRoot, entry.name);
|
|
528
|
+
for (const { item, sourcePath } of await readNamedYamlItems(moduleRoot, [...MODULE_AGENT_FILENAMES])) {
|
|
529
|
+
const normalizedItem = typeof item.id === "string" && item.id.trim() ? item : { ...item, id: entry.name };
|
|
530
|
+
if (!isAgentKind(normalizedItem.kind)) {
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
mergeAgentRecord(mergedAgents, normalizedItem, sourcePath);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
468
537
|
async function loadConventionalObjectsForRoot(root, mergedObjects) {
|
|
469
538
|
for (const directory of CONVENTIONAL_OBJECT_DIRECTORIES) {
|
|
470
539
|
for (const objectRoot of conventionalDirectoryRoots(root, directory)) {
|
|
@@ -485,6 +554,61 @@ async function loadConventionalObjectsForRoot(root, mergedObjects) {
|
|
|
485
554
|
}
|
|
486
555
|
}
|
|
487
556
|
}
|
|
557
|
+
async function readModuleToolItems(root) {
|
|
558
|
+
const modulesRoot = moduleCollectionRoot(root, "tools");
|
|
559
|
+
if (!(await fileExists(modulesRoot))) {
|
|
560
|
+
return [];
|
|
561
|
+
}
|
|
562
|
+
const entries = await readdir(modulesRoot, { withFileTypes: true });
|
|
563
|
+
const records = [];
|
|
564
|
+
for (const entry of entries.filter((candidate) => candidate.isDirectory()).sort((left, right) => left.name.localeCompare(right.name))) {
|
|
565
|
+
const moduleRoot = path.join(modulesRoot, entry.name);
|
|
566
|
+
for (const { item, sourcePath } of await readNamedYamlItems(moduleRoot, [...MODULE_TOOL_FILENAMES])) {
|
|
567
|
+
const normalizedItem = typeof item.id === "string" && item.id.trim() ? item : { ...item, id: entry.name };
|
|
568
|
+
const workspaceObject = parseWorkspaceObject(normalizedItem, sourcePath);
|
|
569
|
+
if (!workspaceObject || workspaceObject.kind !== "tool") {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
const implementation = asObject(normalizedItem.implementation);
|
|
573
|
+
const explicitPath = typeof implementation?.path === "string" ? path.resolve(moduleRoot, implementation.path) : undefined;
|
|
574
|
+
const discoveredPath = explicitPath ??
|
|
575
|
+
MODULE_TOOL_ENTRY_FILENAMES.map((filename) => path.join(moduleRoot, filename)).find((candidate) => existsSync(candidate));
|
|
576
|
+
const inferredType = typeof normalizedItem.type === "string"
|
|
577
|
+
? normalizedItem.type
|
|
578
|
+
: normalizedItem.refs !== undefined || normalizedItem.bundle !== undefined
|
|
579
|
+
? "bundle"
|
|
580
|
+
: normalizedItem.providerTool !== undefined || normalizedItem.provider !== undefined
|
|
581
|
+
? "provider"
|
|
582
|
+
: normalizedItem.backend !== undefined || normalizedItem.operation !== undefined
|
|
583
|
+
? "backend"
|
|
584
|
+
: normalizedItem.mcp !== undefined
|
|
585
|
+
? "mcp"
|
|
586
|
+
: "function";
|
|
587
|
+
if (inferredType === "function" && !discoveredPath) {
|
|
588
|
+
throw new Error(`Module tool ${workspaceObject.id} must define implementation.path or provide index.mjs|index.js|index.cjs`);
|
|
589
|
+
}
|
|
590
|
+
records.push({
|
|
591
|
+
item: {
|
|
592
|
+
...normalizedItem,
|
|
593
|
+
...(typeof implementation?.export === "string" && !normalizedItem.implementationName
|
|
594
|
+
? { implementationName: implementation.export }
|
|
595
|
+
: {}),
|
|
596
|
+
},
|
|
597
|
+
sourcePath: discoveredPath ?? sourcePath,
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return records;
|
|
602
|
+
}
|
|
603
|
+
async function loadModuleObjectsForRoot(root, mergedObjects) {
|
|
604
|
+
for (const { item, sourcePath } of await readModuleToolItems(root)) {
|
|
605
|
+
const workspaceObject = parseWorkspaceObject(item, sourcePath);
|
|
606
|
+
if (!workspaceObject) {
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
mergeWorkspaceObjectRecord(mergedObjects, workspaceObject, item, sourcePath);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
488
612
|
async function loadConfigObjectsForRoot(root, configRoot, mergedObjects) {
|
|
489
613
|
if (!conventionalConfigRoot(root)) {
|
|
490
614
|
return;
|
|
@@ -645,8 +769,10 @@ export async function loadWorkspaceObjects(workspaceRoot, options = {}) {
|
|
|
645
769
|
const configRoot = conventionalConfigRoot(root) ?? root;
|
|
646
770
|
await loadNamedModelsForRoot(configRoot, mergedObjects);
|
|
647
771
|
await loadConfigAgentsForRoot(configRoot, mergedAgents);
|
|
772
|
+
await loadModuleAgentsForRoot(root, mergedAgents);
|
|
648
773
|
await loadConventionalObjectsForRoot(root, mergedObjects);
|
|
649
774
|
await loadConfigObjectsForRoot(root, configRoot, mergedObjects);
|
|
775
|
+
await loadModuleObjectsForRoot(root, mergedObjects);
|
|
650
776
|
await loadRootObjects(root, mergedObjects);
|
|
651
777
|
}
|
|
652
778
|
const agents = Array.from(mergedAgents.values()).map(({ item, sourcePath }) => parseAgentItem(item, sourcePath));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
function getRoutingObject(refs) {
|
|
3
4
|
const runtimeDefaults = getRuntimeDefaults(refs);
|
|
@@ -63,7 +64,27 @@ export function getConcurrencyConfig(refs) {
|
|
|
63
64
|
concurrency.maxConcurrentRuns > 0
|
|
64
65
|
? Math.floor(concurrency.maxConcurrentRuns)
|
|
65
66
|
: 3;
|
|
66
|
-
|
|
67
|
+
const leaseMs = typeof concurrency.leaseMs === "number" &&
|
|
68
|
+
Number.isFinite(concurrency.leaseMs) &&
|
|
69
|
+
concurrency.leaseMs > 0
|
|
70
|
+
? Math.floor(concurrency.leaseMs)
|
|
71
|
+
: 30_000;
|
|
72
|
+
const heartbeatIntervalMs = typeof concurrency.heartbeatIntervalMs === "number" &&
|
|
73
|
+
Number.isFinite(concurrency.heartbeatIntervalMs) &&
|
|
74
|
+
concurrency.heartbeatIntervalMs > 0
|
|
75
|
+
? Math.floor(concurrency.heartbeatIntervalMs)
|
|
76
|
+
: 5_000;
|
|
77
|
+
const heartbeatTimeoutMs = typeof concurrency.heartbeatTimeoutMs === "number" &&
|
|
78
|
+
Number.isFinite(concurrency.heartbeatTimeoutMs) &&
|
|
79
|
+
concurrency.heartbeatTimeoutMs > 0
|
|
80
|
+
? Math.floor(concurrency.heartbeatTimeoutMs)
|
|
81
|
+
: Math.max(leaseMs, heartbeatIntervalMs * 3);
|
|
82
|
+
return {
|
|
83
|
+
maxConcurrentRuns,
|
|
84
|
+
leaseMs,
|
|
85
|
+
heartbeatIntervalMs,
|
|
86
|
+
heartbeatTimeoutMs,
|
|
87
|
+
};
|
|
67
88
|
}
|
|
68
89
|
export function getResilienceConfig(refs) {
|
|
69
90
|
const runtimeDefaults = getRuntimeDefaults(refs);
|
|
@@ -210,6 +231,14 @@ export function resolvePromptValue(promptConfig) {
|
|
|
210
231
|
if (typeof promptConfig === "string" && promptConfig.trim()) {
|
|
211
232
|
return promptConfig;
|
|
212
233
|
}
|
|
234
|
+
if (typeof promptConfig === "object" && promptConfig !== null && !Array.isArray(promptConfig)) {
|
|
235
|
+
const promptPath = typeof promptConfig.path === "string"
|
|
236
|
+
? promptConfig.path
|
|
237
|
+
: undefined;
|
|
238
|
+
if (promptPath?.trim()) {
|
|
239
|
+
return readFileSync(promptPath, "utf8");
|
|
240
|
+
}
|
|
241
|
+
}
|
|
213
242
|
return undefined;
|
|
214
243
|
}
|
|
215
244
|
export function resolveRefId(ref) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@botbotgo/agent-harness",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.82",
|
|
4
4
|
"description": "Workspace runtime for multi-agent applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@10.9.2",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"scripts": {
|
|
54
54
|
"build": "rm -rf dist tsconfig.tsbuildinfo && tsc -p tsconfig.json && cp -R config dist/",
|
|
55
55
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
56
|
-
"test": "vitest run test/hello-file.test.ts test/public-api.test.ts test/runtime-health.test.ts test/memory-runtime.test.ts test/sqlite-persistence.test.ts test/runtime-record-maintenance.test.ts test/resource-optional-provider.test.ts test/resource-isolation.test.ts test/stock-research-app-load-harness.test.ts test/stock-research-app-run.test.ts test/stock-research-app-config.test.ts test/release-workflow.test.ts test/release-version.test.ts test/gitignore.test.ts test/package-lock.test.ts test/readme.test.ts test/product-boundary-docs.test.ts test/long-term-memory-docs.test.ts test/local-docs-persistence-inventory.test.ts test/docs-site.test.ts test/runtime-adapter-regressions.test.ts test/runtime-capabilities.test.ts test/runtime-recovery.test.ts test/tool-extension-gaps.test.ts test/checkpoint-maintenance.test.ts test/llamaindex-dependency-compat.test.ts test/skill-standard.test.ts test/routing-config.test.ts test/workspace-compat-regressions.test.ts test/upstream-compat-regressions.test.ts test/yaml-format.test.ts test/config-secrets.test.ts test/init-command.test.ts test/coding-agent-guide.test.ts",
|
|
56
|
+
"test": "vitest run test/hello-file.test.ts test/public-api.test.ts test/runtime-health.test.ts test/memory-runtime.test.ts test/sqlite-persistence.test.ts test/runtime-queue-lease.test.ts test/runtime-cancel.test.ts test/runtime-record-maintenance.test.ts test/resource-optional-provider.test.ts test/resource-isolation.test.ts test/stock-research-app-load-harness.test.ts test/stock-research-app-run.test.ts test/stock-research-app-config.test.ts test/release-workflow.test.ts test/release-version.test.ts test/gitignore.test.ts test/package-lock.test.ts test/readme.test.ts test/product-boundary-docs.test.ts test/long-term-memory-docs.test.ts test/local-docs-persistence-inventory.test.ts test/docs-site.test.ts test/runtime-adapter-regressions.test.ts test/runtime-capabilities.test.ts test/runtime-recovery.test.ts test/tool-extension-gaps.test.ts test/checkpoint-maintenance.test.ts test/llamaindex-dependency-compat.test.ts test/skill-standard.test.ts test/routing-config.test.ts test/workspace-compat-regressions.test.ts test/upstream-compat-regressions.test.ts test/yaml-format.test.ts test/config-secrets.test.ts test/init-command.test.ts test/coding-agent-guide.test.ts",
|
|
57
57
|
"test:real-providers": "vitest run test/real-provider-harness.test.ts",
|
|
58
58
|
"release:prepare": "npm version patch --no-git-tag-version && node ./scripts/sync-example-version.mjs",
|
|
59
59
|
"release:pack": "npm pack --dry-run",
|