@botbotgo/agent-harness 0.0.24 → 0.0.26
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 +44 -0
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/resource/resource-impl.js +120 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,6 +106,13 @@ Or pass a prebuilt `WorkspaceBundle`:
|
|
|
106
106
|
const harness = await createAgentHarness(workspaceBundle);
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
+
Implementation details:
|
|
110
|
+
|
|
111
|
+
- `createAgentHarness(workspaceRoot)` loads and compiles one `WorkspaceBundle` before the runtime starts
|
|
112
|
+
- the loader reads `config/workspace.yaml`, `config/models.yaml`, `config/agent-context.md`, and every `config/agents/*.yaml` entry file, then validates the final topology
|
|
113
|
+
- local resource refs are hydrated after config load, so `resources/tools/`, `resources/skills/`, and agent-declared MCP servers are resolved before the harness accepts runs
|
|
114
|
+
- after compilation, the harness initializes persistence under the resolved `runRoot`, wires the runtime adapter, starts thread-memory syncing, and starts checkpoint maintenance when configured
|
|
115
|
+
|
|
109
116
|
### Run A Request
|
|
110
117
|
|
|
111
118
|
```ts
|
|
@@ -124,6 +131,13 @@ The result includes:
|
|
|
124
131
|
- `state`
|
|
125
132
|
- `output`
|
|
126
133
|
|
|
134
|
+
Runtime behavior:
|
|
135
|
+
|
|
136
|
+
- every `run(...)` call creates or continues a persisted thread under `runRoot`
|
|
137
|
+
- the harness stores thread metadata, run lifecycle, streamed events, approvals, delegations, and artifacts on disk so `getThread(...)` can reconstruct the session later
|
|
138
|
+
- when a store is configured, the harness also mirrors thread status and open approvals into `/memories/threads/<threadId>/`
|
|
139
|
+
- `stop(...)` should always be called so background persistence and maintenance loops shut down cleanly
|
|
140
|
+
|
|
127
141
|
### Let The Harness Choose The Host Agent
|
|
128
142
|
|
|
129
143
|
Use `agentId: "auto"` when your workspace defines routing:
|
|
@@ -135,6 +149,12 @@ const result = await run(harness, {
|
|
|
135
149
|
});
|
|
136
150
|
```
|
|
137
151
|
|
|
152
|
+
Implementation details:
|
|
153
|
+
|
|
154
|
+
- `agentId: "auto"` asks the runtime adapter to classify between the primary and secondary host bindings discovered from the workspace
|
|
155
|
+
- when the run belongs to an existing thread, the router includes recent conversation turns so follow-up requests can stay on the correct lane
|
|
156
|
+
- if no model-driven routing prompt is configured, the harness falls back to heuristic routing for obvious research or implementation-style requests
|
|
157
|
+
|
|
138
158
|
### Stream Output And Events
|
|
139
159
|
|
|
140
160
|
```ts
|
|
@@ -166,6 +186,13 @@ spec:
|
|
|
166
186
|
- ref: tool/local-toolset
|
|
167
187
|
```
|
|
168
188
|
|
|
189
|
+
Tool implementation details:
|
|
190
|
+
|
|
191
|
+
- tool modules are discovered from `resources/tools/*.js`, `resources/tools/*.mjs`, and `resources/tools/*.cjs`
|
|
192
|
+
- the preferred format is exporting `tool({...})`; the harness also accepts exported functions plus `nameSchema`, `nameSchemaShape`, or generic `schema` metadata
|
|
193
|
+
- when a module exports exactly one tool function, generic `schema` or `schemaShape` metadata can describe that function
|
|
194
|
+
- keep runtime dependencies for local tools in `resources/package.json`, because the resource package is the execution boundary for those modules
|
|
195
|
+
|
|
169
196
|
### Bridge MCP Servers Into Agents
|
|
170
197
|
|
|
171
198
|
Use `mcpServers:` inside agent YAML to bridge MCP servers into the agent's tool list:
|
|
@@ -245,6 +272,12 @@ Use this file for workspace-wide behavior such as:
|
|
|
245
272
|
- routing via `routing.systemPrompt`
|
|
246
273
|
- background checkpoint maintenance via `maintenance.checkpoints.*`
|
|
247
274
|
|
|
275
|
+
Implementation details:
|
|
276
|
+
|
|
277
|
+
- if `runRoot` is omitted, the harness defaults to `<workspace-root>/run-data`
|
|
278
|
+
- the resolved `runRoot` stores thread indexes, per-thread transcripts, per-run events, approvals, delegations, artifacts, and checkpoint references
|
|
279
|
+
- checkpoint maintenance only starts when `maintenance.checkpoints.enabled: true`; for SQLite checkpoints, cleanup policies can sweep old rows and optionally `VACUUM` the database
|
|
280
|
+
|
|
248
281
|
### `config/agent-context.md`
|
|
249
282
|
|
|
250
283
|
Use this file for shared bootstrap context that agents read at construction time.
|
|
@@ -276,8 +309,19 @@ Use `resources/` for executable extensions:
|
|
|
276
309
|
|
|
277
310
|
Each resource package should include its own `package.json`.
|
|
278
311
|
|
|
312
|
+
Implementation details:
|
|
313
|
+
|
|
314
|
+
- keep runtime extension source under `resources/`; keep tests outside the published source tree, for example under the repository `test/` folder
|
|
315
|
+
- `resources/package.json` is the module resolution boundary used when local tools import third-party packages or skill-local scripts
|
|
316
|
+
- SKILL packages stay file-based, so prompts, templates, helper scripts, and metadata can ship together without extra registration steps
|
|
317
|
+
|
|
279
318
|
### Skills And MCP
|
|
280
319
|
|
|
281
320
|
- Use `resources/skills/` for SKILL packages that carry reusable instructions, templates, scripts, and metadata
|
|
282
321
|
- Use `mcpServers:` in `config/agents/*.yaml` when you want the harness to bridge external MCP tools into an agent
|
|
283
322
|
- Use `createToolMcpServer(...)` or `serveToolsOverStdio(...)` when you want the harness itself to act as an MCP server for another client
|
|
323
|
+
|
|
324
|
+
Implementation details:
|
|
325
|
+
|
|
326
|
+
- agent-declared `mcpServers:` entries are hydrated into concrete MCP tool refs during workspace compilation, then validated against the collected MCP server definitions
|
|
327
|
+
- `createToolMcpServer(...)` exposes the selected agent toolset through one MCP server surface; `serveToolsOverStdio(...)` is the stdio transport wrapper around that same server construction
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.25";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.25";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { stat } from "node:fs/promises";
|
|
5
5
|
import { readFile } from "node:fs/promises";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
|
+
import { CompositeBackend, LocalShellBackend, StateBackend, StoreBackend } from "deepagents";
|
|
7
8
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
8
9
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
9
10
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
@@ -62,6 +63,113 @@ function createProviderBackendResolver(provider, workspace) {
|
|
|
62
63
|
return (provider?.createResourceBackendResolver?.(workspace) ??
|
|
63
64
|
provider?.createBuiltinBackendResolver?.(workspace));
|
|
64
65
|
}
|
|
66
|
+
class CompatibleCompositeBackend {
|
|
67
|
+
id;
|
|
68
|
+
execute;
|
|
69
|
+
composite;
|
|
70
|
+
constructor(defaultBackend, routes) {
|
|
71
|
+
this.composite = new CompositeBackend(defaultBackend, routes);
|
|
72
|
+
const sandboxLike = defaultBackend;
|
|
73
|
+
if (typeof sandboxLike.id === "string" && typeof sandboxLike.execute === "function") {
|
|
74
|
+
this.id = sandboxLike.id;
|
|
75
|
+
this.execute = (command) => this.composite.execute(command);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
lsInfo(filePath) {
|
|
79
|
+
return this.composite.lsInfo(filePath);
|
|
80
|
+
}
|
|
81
|
+
read(filePath, offset, limit) {
|
|
82
|
+
return this.composite.read(filePath, offset, limit);
|
|
83
|
+
}
|
|
84
|
+
readRaw(filePath) {
|
|
85
|
+
return this.composite.readRaw(filePath);
|
|
86
|
+
}
|
|
87
|
+
grepRaw(pattern, filePath, glob) {
|
|
88
|
+
return this.composite.grepRaw(pattern, filePath ?? undefined, glob ?? undefined);
|
|
89
|
+
}
|
|
90
|
+
globInfo(pattern, filePath) {
|
|
91
|
+
return this.composite.globInfo(pattern, filePath);
|
|
92
|
+
}
|
|
93
|
+
write(filePath, content) {
|
|
94
|
+
return this.composite.write(filePath, content);
|
|
95
|
+
}
|
|
96
|
+
edit(filePath, oldString, newString, replaceAll) {
|
|
97
|
+
return this.composite.edit(filePath, oldString, newString, replaceAll);
|
|
98
|
+
}
|
|
99
|
+
uploadFiles(files) {
|
|
100
|
+
return this.composite.uploadFiles(files);
|
|
101
|
+
}
|
|
102
|
+
downloadFiles(paths) {
|
|
103
|
+
return this.composite.downloadFiles(paths);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function createInlineBackendResolver(workspace) {
|
|
107
|
+
return (binding) => {
|
|
108
|
+
const backendConfig = binding.deepAgentParams?.backend;
|
|
109
|
+
if (!backendConfig || typeof backendConfig !== "object") {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
const resolveBackendRootDir = (configuredRootDir) => {
|
|
113
|
+
if (typeof configuredRootDir === "string" && configuredRootDir.trim().length > 0) {
|
|
114
|
+
return path.isAbsolute(configuredRootDir)
|
|
115
|
+
? configuredRootDir
|
|
116
|
+
: path.resolve(workspace.workspaceRoot, configuredRootDir);
|
|
117
|
+
}
|
|
118
|
+
return workspace.workspaceRoot;
|
|
119
|
+
};
|
|
120
|
+
const createBackend = (kind, config, runtimeLike) => {
|
|
121
|
+
switch (kind) {
|
|
122
|
+
case "LocalShellBackend": {
|
|
123
|
+
const rootDir = resolveBackendRootDir(config?.rootDir);
|
|
124
|
+
mkdirSync(rootDir, { recursive: true });
|
|
125
|
+
return new LocalShellBackend({
|
|
126
|
+
rootDir,
|
|
127
|
+
virtualMode: config?.virtualMode === true,
|
|
128
|
+
timeout: typeof config?.timeout === "number" ? config.timeout : undefined,
|
|
129
|
+
maxOutputBytes: typeof config?.maxOutputBytes === "number" ? config.maxOutputBytes : undefined,
|
|
130
|
+
env: typeof config?.env === "object" && config.env
|
|
131
|
+
? Object.fromEntries(Object.entries(config.env).filter((entry) => typeof entry[1] === "string"))
|
|
132
|
+
: undefined,
|
|
133
|
+
inheritEnv: config?.inheritEnv !== false,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
case "StateBackend":
|
|
137
|
+
return new StateBackend(runtimeLike);
|
|
138
|
+
case "StoreBackend":
|
|
139
|
+
return new StoreBackend(runtimeLike);
|
|
140
|
+
default:
|
|
141
|
+
throw new Error(`Unsupported DeepAgent backend kind "${kind}". Supported inline kinds: LocalShellBackend, StateBackend, StoreBackend, CompositeBackend.`);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
return (runtimeLike) => {
|
|
145
|
+
const kind = typeof backendConfig.kind === "string" ? backendConfig.kind : "CompositeBackend";
|
|
146
|
+
switch (kind) {
|
|
147
|
+
case "LocalShellBackend":
|
|
148
|
+
return createBackend("LocalShellBackend", backendConfig, runtimeLike);
|
|
149
|
+
case "StateBackend":
|
|
150
|
+
return new StateBackend(runtimeLike);
|
|
151
|
+
case "StoreBackend":
|
|
152
|
+
return new StoreBackend(runtimeLike);
|
|
153
|
+
case "CompositeBackend": {
|
|
154
|
+
const stateConfig = typeof backendConfig.state === "object" && backendConfig.state
|
|
155
|
+
? backendConfig.state
|
|
156
|
+
: { kind: "StateBackend" };
|
|
157
|
+
const defaultBackendKind = typeof stateConfig.kind === "string" ? stateConfig.kind : "StateBackend";
|
|
158
|
+
const routes = typeof backendConfig.routes === "object" && backendConfig.routes
|
|
159
|
+
? backendConfig.routes
|
|
160
|
+
: { "/memories/": { kind: "StoreBackend" } };
|
|
161
|
+
const mappedRoutes = Object.fromEntries(Object.entries(routes).map(([route, routeConfig]) => {
|
|
162
|
+
const routeKind = typeof routeConfig?.kind === "string" ? routeConfig.kind : "StoreBackend";
|
|
163
|
+
return [route, createBackend(routeKind, routeConfig, runtimeLike)];
|
|
164
|
+
}));
|
|
165
|
+
return new CompatibleCompositeBackend(createBackend(defaultBackendKind, stateConfig, runtimeLike), mappedRoutes);
|
|
166
|
+
}
|
|
167
|
+
default:
|
|
168
|
+
throw new Error(`Unsupported DeepAgent backend kind "${kind}". Supported inline kinds: LocalShellBackend, StateBackend, StoreBackend, CompositeBackend.`);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
}
|
|
65
173
|
function requireLocalResource(feature) {
|
|
66
174
|
if (localResource) {
|
|
67
175
|
return localResource;
|
|
@@ -385,7 +493,17 @@ export async function listResourceToolsForSource(source, workspaceRoot = process
|
|
|
385
493
|
}
|
|
386
494
|
export function createResourceBackendResolver(workspace) {
|
|
387
495
|
const localResolver = createProviderBackendResolver(localResource, workspace);
|
|
388
|
-
|
|
496
|
+
const remoteResolvers = (workspace.resourceSources ?? workspace.builtinSources ?? [])
|
|
497
|
+
.map((source) => remoteResourceCache.get(source))
|
|
498
|
+
.filter((provider) => Boolean(provider))
|
|
499
|
+
.map((provider) => createProviderBackendResolver(provider, workspace))
|
|
500
|
+
.filter((resolver) => Boolean(resolver));
|
|
501
|
+
const inlineResolver = createInlineBackendResolver(workspace);
|
|
502
|
+
return (binding) => {
|
|
503
|
+
const providerResolved = localResolver?.(binding) ??
|
|
504
|
+
remoteResolvers.map((resolver) => resolver(binding)).find((resolved) => resolved !== undefined);
|
|
505
|
+
return providerResolved ?? inlineResolver(binding);
|
|
506
|
+
};
|
|
389
507
|
}
|
|
390
508
|
export function createResourceToolResolver(workspace, options = {}) {
|
|
391
509
|
const functionResolver = createFunctionToolResolver(workspace);
|