@botbotgo/agent-harness 0.0.37 → 0.0.39
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 +17 -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/agent-runtime-adapter.js +4 -2
- 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 +81 -31
- package/dist/runtime/index.d.ts +3 -1
- package/dist/runtime/index.js +2 -1
- package/dist/runtime/parsing/stream-event-parsing.d.ts +2 -0
- package/dist/runtime/parsing/stream-event-parsing.js +35 -3
- package/dist/runtime/support/harness-support.d.ts +1 -0
- package/dist/runtime/support/harness-support.js +38 -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 +0 -1
- 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.38";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.38";
|
|
@@ -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";
|
|
@@ -656,6 +656,7 @@ export class AgentRuntimeAdapter {
|
|
|
656
656
|
const events = await this.withTimeout(() => runnable.streamEvents(request, { configurable: { thread_id: threadId }, version: "v2" }), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "agent streamEvents start", "stream");
|
|
657
657
|
const allowVisibleStreamDeltas = Boolean(binding.langchainAgentParams);
|
|
658
658
|
let emittedOutput = "";
|
|
659
|
+
let emittedToolError = false;
|
|
659
660
|
const seenTerminalOutputs = new Set();
|
|
660
661
|
let lastStep = "";
|
|
661
662
|
for await (const event of this.iterateWithTimeout(events, streamIdleTimeoutMs, "agent streamEvents", streamDeadlineAt, invokeTimeoutMs)) {
|
|
@@ -695,7 +696,8 @@ export class AgentRuntimeAdapter {
|
|
|
695
696
|
}
|
|
696
697
|
const toolResult = extractToolResult(event);
|
|
697
698
|
if (toolResult) {
|
|
698
|
-
|
|
699
|
+
emittedToolError = emittedToolError || toolResult.isError === true;
|
|
700
|
+
yield { kind: "tool-result", toolName: toolResult.toolName, output: toolResult.output, isError: toolResult.isError };
|
|
699
701
|
}
|
|
700
702
|
const output = extractTerminalStreamOutput(event);
|
|
701
703
|
if (output) {
|
|
@@ -713,7 +715,7 @@ export class AgentRuntimeAdapter {
|
|
|
713
715
|
}
|
|
714
716
|
}
|
|
715
717
|
}
|
|
716
|
-
if (emittedOutput) {
|
|
718
|
+
if (emittedOutput || emittedToolError) {
|
|
717
719
|
return;
|
|
718
720
|
}
|
|
719
721
|
}
|
|
@@ -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") {
|
|
@@ -429,6 +464,7 @@ export class AgentHarness {
|
|
|
429
464
|
await this.notifyListener(listeners.onToolResult, {
|
|
430
465
|
toolName: item.toolName,
|
|
431
466
|
output: item.output,
|
|
467
|
+
isError: item.isError,
|
|
432
468
|
});
|
|
433
469
|
}
|
|
434
470
|
}
|
|
@@ -543,6 +579,7 @@ export class AgentHarness {
|
|
|
543
579
|
try {
|
|
544
580
|
const priorHistory = await this.loadPriorHistory(threadId, runId);
|
|
545
581
|
let assistantOutput = "";
|
|
582
|
+
const toolErrors = [];
|
|
546
583
|
for await (const chunk of this.runtimeAdapter.stream(binding, options.input, threadId, priorHistory)) {
|
|
547
584
|
if (chunk) {
|
|
548
585
|
const normalizedChunk = typeof chunk === "string"
|
|
@@ -591,6 +628,9 @@ export class AgentHarness {
|
|
|
591
628
|
continue;
|
|
592
629
|
}
|
|
593
630
|
if (normalizedChunk.kind === "tool-result") {
|
|
631
|
+
if (normalizedChunk.isError) {
|
|
632
|
+
toolErrors.push(renderToolFailure(normalizedChunk.toolName, normalizedChunk.output));
|
|
633
|
+
}
|
|
594
634
|
yield {
|
|
595
635
|
type: "tool-result",
|
|
596
636
|
threadId,
|
|
@@ -598,6 +638,7 @@ export class AgentHarness {
|
|
|
598
638
|
agentId: selectedAgentId,
|
|
599
639
|
toolName: normalizedChunk.toolName,
|
|
600
640
|
output: normalizedChunk.output,
|
|
641
|
+
isError: normalizedChunk.isError,
|
|
601
642
|
};
|
|
602
643
|
continue;
|
|
603
644
|
}
|
|
@@ -606,6 +647,11 @@ export class AgentHarness {
|
|
|
606
647
|
yield await this.emitOutputDeltaAndCreateItem(threadId, runId, selectedAgentId, normalizedChunk.content);
|
|
607
648
|
}
|
|
608
649
|
}
|
|
650
|
+
if (!assistantOutput && toolErrors.length > 0) {
|
|
651
|
+
assistantOutput = toolErrors.join("\n\n");
|
|
652
|
+
emitted = true;
|
|
653
|
+
yield await this.emitOutputDeltaAndCreateItem(threadId, runId, selectedAgentId, assistantOutput);
|
|
654
|
+
}
|
|
609
655
|
if (!assistantOutput) {
|
|
610
656
|
const actual = await this.invokeWithHistory(binding, options.input, threadId, runId);
|
|
611
657
|
if (actual.output) {
|
|
@@ -750,7 +796,11 @@ export class AgentHarness {
|
|
|
750
796
|
}
|
|
751
797
|
async close() {
|
|
752
798
|
await this.checkpointMaintenance?.stop();
|
|
753
|
-
this.
|
|
799
|
+
this.unregisterThreadMemorySync();
|
|
754
800
|
await this.threadMemorySync.close();
|
|
755
801
|
}
|
|
802
|
+
async stop() {
|
|
803
|
+
await this.close();
|
|
804
|
+
}
|
|
756
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";
|
|
@@ -14,6 +14,7 @@ export type RuntimeStreamChunk = {
|
|
|
14
14
|
kind: "tool-result";
|
|
15
15
|
toolName: string;
|
|
16
16
|
output: unknown;
|
|
17
|
+
isError?: boolean;
|
|
17
18
|
};
|
|
18
19
|
export declare function extractTerminalStreamOutput(event: unknown): string;
|
|
19
20
|
export declare function extractReasoningStreamOutput(event: unknown): string;
|
|
@@ -23,6 +24,7 @@ export declare function extractAgentStep(event: unknown): string | null;
|
|
|
23
24
|
export declare function extractToolResult(event: unknown): {
|
|
24
25
|
toolName: string;
|
|
25
26
|
output: unknown;
|
|
27
|
+
isError?: boolean;
|
|
26
28
|
} | null;
|
|
27
29
|
export declare function extractInterruptPayload(event: unknown): string | null;
|
|
28
30
|
export declare function normalizeTerminalOutputKey(value: string): string;
|