@botbotgo/agent-harness 0.0.38 → 0.0.40
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 +79 -137
- package/dist/api.d.ts +15 -12
- package/dist/api.js +25 -41
- package/dist/config/embedding-model.yaml +2 -2
- package/dist/config/workspace.yaml +37 -3
- package/dist/contracts/types.d.ts +15 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp.d.ts +8 -3
- package/dist/mcp.js +10 -11
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/resource/resource-impl.d.ts +2 -1
- package/dist/resource/resource-impl.js +16 -11
- package/dist/resource/resource.d.ts +1 -1
- package/dist/resource/resource.js +1 -1
- package/dist/runtime/event-bus.d.ts +5 -4
- package/dist/runtime/event-bus.js +7 -9
- package/dist/runtime/event-sink.d.ts +9 -0
- package/dist/runtime/event-sink.js +35 -0
- package/dist/runtime/harness.d.ts +21 -12
- package/dist/runtime/harness.js +70 -31
- package/dist/runtime/index.d.ts +3 -1
- package/dist/runtime/index.js +2 -1
- package/dist/runtime/support/harness-support.js +11 -4
- package/dist/runtime/thread-memory-sync.d.ts +4 -2
- package/dist/runtime/thread-memory-sync.js +10 -1
- package/dist/workspace/agent-binding-compiler.js +0 -3
- package/dist/workspace/compile.js +16 -2
- package/dist/workspace/support/discovery.js +11 -8
- package/dist/workspace/support/source-collectors.js +1 -1
- package/dist/workspace/support/workspace-ref-utils.d.ts +19 -0
- package/dist/workspace/support/workspace-ref-utils.js +112 -6
- package/package.json +3 -3
package/dist/mcp.js
CHANGED
|
@@ -7,14 +7,13 @@ import { loadToolModuleDefinition } from "./tool-modules.js";
|
|
|
7
7
|
function asResolvedTool(value) {
|
|
8
8
|
return typeof value === "object" && value !== null ? value : null;
|
|
9
9
|
}
|
|
10
|
-
async function buildRawShapeForTool(
|
|
11
|
-
|
|
12
|
-
if (!tool) {
|
|
10
|
+
async function buildRawShapeForTool(sourceTool, resolvedTool) {
|
|
11
|
+
if (!sourceTool) {
|
|
13
12
|
return undefined;
|
|
14
13
|
}
|
|
15
|
-
if (
|
|
16
|
-
const imported = await import(pathToFileURL(
|
|
17
|
-
const definition = loadToolModuleDefinition(imported,
|
|
14
|
+
if (sourceTool.type === "function") {
|
|
15
|
+
const imported = await import(pathToFileURL(sourceTool.sourcePath).href);
|
|
16
|
+
const definition = loadToolModuleDefinition(imported, sourceTool.implementationName ?? sourceTool.id);
|
|
18
17
|
return typeof definition.schema.shape === "object" && definition.schema.shape !== null
|
|
19
18
|
? definition.schema.shape
|
|
20
19
|
: undefined;
|
|
@@ -84,17 +83,17 @@ function jsonSchemaToZod(schema) {
|
|
|
84
83
|
return z.any();
|
|
85
84
|
}
|
|
86
85
|
}
|
|
87
|
-
export async function
|
|
86
|
+
export async function createToolMcpServerFromTools(tools, options) {
|
|
88
87
|
const server = new McpServer({
|
|
89
88
|
name: options.serverInfo?.name ?? `agent-harness-${options.agentId}`,
|
|
90
89
|
version: options.serverInfo?.version ?? AGENT_HARNESS_VERSION,
|
|
91
90
|
});
|
|
92
91
|
const allowedNames = options.includeToolNames ? new Set(options.includeToolNames) : null;
|
|
93
|
-
for (const { compiledTool, resolvedTool } of
|
|
92
|
+
for (const { compiledTool, resolvedTool, sourceTool } of tools) {
|
|
94
93
|
if (allowedNames && !allowedNames.has(compiledTool.name)) {
|
|
95
94
|
continue;
|
|
96
95
|
}
|
|
97
|
-
const rawShape = await buildRawShapeForTool(
|
|
96
|
+
const rawShape = await buildRawShapeForTool(sourceTool, resolvedTool);
|
|
98
97
|
server.tool(compiledTool.name, compiledTool.description, rawShape ?? {}, async (input) => ({
|
|
99
98
|
content: [
|
|
100
99
|
{
|
|
@@ -106,8 +105,8 @@ export async function createToolMcpServerFromHarness(harness, options) {
|
|
|
106
105
|
}
|
|
107
106
|
return server;
|
|
108
107
|
}
|
|
109
|
-
export async function serveToolsOverStdioFromHarness(
|
|
110
|
-
const server = await
|
|
108
|
+
export async function serveToolsOverStdioFromHarness(tools, options) {
|
|
109
|
+
const server = await createToolMcpServerFromTools(tools, options);
|
|
111
110
|
await server.connect(new StdioServerTransport());
|
|
112
111
|
return server;
|
|
113
112
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.39";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.39";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
2
|
import type { RuntimeAdapterOptions, WorkspaceBundle } from "../contracts/types.js";
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function resolveLocalResourceProviderEntry(currentResourceDir: string, resolveInstalledEntry?: () => string | null): string;
|
|
4
4
|
export type ResourceToolInfo = {
|
|
5
5
|
toolPath: string;
|
|
6
6
|
backendOperation: string;
|
|
@@ -46,3 +46,4 @@ export declare const listBuiltinTools: typeof listResourceTools;
|
|
|
46
46
|
export declare const listBuiltinToolsForSource: typeof listResourceToolsForSource;
|
|
47
47
|
export declare const createBuiltinBackendResolver: typeof createResourceBackendResolver;
|
|
48
48
|
export declare const createBuiltinToolResolver: typeof createResourceToolResolver;
|
|
49
|
+
export declare const resolveLocalBuiltinsEntry: typeof resolveLocalResourceProviderEntry;
|
|
@@ -16,7 +16,13 @@ import { resolveIsolatedResourceModulePath } from "./isolation.js";
|
|
|
16
16
|
import { ensureExternalResourceSource, ensureExternalSource, isExternalSourceLocator, parseExternalSourceLocator } from "./sources.js";
|
|
17
17
|
const resourceDir = path.dirname(fileURLToPath(import.meta.url));
|
|
18
18
|
const require = createRequire(import.meta.url);
|
|
19
|
-
function
|
|
19
|
+
function installedResourceProviderEntry() {
|
|
20
|
+
try {
|
|
21
|
+
return require.resolve("@botbotgo/agent-harness-resource");
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Fall through to the legacy package name for backward compatibility only.
|
|
25
|
+
}
|
|
20
26
|
try {
|
|
21
27
|
return require.resolve("@botbotgo/agent-harness-builtin");
|
|
22
28
|
}
|
|
@@ -24,12 +30,10 @@ function installedBuiltinsEntry() {
|
|
|
24
30
|
return null;
|
|
25
31
|
}
|
|
26
32
|
}
|
|
27
|
-
export function
|
|
33
|
+
export function resolveLocalResourceProviderEntry(currentResourceDir, resolveInstalledEntry = installedResourceProviderEntry) {
|
|
28
34
|
const candidates = [
|
|
29
|
-
path.resolve(currentResourceDir, "../../../
|
|
30
|
-
path.resolve(currentResourceDir, "../../../
|
|
31
|
-
path.resolve(currentResourceDir, "../../../builtins/src/index.ts"),
|
|
32
|
-
path.resolve(currentResourceDir, "../../../agent-harness/packages/builtins/src/index.ts"),
|
|
35
|
+
path.resolve(currentResourceDir, "../../../resource-package/dist/src/index.js"),
|
|
36
|
+
path.resolve(currentResourceDir, "../../../resource-package/src/index.ts"),
|
|
33
37
|
resolveInstalledEntry(),
|
|
34
38
|
].filter((candidate) => typeof candidate === "string" && candidate.length > 0);
|
|
35
39
|
for (const candidate of candidates) {
|
|
@@ -37,9 +41,9 @@ export function resolveLocalBuiltinsEntry(currentResourceDir, resolveInstalledEn
|
|
|
37
41
|
return candidate;
|
|
38
42
|
}
|
|
39
43
|
}
|
|
40
|
-
return candidates.at(-1) ?? path.resolve(currentResourceDir, "../../../
|
|
44
|
+
return candidates.at(-1) ?? path.resolve(currentResourceDir, "../../../resource-package/dist/src/index.js");
|
|
41
45
|
}
|
|
42
|
-
const
|
|
46
|
+
const resourceProviderEntry = resolveLocalResourceProviderEntry(resourceDir);
|
|
43
47
|
async function loadLocalResource(entry) {
|
|
44
48
|
if (!existsSync(entry)) {
|
|
45
49
|
return null;
|
|
@@ -47,7 +51,7 @@ async function loadLocalResource(entry) {
|
|
|
47
51
|
const imported = await import(pathToFileURL(entry).href);
|
|
48
52
|
return (imported.default ?? imported);
|
|
49
53
|
}
|
|
50
|
-
const localResource = await loadLocalResource(
|
|
54
|
+
const localResource = await loadLocalResource(resourceProviderEntry);
|
|
51
55
|
function listProviderTools(provider) {
|
|
52
56
|
const rawTools = provider?.listResourceTools?.() ?? provider?.listBuiltinTools?.() ?? [];
|
|
53
57
|
return rawTools.map((tool) => ({
|
|
@@ -509,7 +513,7 @@ export async function listResourceToolsForSource(source, workspaceRoot = process
|
|
|
509
513
|
}
|
|
510
514
|
export function createResourceBackendResolver(workspace) {
|
|
511
515
|
const localResolver = createProviderBackendResolver(localResource, workspace);
|
|
512
|
-
const remoteResolvers = (workspace.resourceSources ??
|
|
516
|
+
const remoteResolvers = (workspace.resourceSources ?? [])
|
|
513
517
|
.map((source) => remoteResourceCache.get(source))
|
|
514
518
|
.filter((provider) => Boolean(provider))
|
|
515
519
|
.map((provider) => createProviderBackendResolver(provider, workspace))
|
|
@@ -525,7 +529,7 @@ export function createResourceToolResolver(workspace, options = {}) {
|
|
|
525
529
|
const functionResolver = createFunctionToolResolver(workspace);
|
|
526
530
|
const mcpResolver = createMcpToolResolver(workspace);
|
|
527
531
|
const localResolver = createProviderToolResolver(localResource, workspace, options);
|
|
528
|
-
const remoteResolvers = (workspace.resourceSources ??
|
|
532
|
+
const remoteResolvers = (workspace.resourceSources ?? [])
|
|
529
533
|
.map((source) => remoteResourceCache.get(source))
|
|
530
534
|
.filter((provider) => Boolean(provider))
|
|
531
535
|
.map((provider) => createProviderToolResolver(provider, workspace, options))
|
|
@@ -551,3 +555,4 @@ export const listBuiltinTools = listResourceTools;
|
|
|
551
555
|
export const listBuiltinToolsForSource = listResourceToolsForSource;
|
|
552
556
|
export const createBuiltinBackendResolver = createResourceBackendResolver;
|
|
553
557
|
export const createBuiltinToolResolver = createResourceToolResolver;
|
|
558
|
+
export const resolveLocalBuiltinsEntry = resolveLocalResourceProviderEntry;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { type ResourceToolInfo, ensureResourceSources, defaultResourceSkillsRoot, defaultResourceConfigRoot, listResourceTools, listResourceToolsForSource, createResourceBackendResolver, createResourceToolResolver,
|
|
1
|
+
export { type ResourceToolInfo, ensureResourceSources, defaultResourceSkillsRoot, defaultResourceConfigRoot, listResourceTools, listResourceToolsForSource, createResourceBackendResolver, createResourceToolResolver, resolveLocalResourceProviderEntry, } from "./resource-impl.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { ensureResourceSources, defaultResourceSkillsRoot, defaultResourceConfigRoot, listResourceTools, listResourceToolsForSource, createResourceBackendResolver, createResourceToolResolver,
|
|
1
|
+
export { ensureResourceSources, defaultResourceSkillsRoot, defaultResourceConfigRoot, listResourceTools, listResourceToolsForSource, createResourceBackendResolver, createResourceToolResolver, resolveLocalResourceProviderEntry, } from "./resource-impl.js";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { HarnessEvent } from "../contracts/types.js";
|
|
2
|
-
export declare class EventBus {
|
|
3
|
-
private readonly
|
|
1
|
+
import type { HarnessEvent, HarnessEventListener, HarnessEventProjection, RuntimeEventSink } from "../contracts/types.js";
|
|
2
|
+
export declare class EventBus implements RuntimeEventSink {
|
|
3
|
+
private readonly sink;
|
|
4
4
|
publish(event: HarnessEvent): void;
|
|
5
|
-
subscribe(listener:
|
|
5
|
+
subscribe(listener: HarnessEventListener): () => void;
|
|
6
|
+
registerProjection(projection: HarnessEventProjection): () => void;
|
|
6
7
|
}
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getEventSubscribers } from "../extensions.js";
|
|
1
|
+
import { createRuntimeEventSink } from "./event-sink.js";
|
|
3
2
|
export class EventBus {
|
|
4
|
-
|
|
3
|
+
sink = createRuntimeEventSink();
|
|
5
4
|
publish(event) {
|
|
6
|
-
this.
|
|
7
|
-
for (const subscriber of getEventSubscribers()) {
|
|
8
|
-
void Promise.resolve(subscriber.onEvent(event));
|
|
9
|
-
}
|
|
5
|
+
this.sink.publish(event);
|
|
10
6
|
}
|
|
11
7
|
subscribe(listener) {
|
|
12
|
-
this.
|
|
13
|
-
|
|
8
|
+
return this.sink.subscribe(listener);
|
|
9
|
+
}
|
|
10
|
+
registerProjection(projection) {
|
|
11
|
+
return this.sink.registerProjection(projection);
|
|
14
12
|
}
|
|
15
13
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { HarnessEvent, HarnessEventListener, HarnessEventProjection, RuntimeEventSink } from "../contracts/types.js";
|
|
2
|
+
export declare class RuntimeEventSinkImpl implements RuntimeEventSink {
|
|
3
|
+
private readonly emitter;
|
|
4
|
+
private readonly projections;
|
|
5
|
+
publish(event: HarnessEvent): void;
|
|
6
|
+
subscribe(listener: HarnessEventListener): () => void;
|
|
7
|
+
registerProjection(projection: HarnessEventProjection): () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function createRuntimeEventSink(): RuntimeEventSinkImpl;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import { getEventSubscribers } from "../extensions.js";
|
|
3
|
+
function dispatchListener(listener, event) {
|
|
4
|
+
void Promise.resolve(listener(event));
|
|
5
|
+
}
|
|
6
|
+
function dispatchProjection(projection, event) {
|
|
7
|
+
if (projection.shouldHandle && !projection.shouldHandle(event)) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
void Promise.resolve(projection.handleEvent(event));
|
|
11
|
+
}
|
|
12
|
+
export class RuntimeEventSinkImpl {
|
|
13
|
+
emitter = new EventEmitter();
|
|
14
|
+
projections = new Set();
|
|
15
|
+
publish(event) {
|
|
16
|
+
this.emitter.emit("event", event);
|
|
17
|
+
for (const projection of this.projections) {
|
|
18
|
+
dispatchProjection(projection, event);
|
|
19
|
+
}
|
|
20
|
+
for (const subscriber of getEventSubscribers()) {
|
|
21
|
+
dispatchListener(subscriber.onEvent, event);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
subscribe(listener) {
|
|
25
|
+
this.emitter.on("event", listener);
|
|
26
|
+
return () => this.emitter.off("event", listener);
|
|
27
|
+
}
|
|
28
|
+
registerProjection(projection) {
|
|
29
|
+
this.projections.add(projection);
|
|
30
|
+
return () => this.projections.delete(projection);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function createRuntimeEventSink() {
|
|
34
|
+
return new RuntimeEventSinkImpl();
|
|
35
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { ApprovalRecord,
|
|
2
|
-
|
|
1
|
+
import type { ApprovalRecord, HarnessEvent, HarnessStreamItem, MessageContent, RunStartOptions, RestartConversationOptions, RuntimeAdapterOptions, ResumeOptions, RunOptions, RunResult, ThreadSummary, ThreadRecord, WorkspaceBundle } from "../contracts/types.js";
|
|
2
|
+
import { type ToolMcpServerOptions } from "../mcp.js";
|
|
3
|
+
export declare class AgentHarnessRuntime {
|
|
3
4
|
private readonly workspace;
|
|
4
5
|
private readonly runtimeAdapterOptions;
|
|
5
6
|
private readonly eventBus;
|
|
@@ -12,8 +13,11 @@ export declare class AgentHarness {
|
|
|
12
13
|
private readonly vectorStores;
|
|
13
14
|
private readonly defaultStore;
|
|
14
15
|
private readonly routingSystemPrompt?;
|
|
16
|
+
private readonly routingRules;
|
|
17
|
+
private readonly routingDefaultAgentId?;
|
|
18
|
+
private readonly modelRoutingEnabled;
|
|
15
19
|
private readonly threadMemorySync;
|
|
16
|
-
private readonly
|
|
20
|
+
private readonly unregisterThreadMemorySync;
|
|
17
21
|
private readonly resolvedRuntimeAdapterOptions;
|
|
18
22
|
private readonly checkpointMaintenance;
|
|
19
23
|
private listHostBindings;
|
|
@@ -27,19 +31,22 @@ export declare class AgentHarness {
|
|
|
27
31
|
constructor(workspace: WorkspaceBundle, runtimeAdapterOptions?: RuntimeAdapterOptions);
|
|
28
32
|
initialize(): Promise<void>;
|
|
29
33
|
subscribe(listener: (event: HarnessEvent) => void): () => void;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
compiledTool: CompiledTool;
|
|
35
|
-
resolvedTool: unknown;
|
|
36
|
-
}>;
|
|
37
|
-
listSessions(filter?: {
|
|
34
|
+
private getBinding;
|
|
35
|
+
private listAgentTools;
|
|
36
|
+
private resolveAgentTools;
|
|
37
|
+
listThreads(filter?: {
|
|
38
38
|
agentId?: string;
|
|
39
39
|
}): Promise<ThreadSummary[]>;
|
|
40
40
|
private getSession;
|
|
41
41
|
getThread(threadId: string): Promise<ThreadRecord | null>;
|
|
42
|
-
|
|
42
|
+
listApprovals(filter?: {
|
|
43
|
+
status?: ApprovalRecord["status"];
|
|
44
|
+
threadId?: string;
|
|
45
|
+
runId?: string;
|
|
46
|
+
}): Promise<ApprovalRecord[]>;
|
|
47
|
+
getApproval(approvalId: string): Promise<ApprovalRecord | null>;
|
|
48
|
+
createToolMcpServer(options: ToolMcpServerOptions): Promise<import("@modelcontextprotocol/sdk/server/mcp.js").McpServer>;
|
|
49
|
+
serveToolsOverStdio(options: ToolMcpServerOptions): Promise<import("@modelcontextprotocol/sdk/server/mcp.js").McpServer>;
|
|
43
50
|
routeAgent(input: MessageContent, options?: {
|
|
44
51
|
threadId?: string;
|
|
45
52
|
}): Promise<string>;
|
|
@@ -65,4 +72,6 @@ export declare class AgentHarness {
|
|
|
65
72
|
restart: Record<string, string>;
|
|
66
73
|
}>;
|
|
67
74
|
close(): Promise<void>;
|
|
75
|
+
stop(): Promise<void>;
|
|
68
76
|
}
|
|
77
|
+
export { AgentHarnessRuntime as AgentHarness };
|
package/dist/runtime/harness.js
CHANGED
|
@@ -5,8 +5,8 @@ import { AGENT_INTERRUPT_SENTINEL_PREFIX, AgentRuntimeAdapter, RuntimeOperationT
|
|
|
5
5
|
import { createResourceBackendResolver, createResourceToolResolver } from "../resource/resource.js";
|
|
6
6
|
import { EventBus } from "./event-bus.js";
|
|
7
7
|
import { PolicyEngine } from "./policy-engine.js";
|
|
8
|
-
import { getRoutingSystemPrompt } from "../workspace/support/workspace-ref-utils.js";
|
|
9
|
-
import { createHarnessEvent, createPendingApproval, heuristicRoute, inferRoutingBindings,
|
|
8
|
+
import { getRoutingDefaultAgentId, getRoutingRules, getRoutingSystemPrompt, isModelRoutingEnabled, matchRoutingRule, } from "../workspace/support/workspace-ref-utils.js";
|
|
9
|
+
import { createHarnessEvent, createPendingApproval, heuristicRoute, inferRoutingBindings, renderRuntimeFailure, renderToolFailure, } from "./support/harness-support.js";
|
|
10
10
|
import { createCheckpointerForConfig, createStoreForConfig } from "./support/runtime-factories.js";
|
|
11
11
|
import { resolveCompiledEmbeddingModel, resolveCompiledEmbeddingModelRef } from "./support/embedding-models.js";
|
|
12
12
|
import { resolveCompiledVectorStore, resolveCompiledVectorStoreRef } from "./support/vector-stores.js";
|
|
@@ -14,7 +14,8 @@ import { ThreadMemorySync } from "./thread-memory-sync.js";
|
|
|
14
14
|
import { FileBackedStore } from "./store.js";
|
|
15
15
|
import { CheckpointMaintenanceLoop, discoverCheckpointMaintenanceTargets, readCheckpointMaintenanceConfig, } from "./checkpoint-maintenance.js";
|
|
16
16
|
import { extractMessageText, normalizeMessageContent } from "../utils/message-content.js";
|
|
17
|
-
|
|
17
|
+
import { createToolMcpServerFromTools, serveToolsOverStdioFromHarness } from "../mcp.js";
|
|
18
|
+
export class AgentHarnessRuntime {
|
|
18
19
|
workspace;
|
|
19
20
|
runtimeAdapterOptions;
|
|
20
21
|
eventBus = new EventBus();
|
|
@@ -27,8 +28,11 @@ export class AgentHarness {
|
|
|
27
28
|
vectorStores = new Map();
|
|
28
29
|
defaultStore;
|
|
29
30
|
routingSystemPrompt;
|
|
31
|
+
routingRules;
|
|
32
|
+
routingDefaultAgentId;
|
|
33
|
+
modelRoutingEnabled;
|
|
30
34
|
threadMemorySync;
|
|
31
|
-
|
|
35
|
+
unregisterThreadMemorySync;
|
|
32
36
|
resolvedRuntimeAdapterOptions;
|
|
33
37
|
checkpointMaintenance;
|
|
34
38
|
listHostBindings() {
|
|
@@ -144,10 +148,11 @@ export class AgentHarness {
|
|
|
144
148
|
};
|
|
145
149
|
this.runtimeAdapter = new AgentRuntimeAdapter(this.resolvedRuntimeAdapterOptions);
|
|
146
150
|
this.routingSystemPrompt = getRoutingSystemPrompt(workspace.refs);
|
|
151
|
+
this.routingRules = getRoutingRules(workspace.refs);
|
|
152
|
+
this.routingDefaultAgentId = getRoutingDefaultAgentId(workspace.refs);
|
|
153
|
+
this.modelRoutingEnabled = isModelRoutingEnabled(workspace.refs);
|
|
147
154
|
this.threadMemorySync = new ThreadMemorySync(this.persistence, this.defaultStore);
|
|
148
|
-
this.
|
|
149
|
-
void this.threadMemorySync.handleEvent(event);
|
|
150
|
-
});
|
|
155
|
+
this.unregisterThreadMemorySync = this.eventBus.registerProjection(this.threadMemorySync);
|
|
151
156
|
const checkpointMaintenanceConfig = readCheckpointMaintenanceConfig(workspace);
|
|
152
157
|
this.checkpointMaintenance = checkpointMaintenanceConfig
|
|
153
158
|
? new CheckpointMaintenanceLoop(discoverCheckpointMaintenanceTargets(workspace), checkpointMaintenanceConfig)
|
|
@@ -160,9 +165,6 @@ export class AgentHarness {
|
|
|
160
165
|
subscribe(listener) {
|
|
161
166
|
return this.eventBus.subscribe(listener);
|
|
162
167
|
}
|
|
163
|
-
getWorkspace() {
|
|
164
|
-
return this.workspace;
|
|
165
|
-
}
|
|
166
168
|
getBinding(agentId) {
|
|
167
169
|
return this.workspace.bindings.get(agentId);
|
|
168
170
|
}
|
|
@@ -186,7 +188,7 @@ export class AgentHarness {
|
|
|
186
188
|
resolvedTool: resolvedTools[index],
|
|
187
189
|
}));
|
|
188
190
|
}
|
|
189
|
-
async
|
|
191
|
+
async listThreads(filter) {
|
|
190
192
|
const threadSummaries = await this.persistence.listSessions();
|
|
191
193
|
if (!filter?.agentId) {
|
|
192
194
|
return threadSummaries;
|
|
@@ -231,30 +233,63 @@ export class AgentHarness {
|
|
|
231
233
|
: undefined,
|
|
232
234
|
};
|
|
233
235
|
}
|
|
234
|
-
async
|
|
235
|
-
|
|
236
|
+
async listApprovals(filter) {
|
|
237
|
+
const approvals = filter?.threadId && filter?.runId
|
|
238
|
+
? await this.persistence.getRunApprovals(filter.threadId, filter.runId)
|
|
239
|
+
: await this.persistence.listApprovals();
|
|
240
|
+
return approvals.filter((approval) => {
|
|
241
|
+
if (filter?.status && approval.status !== filter.status) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
if (filter?.threadId && approval.threadId !== filter.threadId) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
if (filter?.runId && approval.runId !== filter.runId) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
return true;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
async getApproval(approvalId) {
|
|
254
|
+
return this.persistence.getApproval(approvalId);
|
|
255
|
+
}
|
|
256
|
+
async createToolMcpServer(options) {
|
|
257
|
+
const tools = this.resolveAgentTools(options.agentId).map(({ compiledTool, resolvedTool }) => ({
|
|
258
|
+
compiledTool,
|
|
259
|
+
resolvedTool,
|
|
260
|
+
sourceTool: this.workspace.tools.get(compiledTool.id),
|
|
261
|
+
}));
|
|
262
|
+
return createToolMcpServerFromTools(tools, options);
|
|
263
|
+
}
|
|
264
|
+
async serveToolsOverStdio(options) {
|
|
265
|
+
const tools = this.resolveAgentTools(options.agentId).map(({ compiledTool, resolvedTool }) => ({
|
|
266
|
+
compiledTool,
|
|
267
|
+
resolvedTool,
|
|
268
|
+
sourceTool: this.workspace.tools.get(compiledTool.id),
|
|
269
|
+
}));
|
|
270
|
+
return serveToolsOverStdioFromHarness(tools, options);
|
|
236
271
|
}
|
|
237
272
|
async routeAgent(input, options = {}) {
|
|
238
273
|
const routingInput = await this.buildRoutingInput(input, options.threadId);
|
|
239
|
-
const
|
|
240
|
-
const { primaryBinding, secondaryBinding
|
|
241
|
-
const
|
|
242
|
-
if (
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if (secondaryBinding?.deepAgentParams) {
|
|
248
|
-
return secondaryBinding.agent.id;
|
|
249
|
-
}
|
|
250
|
-
if (deterministicIntent === "primary") {
|
|
251
|
-
return primaryBinding?.agent.id ?? heuristicRoute(extractMessageText(input), primaryBinding, secondaryBinding);
|
|
274
|
+
const rawInput = extractMessageText(input);
|
|
275
|
+
const { primaryBinding, secondaryBinding } = inferRoutingBindings(this.workspace);
|
|
276
|
+
const configuredRule = this.routingRules.find((rule) => matchRoutingRule(rawInput, rule, options));
|
|
277
|
+
if (configuredRule) {
|
|
278
|
+
const configuredBinding = this.workspace.bindings.get(configuredRule.agentId);
|
|
279
|
+
if (configuredBinding && configuredBinding.harnessRuntime.hostFacing !== false) {
|
|
280
|
+
return configuredBinding.agent.id;
|
|
281
|
+
}
|
|
252
282
|
}
|
|
253
|
-
if (
|
|
254
|
-
|
|
283
|
+
if (!this.modelRoutingEnabled) {
|
|
284
|
+
const defaultBinding = this.routingDefaultAgentId
|
|
285
|
+
? this.workspace.bindings.get(this.routingDefaultAgentId)
|
|
286
|
+
: primaryBinding;
|
|
287
|
+
if (defaultBinding && defaultBinding.harnessRuntime.hostFacing !== false) {
|
|
288
|
+
return defaultBinding.agent.id;
|
|
289
|
+
}
|
|
255
290
|
}
|
|
256
291
|
if (!primaryBinding || !secondaryBinding) {
|
|
257
|
-
return heuristicRoute(
|
|
292
|
+
return heuristicRoute(rawInput, primaryBinding, secondaryBinding);
|
|
258
293
|
}
|
|
259
294
|
try {
|
|
260
295
|
return await this.runtimeAdapter.route(routingInput, primaryBinding, secondaryBinding, {
|
|
@@ -262,7 +297,7 @@ export class AgentHarness {
|
|
|
262
297
|
});
|
|
263
298
|
}
|
|
264
299
|
catch {
|
|
265
|
-
return heuristicRoute(
|
|
300
|
+
return heuristicRoute(rawInput, primaryBinding, secondaryBinding);
|
|
266
301
|
}
|
|
267
302
|
}
|
|
268
303
|
async emit(threadId, runId, sequence, eventType, payload, source = "runtime") {
|
|
@@ -761,7 +796,11 @@ export class AgentHarness {
|
|
|
761
796
|
}
|
|
762
797
|
async close() {
|
|
763
798
|
await this.checkpointMaintenance?.stop();
|
|
764
|
-
this.
|
|
799
|
+
this.unregisterThreadMemorySync();
|
|
765
800
|
await this.threadMemorySync.close();
|
|
766
801
|
}
|
|
802
|
+
async stop() {
|
|
803
|
+
await this.close();
|
|
804
|
+
}
|
|
767
805
|
}
|
|
806
|
+
export { AgentHarnessRuntime as AgentHarness };
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
export { AgentRuntimeAdapter, AGENT_INTERRUPT_SENTINEL_PREFIX } from "./agent-runtime-adapter.js";
|
|
2
2
|
export { EventBus } from "./event-bus.js";
|
|
3
|
+
export { createRuntimeEventSink, RuntimeEventSinkImpl } from "./event-sink.js";
|
|
3
4
|
export { FileCheckpointSaver } from "./file-checkpoint-saver.js";
|
|
4
5
|
export { CheckpointMaintenanceLoop, discoverCheckpointMaintenanceTargets, maintainSqliteCheckpoints, readCheckpointMaintenanceConfig, } from "./checkpoint-maintenance.js";
|
|
5
6
|
export { ManagedSqliteSaver } from "./sqlite-maintained-checkpoint-saver.js";
|
|
6
|
-
export { AgentHarness } from "./harness.js";
|
|
7
|
+
export { AgentHarnessRuntime, AgentHarness } from "./harness.js";
|
|
7
8
|
export { describeWorkspaceInventory, findAgentBinding, listAgentSkills, listAgentTools, listAvailableAgents, listSpecialists, } from "./inventory.js";
|
|
8
9
|
export * from "./parsing/index.js";
|
|
9
10
|
export { PolicyEngine } from "./policy-engine.js";
|
|
10
11
|
export { createInMemoryStore, FileBackedStore } from "./store.js";
|
|
11
12
|
export * from "./support/index.js";
|
|
12
13
|
export { ThreadMemorySync } from "./thread-memory-sync.js";
|
|
14
|
+
export type { HarnessEventListener, HarnessEventProjection, RuntimeEventSink } from "../contracts/types.js";
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { AgentRuntimeAdapter, AGENT_INTERRUPT_SENTINEL_PREFIX } from "./agent-runtime-adapter.js";
|
|
2
2
|
export { EventBus } from "./event-bus.js";
|
|
3
|
+
export { createRuntimeEventSink, RuntimeEventSinkImpl } from "./event-sink.js";
|
|
3
4
|
export { FileCheckpointSaver } from "./file-checkpoint-saver.js";
|
|
4
5
|
export { CheckpointMaintenanceLoop, discoverCheckpointMaintenanceTargets, maintainSqliteCheckpoints, readCheckpointMaintenanceConfig, } from "./checkpoint-maintenance.js";
|
|
5
6
|
export { ManagedSqliteSaver } from "./sqlite-maintained-checkpoint-saver.js";
|
|
6
|
-
export { AgentHarness } from "./harness.js";
|
|
7
|
+
export { AgentHarnessRuntime, AgentHarness } from "./harness.js";
|
|
7
8
|
export { describeWorkspaceInventory, findAgentBinding, listAgentSkills, listAgentTools, listAvailableAgents, listSpecialists, } from "./inventory.js";
|
|
8
9
|
export * from "./parsing/index.js";
|
|
9
10
|
export { PolicyEngine } from "./policy-engine.js";
|
|
@@ -133,11 +133,18 @@ export function inferRoutingBindings(workspace) {
|
|
|
133
133
|
const directBinding = hostBindings.find((binding) => binding.agent.id === "direct");
|
|
134
134
|
const langchainHost = hostBindings.find((binding) => binding.agent.executionMode === "langchain-v1" && binding.agent.id !== researchBinding?.agent.id);
|
|
135
135
|
const deepagentHosts = hostBindings.filter((binding) => binding.agent.executionMode === "deepagent");
|
|
136
|
+
const defaultDeepagentHost = deepagentHosts.find((binding) => binding.agent.id === "orchestra") ??
|
|
137
|
+
deepagentHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
|
|
138
|
+
deepagentHosts[0];
|
|
136
139
|
const deepagentWithSubagents = deepagentHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
|
|
137
140
|
deepagentHosts[0];
|
|
138
|
-
const primaryBinding = directBinding ?? langchainHost ?? hostBindings[0];
|
|
139
|
-
const secondaryBinding =
|
|
140
|
-
?
|
|
141
|
-
:
|
|
141
|
+
const primaryBinding = defaultDeepagentHost ?? directBinding ?? langchainHost ?? hostBindings[0];
|
|
142
|
+
const secondaryBinding = langchainHost && langchainHost.agent.id !== primaryBinding?.agent.id
|
|
143
|
+
? langchainHost
|
|
144
|
+
: directBinding && directBinding.agent.id !== primaryBinding?.agent.id
|
|
145
|
+
? directBinding
|
|
146
|
+
: deepagentWithSubagents && deepagentWithSubagents.agent.id !== primaryBinding?.agent.id
|
|
147
|
+
? deepagentWithSubagents
|
|
148
|
+
: hostBindings.find((binding) => binding.agent.id !== primaryBinding?.agent.id);
|
|
142
149
|
return { primaryBinding, secondaryBinding, researchBinding, hostBindings };
|
|
143
150
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import type { HarnessEvent } from "../contracts/types.js";
|
|
1
|
+
import type { HarnessEvent, HarnessEventProjection } from "../contracts/types.js";
|
|
2
2
|
import type { FilePersistence } from "../persistence/file-store.js";
|
|
3
|
-
export declare class ThreadMemorySync {
|
|
3
|
+
export declare class ThreadMemorySync implements HarnessEventProjection {
|
|
4
4
|
private readonly persistence;
|
|
5
5
|
private readonly store?;
|
|
6
6
|
private readonly pending;
|
|
7
|
+
readonly name = "thread-memory-sync";
|
|
7
8
|
constructor(persistence: FilePersistence, store?: {
|
|
8
9
|
put: (namespace: string[], key: string, value: Record<string, any>) => Promise<void>;
|
|
9
10
|
} | undefined);
|
|
11
|
+
shouldHandle(event: HarnessEvent): boolean;
|
|
10
12
|
handleEvent(event: HarnessEvent): Promise<void>;
|
|
11
13
|
private syncThread;
|
|
12
14
|
close(): Promise<void>;
|
|
@@ -42,16 +42,25 @@ function renderOpenApprovalsMarkdown(approvals) {
|
|
|
42
42
|
}
|
|
43
43
|
return lines.join("\n");
|
|
44
44
|
}
|
|
45
|
+
const THREAD_MEMORY_EVENT_TYPES = new Set([
|
|
46
|
+
"run.state.changed",
|
|
47
|
+
"approval.resolved",
|
|
48
|
+
"approval.requested",
|
|
49
|
+
]);
|
|
45
50
|
export class ThreadMemorySync {
|
|
46
51
|
persistence;
|
|
47
52
|
store;
|
|
48
53
|
pending = new Set();
|
|
54
|
+
name = "thread-memory-sync";
|
|
49
55
|
constructor(persistence, store) {
|
|
50
56
|
this.persistence = persistence;
|
|
51
57
|
this.store = store;
|
|
52
58
|
}
|
|
59
|
+
shouldHandle(event) {
|
|
60
|
+
return THREAD_MEMORY_EVENT_TYPES.has(event.eventType);
|
|
61
|
+
}
|
|
53
62
|
async handleEvent(event) {
|
|
54
|
-
if (
|
|
63
|
+
if (!this.shouldHandle(event)) {
|
|
55
64
|
return;
|
|
56
65
|
}
|
|
57
66
|
const task = this.syncThread(event.threadId)
|
|
@@ -52,9 +52,6 @@ function resolveAgentRuntimeName(agent) {
|
|
|
52
52
|
return baseName;
|
|
53
53
|
}
|
|
54
54
|
const normalizedSourcePath = agent.sourcePath.split(path.sep).join("/");
|
|
55
|
-
if (normalizedSourcePath.includes("/packages/builtins/")) {
|
|
56
|
-
return `builtin.${baseName}`;
|
|
57
|
-
}
|
|
58
55
|
if (normalizedSourcePath.includes("/packages/framework/")) {
|
|
59
56
|
return `core.${baseName}`;
|
|
60
57
|
}
|
|
@@ -5,7 +5,7 @@ import { validateAgent, validateTopology } from "./validate.js";
|
|
|
5
5
|
import { compileBinding } from "./agent-binding-compiler.js";
|
|
6
6
|
import { discoverSubagents, ensureDiscoverySources } from "./support/discovery.js";
|
|
7
7
|
import { collectAgentDiscoverySourceRefs, collectToolSourceRefs } from "./support/source-collectors.js";
|
|
8
|
-
import { resolveRefId } from "./support/workspace-ref-utils.js";
|
|
8
|
+
import { getRoutingDefaultAgentId, getRoutingRules, resolveRefId, } from "./support/workspace-ref-utils.js";
|
|
9
9
|
import { hydrateAgentMcpTools, hydrateResourceAndExternalTools } from "./tool-hydration.js";
|
|
10
10
|
function collectParsedResources(refs) {
|
|
11
11
|
const embeddings = new Map();
|
|
@@ -70,6 +70,20 @@ function compileBindings(workspaceRoot, refs, agentsList, models, tools) {
|
|
|
70
70
|
}
|
|
71
71
|
return bindings;
|
|
72
72
|
}
|
|
73
|
+
function validateRoutingTargets(refs, agentsList) {
|
|
74
|
+
const hostFacingAgentIds = new Set(agentsList
|
|
75
|
+
.filter((agent) => !agentsList.some((owner) => owner.subagentRefs.some((ref) => resolveRefId(ref) === agent.id)))
|
|
76
|
+
.map((agent) => agent.id));
|
|
77
|
+
const defaultAgentId = getRoutingDefaultAgentId(refs);
|
|
78
|
+
if (defaultAgentId && !hostFacingAgentIds.has(defaultAgentId)) {
|
|
79
|
+
throw new Error(`Runtime routing.defaultAgentId references unknown host-facing agent ${defaultAgentId}`);
|
|
80
|
+
}
|
|
81
|
+
for (const rule of getRoutingRules(refs)) {
|
|
82
|
+
if (!hostFacingAgentIds.has(rule.agentId)) {
|
|
83
|
+
throw new Error(`Runtime routing.rules references unknown host-facing agent ${rule.agentId}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
73
87
|
export async function loadWorkspace(workspaceRoot, options = {}) {
|
|
74
88
|
const loaded = await loadWorkspaceObjects(workspaceRoot, options);
|
|
75
89
|
loaded.agents = await discoverSubagents(loaded.agents, workspaceRoot);
|
|
@@ -84,10 +98,10 @@ export async function loadWorkspace(workspaceRoot, options = {}) {
|
|
|
84
98
|
await ensureResourceSources(toolSourceRefs, workspaceRoot);
|
|
85
99
|
await hydrateResourceAndExternalTools(tools, toolSourceRefs, workspaceRoot);
|
|
86
100
|
validateWorkspaceResources(embeddings, mcpServers, models, vectorStores, tools, loaded.agents);
|
|
101
|
+
validateRoutingTargets(loaded.refs, loaded.agents);
|
|
87
102
|
return {
|
|
88
103
|
workspaceRoot,
|
|
89
104
|
resourceSources: [...toolSourceRefs],
|
|
90
|
-
builtinSources: [...toolSourceRefs],
|
|
91
105
|
refs: loaded.refs,
|
|
92
106
|
embeddings,
|
|
93
107
|
mcpServers,
|