@botbotgo/agent-harness 0.0.160 → 0.0.162

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 CHANGED
@@ -881,9 +881,13 @@ Primary exports:
881
881
  - `getApproval`
882
882
  - `listArtifacts`
883
883
  - `getArtifact`
884
+ - `listRunEvents`
885
+ - `exportRunPackage`
886
+ - `exportSessionPackage`
884
887
  - `exportEvaluationBundle`
885
888
  - `replayEvaluationBundle`
886
889
  - `createAcpServer`
890
+ - `serveAcpHttp`
887
891
  - `serveAcpStdio`
888
892
  - `createToolMcpServer`
889
893
  - `serveToolsOverStdio`
@@ -901,3 +905,10 @@ Inspection helpers:
901
905
  - `exportFlowGraphToSequenceMermaid(...)` renders the same inspection graph as a Mermaid sequence diagram. By default it emits only user-defined participants and calls, while `view: "debug"` includes runtime participants and lifecycle messages.
902
906
 
903
907
  These helpers are visualization and inspection utilities. They do not introduce a canonical harness-owned execution protocol.
908
+
909
+ ACP transport notes:
910
+
911
+ - `serveAcpStdio(runtime)` exposes newline-delimited JSON-RPC over stdio for local IDE, CLI, or subprocess clients.
912
+ - `serveAcpHttp(runtime)` exposes JSON-RPC over HTTP plus SSE runtime events so remote operator surfaces can connect without importing the runtime in-process.
913
+ - `exportRunPackage(...)` and `exportSessionPackage(...)` package stable runtime records, transcript, approvals, events, and artifacts for operator tooling without reaching into persistence internals.
914
+ - `runtime/default.governance.remoteMcp` can now deny or allow specific MCP servers, raise approval requirements by transport, and stamp transport-based risk tiers into runtime governance bundles.
package/README.zh.md CHANGED
@@ -840,9 +840,13 @@ spec:
840
840
  - `getApproval`
841
841
  - `listArtifacts`
842
842
  - `getArtifact`
843
+ - `listRunEvents`
844
+ - `exportRunPackage`
845
+ - `exportSessionPackage`
843
846
  - `exportEvaluationBundle`
844
847
  - `replayEvaluationBundle`
845
848
  - `createAcpServer`
849
+ - `serveAcpHttp`
846
850
  - `serveAcpStdio`
847
851
  - `createToolMcpServer`
848
852
  - `serveToolsOverStdio`
@@ -860,3 +864,10 @@ Inspection 辅助工具:
860
864
  - `exportFlowGraphToSequenceMermaid(...)` 可把同一份 inspection graph 导出为 Mermaid sequence diagram。默认只输出用户定义的参与者与调用;传 `view: "debug"` 才会包含 runtime participant 与生命周期消息。
861
865
 
862
866
  这些 helper 只用于可视化与检查,不代表新的 harness 官方执行协议。
867
+
868
+ ACP transport 说明:
869
+
870
+ - `serveAcpStdio(runtime)` 提供基于 stdio 的 newline-delimited JSON-RPC,适合本地 IDE、CLI 或子进程客户端。
871
+ - `serveAcpHttp(runtime)` 提供基于 HTTP 的 JSON-RPC 与 SSE runtime events,适合远程 operator surface 或独立控制面接入。
872
+ - `exportRunPackage(...)` 与 `exportSessionPackage(...)` 可把稳定 runtime 记录、transcript、approvals、events 和 artifacts 打包给 operator tooling,而不必直接访问 persistence 内部实现。
873
+ - `runtime/default.governance.remoteMcp` 现在可以按 MCP server 或 transport 做 allow/deny、审批升级,并把 transport 风险等级写进 runtime governance bundles。
package/dist/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ArtifactListing, CancelOptions, InvocationEnvelope, ListMemoriesInput, ListMemoriesResult, MemoryRecord, MemorizeInput, MemorizeResult, MessageContent, RecallInput, RecallResult, RemoveMemoryInput, RequestRecord, RequestSummary, ResumeOptions, RunDecisionOptions, RunResult, RunStartOptions, RuntimeHealthSnapshot, RuntimeAdapterOptions, RuntimeEvaluationExport, RuntimeEvaluationExportInput, RuntimeEvaluationReplayInput, RuntimeEvaluationReplayResult, SessionRecord, SessionSummary, UpdateMemoryInput, WorkspaceLoadOptions } from "./contracts/types.js";
1
+ import type { ArtifactListing, CancelOptions, InvocationEnvelope, ListMemoriesInput, ListMemoriesResult, MemoryRecord, MemorizeInput, MemorizeResult, MessageContent, RecallInput, RecallResult, RemoveMemoryInput, RequestRecord, RequestSummary, ResumeOptions, RunDecisionOptions, RunResult, RunStartOptions, RuntimeHealthSnapshot, RuntimeAdapterOptions, RuntimeEvaluationExport, RuntimeEvaluationExportInput, RuntimeEvaluationReplayInput, RuntimeEvaluationReplayResult, RuntimeRunPackage, RuntimeRunPackageInput, RuntimeSessionPackage, RuntimeSessionPackageInput, SessionRecord, SessionSummary, UpdateMemoryInput, WorkspaceLoadOptions } from "./contracts/types.js";
2
2
  import { AgentHarnessRuntime } from "./runtime/harness.js";
3
3
  import type { InventoryAgentRecord, InventorySkillRecord } from "./runtime/harness/system/inventory.js";
4
4
  import type { RequirementAssessmentOptions } from "./runtime/harness/system/skill-requirements.js";
@@ -8,7 +8,8 @@ export type { AcpApproval, AcpArtifact, AcpEventNotification, AcpJsonRpcError, A
8
8
  export { AgentHarnessRuntime } from "./runtime/harness.js";
9
9
  export { buildFlowGraph, exportFlowGraphToMermaid, exportFlowGraphToSequenceMermaid } from "./flow/index.js";
10
10
  export { createUpstreamTimelineReducer } from "./upstream-events.js";
11
- export type { ListMemoriesInput, ListMemoriesResult, MemoryDecision, MemoryKind, MemoryRecord, MemoryScope, MemorizeInput, MemorizeResult, RecallInput, RecallResult, RemoveMemoryInput, RuntimeEvaluationExport, RuntimeEvaluationExportInput, RuntimeEvaluationReplayInput, RuntimeEvaluationReplayResult, UpdateMemoryInput, } from "./contracts/types.js";
11
+ export type { ListMemoriesInput, ListMemoriesResult, MemoryDecision, MemoryKind, MemoryRecord, MemoryScope, MemorizeInput, MemorizeResult, RecallInput, RecallResult, RemoveMemoryInput, RuntimeEvaluationExport, RuntimeEvaluationExportInput, RuntimeEvaluationReplayInput, RuntimeEvaluationReplayResult, RuntimeRunPackage, RuntimeRunPackageInput, RuntimeSessionPackage, RuntimeSessionPackageInput, UpdateMemoryInput, } from "./contracts/types.js";
12
+ export type { AcpHttpServer, AcpHttpServerOptions } from "./protocol/acp/http.js";
12
13
  export type { AcpStdioServer, AcpStdioServerOptions } from "./protocol/acp/stdio.js";
13
14
  type PublicApprovalRecord = {
14
15
  approvalId: string;
@@ -85,10 +86,17 @@ export declare function getArtifact(runtime: AgentHarnessRuntime, input: {
85
86
  requestId: string;
86
87
  artifactPath: string;
87
88
  }): Promise<unknown>;
89
+ export declare function listRunEvents(runtime: AgentHarnessRuntime, input: {
90
+ sessionId: string;
91
+ requestId: string;
92
+ }): Promise<import("./contracts/runtime.js").HarnessEvent[]>;
88
93
  export declare function getHealth(runtime: AgentHarnessRuntime): Promise<RuntimeHealthSnapshot>;
94
+ export declare function exportRunPackage(runtime: AgentHarnessRuntime, input: RuntimeRunPackageInput): Promise<RuntimeRunPackage>;
95
+ export declare function exportSessionPackage(runtime: AgentHarnessRuntime, input: RuntimeSessionPackageInput): Promise<RuntimeSessionPackage>;
89
96
  export declare function exportEvaluationBundle(runtime: AgentHarnessRuntime, input: RuntimeEvaluationExportInput): Promise<RuntimeEvaluationExport>;
90
97
  export declare function replayEvaluationBundle(runtime: AgentHarnessRuntime, input: RuntimeEvaluationReplayInput): Promise<RuntimeEvaluationReplayResult>;
91
98
  export declare function serveAcpStdio(runtime: AgentHarnessRuntime, options?: import("./protocol/acp/stdio.js").AcpStdioServerOptions): import("./protocol/acp/stdio.js").AcpStdioServer;
99
+ export declare function serveAcpHttp(runtime: AgentHarnessRuntime, options?: import("./protocol/acp/http.js").AcpHttpServerOptions): Promise<import("./protocol/acp/http.js").AcpHttpServer>;
92
100
  export declare function listAgentSkills(runtime: AgentHarnessRuntime, agentId: string, options?: RequirementAssessmentOptions): InventorySkillRecord[];
93
101
  export declare function getAgent(runtime: AgentHarnessRuntime, agentId: string, options?: RequirementAssessmentOptions): InventoryAgentRecord | null;
94
102
  export declare function describeInventory(runtime: AgentHarnessRuntime, options?: RequirementAssessmentOptions): {
package/dist/api.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { AgentHarnessRuntime } from "./runtime/harness.js";
2
+ import { serveAcpOverHttp } from "./protocol/acp/http.js";
2
3
  import { serveAcpOverStdio } from "./protocol/acp/stdio.js";
3
4
  import { normalizeMessageContent } from "./utils/message-content.js";
4
5
  import { loadWorkspace } from "./workspace/compile.js";
@@ -187,9 +188,18 @@ export async function listArtifacts(runtime, input) {
187
188
  export async function getArtifact(runtime, input) {
188
189
  return runtime.readArtifact(input.sessionId, input.requestId, input.artifactPath);
189
190
  }
191
+ export async function listRunEvents(runtime, input) {
192
+ return runtime.listRunEvents(input.sessionId, input.requestId);
193
+ }
190
194
  export async function getHealth(runtime) {
191
195
  return runtime.getHealth();
192
196
  }
197
+ export async function exportRunPackage(runtime, input) {
198
+ return runtime.exportRunPackage(input);
199
+ }
200
+ export async function exportSessionPackage(runtime, input) {
201
+ return runtime.exportSessionPackage(input);
202
+ }
193
203
  export async function exportEvaluationBundle(runtime, input) {
194
204
  return runtime.exportEvaluationBundle(input);
195
205
  }
@@ -199,6 +209,9 @@ export async function replayEvaluationBundle(runtime, input) {
199
209
  export function serveAcpStdio(runtime, options) {
200
210
  return serveAcpOverStdio(runtime, options);
201
211
  }
212
+ export async function serveAcpHttp(runtime, options) {
213
+ return serveAcpOverHttp(runtime, options);
214
+ }
202
215
  export function listAgentSkills(runtime, agentId, options) {
203
216
  return runtime.listAgentSkills(agentId, options);
204
217
  }
package/dist/cli.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createAgentHarness } from "./api.js";
3
+ import { serveAcpOverHttp } from "./protocol/acp/http.js";
3
4
  import { serveAcpOverStdio } from "./protocol/acp/stdio.js";
4
5
  type CliIo = {
5
6
  cwd?: string;
@@ -8,6 +9,7 @@ type CliIo = {
8
9
  };
9
10
  type CliDeps = {
10
11
  createAgentHarness?: typeof createAgentHarness;
12
+ serveAcpOverHttp?: typeof serveAcpOverHttp;
11
13
  serveAcpOverStdio?: typeof serveAcpOverStdio;
12
14
  };
13
15
  export declare function runCli(argv: string[], io?: CliIo, deps?: CliDeps): Promise<number>;
package/dist/cli.js CHANGED
@@ -3,11 +3,12 @@ import path from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import { createAgentHarness } from "./api.js";
5
5
  import { initProject } from "./init-project.js";
6
+ import { serveAcpOverHttp } from "./protocol/acp/http.js";
6
7
  import { serveAcpOverStdio } from "./protocol/acp/stdio.js";
7
8
  function renderUsage() {
8
9
  return `Usage:
9
10
  agent-harness init <project-name> [--template deep-research|single-agent] [--provider <provider>] [--model <model>] [--with-web-search|--no-web-search]
10
- agent-harness acp serve [--workspace <path>] [--transport stdio]
11
+ agent-harness acp serve [--workspace <path>] [--transport stdio|http] [--host <hostname>] [--port <port>]
11
12
  `;
12
13
  }
13
14
  function isTemplate(value) {
@@ -52,6 +53,8 @@ function parseInitOptions(args) {
52
53
  function parseAcpServeOptions(args) {
53
54
  let workspaceRoot;
54
55
  let transport = "stdio";
56
+ let hostname;
57
+ let port;
55
58
  for (let index = 0; index < args.length; index += 1) {
56
59
  const arg = args[index];
57
60
  if (arg === "--workspace") {
@@ -66,18 +69,40 @@ function parseAcpServeOptions(args) {
66
69
  if (arg === "--transport") {
67
70
  const value = args[index + 1];
68
71
  if (!value) {
69
- return { transport, error: "Missing value for --transport" };
72
+ return { transport, hostname, port, error: "Missing value for --transport" };
70
73
  }
71
- if (value !== "stdio") {
72
- return { transport, error: `Unsupported ACP transport: ${value}` };
74
+ if (value !== "stdio" && value !== "http") {
75
+ return { transport, hostname, port, error: `Unsupported ACP transport: ${value}` };
73
76
  }
74
- transport = "stdio";
77
+ transport = value;
75
78
  index += 1;
76
79
  continue;
77
80
  }
78
- return { transport, error: `Unknown option: ${arg}` };
81
+ if (arg === "--host") {
82
+ const value = args[index + 1];
83
+ if (!value) {
84
+ return { transport, hostname, port, error: "Missing value for --host" };
85
+ }
86
+ hostname = value;
87
+ index += 1;
88
+ continue;
89
+ }
90
+ if (arg === "--port") {
91
+ const value = args[index + 1];
92
+ if (!value) {
93
+ return { transport, hostname, port, error: "Missing value for --port" };
94
+ }
95
+ const parsedPort = Number.parseInt(value, 10);
96
+ if (!Number.isFinite(parsedPort) || parsedPort < 0) {
97
+ return { transport, hostname, port, error: `Invalid ACP port: ${value}` };
98
+ }
99
+ port = parsedPort;
100
+ index += 1;
101
+ continue;
102
+ }
103
+ return { transport, hostname, port, error: `Unknown option: ${arg}` };
79
104
  }
80
- return { workspaceRoot, transport };
105
+ return { workspaceRoot, transport, hostname, port };
81
106
  }
82
107
  export async function runCli(argv, io = {}, deps = {}) {
83
108
  const cwd = io.cwd ?? process.cwd();
@@ -85,6 +110,7 @@ export async function runCli(argv, io = {}, deps = {}) {
85
110
  const stderr = io.stderr ?? ((message) => process.stderr.write(message));
86
111
  const [command, projectName, ...rest] = argv;
87
112
  const createHarness = deps.createAgentHarness ?? createAgentHarness;
113
+ const serveAcpHttp = deps.serveAcpOverHttp ?? serveAcpOverHttp;
88
114
  const serveAcp = deps.serveAcpOverStdio ?? serveAcpOverStdio;
89
115
  if (command === "init") {
90
116
  if (!projectName?.trim()) {
@@ -130,9 +156,20 @@ export async function runCli(argv, io = {}, deps = {}) {
130
156
  }
131
157
  try {
132
158
  const runtime = await createHarness(path.resolve(cwd, parsed.workspaceRoot ?? "."));
133
- stderr(`Serving ACP over ${parsed.transport} from ${path.resolve(cwd, parsed.workspaceRoot ?? ".")}\n`);
134
- const server = serveAcp(runtime);
135
- await server.completed;
159
+ const workspacePath = path.resolve(cwd, parsed.workspaceRoot ?? ".");
160
+ if (parsed.transport === "http") {
161
+ const server = await serveAcpHttp(runtime, {
162
+ hostname: parsed.hostname,
163
+ port: parsed.port,
164
+ });
165
+ stderr(`Serving ACP over http from ${workspacePath} at ${server.rpcUrl} (events ${server.eventsUrl})\n`);
166
+ await server.completed;
167
+ }
168
+ else {
169
+ stderr(`Serving ACP over stdio from ${workspacePath}\n`);
170
+ const server = serveAcp(runtime);
171
+ await server.completed;
172
+ }
136
173
  await runtime.stop();
137
174
  return 0;
138
175
  }
@@ -102,6 +102,8 @@ export type RuntimeGovernanceToolPolicy = {
102
102
  toolId: string;
103
103
  toolType: string;
104
104
  category: "local" | "backend" | "mcp" | "provider-native";
105
+ mcpServerRef?: string;
106
+ mcpTransport?: string;
105
107
  risk: RuntimeGovernanceRiskLevel;
106
108
  requiresApproval: boolean;
107
109
  approvalPolicy: "explicit-hitl" | "runtime-default" | "none";
@@ -626,6 +628,36 @@ export type RuntimeEvaluationReplayResult = {
626
628
  expectedOutputMatched?: boolean;
627
629
  };
628
630
  };
631
+ export type RuntimeRunPackageInput = {
632
+ sessionId: string;
633
+ requestId: string;
634
+ includeArtifacts?: boolean;
635
+ includeArtifactContents?: boolean;
636
+ includeRuntimeHealth?: boolean;
637
+ };
638
+ export type RuntimeRunPackage = {
639
+ session: SessionRecord | null;
640
+ request: RequestRecord | null;
641
+ approvals: ApprovalRecord[];
642
+ transcript: TranscriptMessage[];
643
+ events: HarnessEvent[];
644
+ artifacts: RuntimeEvaluationArtifact[];
645
+ runtimeHealth?: RuntimeHealthSnapshot;
646
+ };
647
+ export type RuntimeSessionPackageInput = {
648
+ sessionId: string;
649
+ includeArtifacts?: boolean;
650
+ includeArtifactContents?: boolean;
651
+ includeRuntimeHealth?: boolean;
652
+ };
653
+ export type RuntimeSessionPackage = {
654
+ session: SessionRecord | null;
655
+ requests: RequestRecord[];
656
+ approvals: ApprovalRecord[];
657
+ transcript: TranscriptMessage[];
658
+ runs: RuntimeRunPackage[];
659
+ runtimeHealth?: RuntimeHealthSnapshot;
660
+ };
629
661
  export type RuntimeInventoryContext = {
630
662
  workspace: WorkspaceBundle;
631
663
  };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- export { AgentHarnessAcpServer, AgentHarnessRuntime, buildFlowGraph, cancelRun, createAgentHarness, createAcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, replayEvaluationBundle, getArtifact, getAgent, getApproval, getRequest, getHealth, listMemories, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listSessions, memorize, normalizeUserChatInput, recall, removeMemory, resolveApproval, run, serveAcpStdio, serveToolsOverStdio, subscribe, stop, updateMemory, exportFlowGraphToMermaid, exportFlowGraphToSequenceMermaid, } from "./api.js";
1
+ export { AgentHarnessAcpServer, AgentHarnessRuntime, buildFlowGraph, cancelRun, createAgentHarness, createAcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, exportRunPackage, exportSessionPackage, replayEvaluationBundle, getArtifact, getAgent, getApproval, getRequest, getHealth, listMemories, listRunEvents, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listSessions, memorize, normalizeUserChatInput, recall, removeMemory, resolveApproval, run, serveAcpHttp, serveAcpStdio, serveToolsOverStdio, subscribe, stop, updateMemory, exportFlowGraphToMermaid, exportFlowGraphToSequenceMermaid, } from "./api.js";
2
2
  export type { AcpApproval, AcpArtifact, AcpEventNotification, AcpJsonRpcError, AcpJsonRpcRequest, AcpJsonRpcResponse, AcpJsonRpcSuccess, AcpRequestRecord, AcpRunRequestParams, AcpServerCapabilities, AcpSessionRecord, } from "./acp.js";
3
- export type { ListMemoriesInput, ListMemoriesResult, MemoryDecision, MemoryKind, MemoryRecord, MemoryScope, MemorizeInput, MemorizeResult, NormalizeUserChatInputOptions, RecallInput, RecallResult, RemoveMemoryInput, RuntimeEvaluationExport, RuntimeEvaluationExportInput, RuntimeEvaluationReplayInput, RuntimeEvaluationReplayResult, UpdateMemoryInput, UserChatInput, UserChatMessage, } from "./api.js";
3
+ export type { ListMemoriesInput, ListMemoriesResult, MemoryDecision, MemoryKind, MemoryRecord, MemoryScope, MemorizeInput, MemorizeResult, NormalizeUserChatInputOptions, RecallInput, RecallResult, RemoveMemoryInput, RuntimeEvaluationExport, RuntimeEvaluationExportInput, RuntimeEvaluationReplayInput, RuntimeEvaluationReplayResult, RuntimeRunPackage, RuntimeRunPackageInput, RuntimeSessionPackage, RuntimeSessionPackageInput, UpdateMemoryInput, UserChatInput, UserChatMessage, } from "./api.js";
4
4
  export type { BuildFlowGraphInput, FlowEdge, FlowEdgeKind, FlowGraph, FlowGraphMermaidOptions, FlowGraphSequenceMermaidOptions, FlowGroup, FlowGroupKind, FlowNode, FlowNodeKind, FlowNodeLayer, FlowNodeStatus, } from "./flow/index.js";
5
- export type { AcpStdioServer, AcpStdioServerOptions } from "./api.js";
5
+ export type { AcpHttpServer, AcpHttpServerOptions, AcpStdioServer, AcpStdioServerOptions } from "./api.js";
6
6
  export type { ToolMcpServerOptions } from "./mcp.js";
7
7
  export { tool } from "./tools.js";
8
8
  export type { UpstreamTimelineProjection, UpstreamTimelineReducer } from "./upstream-events.js";
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { AgentHarnessAcpServer, AgentHarnessRuntime, buildFlowGraph, cancelRun, createAgentHarness, createAcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, replayEvaluationBundle, getArtifact, getAgent, getApproval, getRequest, getHealth, listMemories, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listSessions, memorize, normalizeUserChatInput, recall, removeMemory, resolveApproval, run, serveAcpStdio, serveToolsOverStdio, subscribe, stop, updateMemory, exportFlowGraphToMermaid, exportFlowGraphToSequenceMermaid, } from "./api.js";
1
+ export { AgentHarnessAcpServer, AgentHarnessRuntime, buildFlowGraph, cancelRun, createAgentHarness, createAcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, exportRunPackage, exportSessionPackage, replayEvaluationBundle, getArtifact, getAgent, getApproval, getRequest, getHealth, listMemories, listRunEvents, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listSessions, memorize, normalizeUserChatInput, recall, removeMemory, resolveApproval, run, serveAcpHttp, serveAcpStdio, serveToolsOverStdio, subscribe, stop, updateMemory, exportFlowGraphToMermaid, exportFlowGraphToSequenceMermaid, } from "./api.js";
2
2
  export { tool } from "./tools.js";
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.159";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.161";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.159";
1
+ export const AGENT_HARNESS_VERSION = "0.0.161";
@@ -0,0 +1,20 @@
1
+ import { type Server } from "node:http";
2
+ import type { AgentHarnessRuntime } from "../../runtime/harness.js";
3
+ export type AcpHttpServerOptions = {
4
+ hostname?: string;
5
+ port?: number;
6
+ rpcPath?: string;
7
+ eventsPath?: string;
8
+ };
9
+ export type AcpHttpServer = {
10
+ hostname: string;
11
+ port: number;
12
+ rpcPath: string;
13
+ eventsPath: string;
14
+ rpcUrl: string;
15
+ eventsUrl: string;
16
+ completed: Promise<void>;
17
+ close: () => Promise<void>;
18
+ };
19
+ export declare function serveAcpOverHttp(runtime: AgentHarnessRuntime, options?: AcpHttpServerOptions): Promise<AcpHttpServer>;
20
+ export type { Server as AcpHttpNodeServer };
@@ -0,0 +1,130 @@
1
+ import { createServer } from "node:http";
2
+ import { createAcpServer } from "../../acp.js";
3
+ function normalizePath(value, fallback) {
4
+ const source = typeof value === "string" && value.trim().length > 0 ? value.trim() : fallback;
5
+ return source.startsWith("/") ? source : `/${source}`;
6
+ }
7
+ function writeJson(response, statusCode, payload) {
8
+ response.statusCode = statusCode;
9
+ response.setHeader("content-type", "application/json; charset=utf-8");
10
+ response.end(JSON.stringify(payload));
11
+ }
12
+ function readRequestBody(request) {
13
+ return new Promise((resolve, reject) => {
14
+ const chunks = [];
15
+ request.on("data", (chunk) => {
16
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
17
+ });
18
+ request.on("end", () => {
19
+ resolve(Buffer.concat(chunks).toString("utf8"));
20
+ });
21
+ request.on("error", reject);
22
+ });
23
+ }
24
+ export async function serveAcpOverHttp(runtime, options = {}) {
25
+ const hostname = options.hostname?.trim() || "127.0.0.1";
26
+ const port = typeof options.port === "number" && Number.isFinite(options.port) ? options.port : 0;
27
+ const rpcPath = normalizePath(options.rpcPath, "/rpc");
28
+ const eventsPath = normalizePath(options.eventsPath, "/events");
29
+ const server = createAcpServer(runtime);
30
+ const eventSubscribers = new Set();
31
+ const unsubscribe = server.subscribe((notification) => {
32
+ const payload = `data: ${JSON.stringify(notification)}\n\n`;
33
+ for (const response of Array.from(eventSubscribers)) {
34
+ if (response.writableEnded || response.destroyed) {
35
+ eventSubscribers.delete(response);
36
+ continue;
37
+ }
38
+ response.write(payload);
39
+ }
40
+ });
41
+ const httpServer = createServer(async (request, response) => {
42
+ try {
43
+ const requestUrl = new URL(request.url ?? "/", `http://${hostname}`);
44
+ if (request.method === "GET" && requestUrl.pathname === eventsPath) {
45
+ response.statusCode = 200;
46
+ response.setHeader("content-type", "text/event-stream; charset=utf-8");
47
+ response.setHeader("cache-control", "no-cache, no-transform");
48
+ response.setHeader("connection", "keep-alive");
49
+ response.write(": connected\n\n");
50
+ eventSubscribers.add(response);
51
+ request.on("close", () => {
52
+ eventSubscribers.delete(response);
53
+ response.end();
54
+ });
55
+ return;
56
+ }
57
+ if (request.method === "POST" && requestUrl.pathname === rpcPath) {
58
+ const body = await readRequestBody(request);
59
+ let payload;
60
+ try {
61
+ payload = JSON.parse(body);
62
+ }
63
+ catch {
64
+ writeJson(response, 400, {
65
+ jsonrpc: "2.0",
66
+ id: null,
67
+ error: {
68
+ code: -32700,
69
+ message: "Invalid JSON payload.",
70
+ },
71
+ });
72
+ return;
73
+ }
74
+ const rpcResponse = await server.handle(payload);
75
+ if (!rpcResponse) {
76
+ response.statusCode = 204;
77
+ response.end();
78
+ return;
79
+ }
80
+ writeJson(response, 200, rpcResponse);
81
+ return;
82
+ }
83
+ writeJson(response, 404, {
84
+ error: "Not Found",
85
+ rpcPath,
86
+ eventsPath,
87
+ });
88
+ }
89
+ catch (error) {
90
+ writeJson(response, 500, {
91
+ error: error instanceof Error ? error.message : "ACP HTTP transport failed.",
92
+ });
93
+ }
94
+ });
95
+ const completed = new Promise((resolve, reject) => {
96
+ httpServer.once("close", resolve);
97
+ httpServer.once("error", reject);
98
+ });
99
+ await new Promise((resolve, reject) => {
100
+ httpServer.listen(port, hostname, () => resolve());
101
+ httpServer.once("error", reject);
102
+ });
103
+ const address = httpServer.address();
104
+ const resolvedPort = typeof address === "object" && address ? address.port : port;
105
+ return {
106
+ hostname,
107
+ port: resolvedPort,
108
+ rpcPath,
109
+ eventsPath,
110
+ rpcUrl: `http://${hostname}:${resolvedPort}${rpcPath}`,
111
+ eventsUrl: `http://${hostname}:${resolvedPort}${eventsPath}`,
112
+ completed,
113
+ close: async () => {
114
+ unsubscribe();
115
+ for (const response of Array.from(eventSubscribers)) {
116
+ response.end();
117
+ }
118
+ eventSubscribers.clear();
119
+ await new Promise((resolve, reject) => {
120
+ httpServer.close((error) => {
121
+ if (error) {
122
+ reject(error);
123
+ return;
124
+ }
125
+ resolve();
126
+ });
127
+ });
128
+ },
129
+ };
130
+ }
@@ -57,6 +57,25 @@ function readRisk(value) {
57
57
  function readApprovalPolicy(value) {
58
58
  return value === "explicit-hitl" || value === "runtime-default" || value === "none" ? value : undefined;
59
59
  }
60
+ function normalizeServerRef(value) {
61
+ if (typeof value !== "string" || value.trim().length === 0) {
62
+ return undefined;
63
+ }
64
+ const trimmed = value.trim();
65
+ return trimmed.startsWith("mcp/") ? trimmed : `mcp/${trimmed}`;
66
+ }
67
+ function readRemoteMcpMetadata(tool) {
68
+ const config = asObject(tool.config);
69
+ const mcpReference = asObject(config?.mcp);
70
+ const inlineServer = asObject(config?.mcpServer);
71
+ const transport = typeof inlineServer?.transport === "string" && inlineServer.transport.trim().length > 0
72
+ ? inlineServer.transport.trim()
73
+ : undefined;
74
+ return {
75
+ ...(normalizeServerRef(mcpReference?.serverRef) ? { serverRef: normalizeServerRef(mcpReference?.serverRef) } : {}),
76
+ ...(transport ? { transport } : {}),
77
+ };
78
+ }
60
79
  function matchesToolPolicy(rule, policy) {
61
80
  const match = asObject(rule.match) ?? rule;
62
81
  const toolName = typeof match.toolName === "string" ? match.toolName.trim() : undefined;
@@ -102,14 +121,49 @@ function applyGovernanceOverrides(binding, policies) {
102
121
  return merged;
103
122
  });
104
123
  }
124
+ function applyRemoteMcpGovernance(binding, policies) {
125
+ const governance = asObject(binding.harnessRuntime.governance);
126
+ const remoteMcp = asObject(governance?.remoteMcp);
127
+ if (!remoteMcp) {
128
+ return policies;
129
+ }
130
+ const requireApprovalTransports = new Set(readStringArray(remoteMcp.requireApprovalTransports));
131
+ const riskByTransport = asObject(remoteMcp.riskByTransport);
132
+ const inputRiskHintsByTransport = asObject(remoteMcp.inputRiskHintsByTransport);
133
+ return policies.map((policy) => {
134
+ if (policy.category !== "mcp") {
135
+ return policy;
136
+ }
137
+ const merged = { ...policy };
138
+ const transport = merged.mcpTransport;
139
+ if (transport && requireApprovalTransports.has(transport)) {
140
+ merged.requiresApproval = true;
141
+ if (merged.approvalPolicy === "none") {
142
+ merged.approvalPolicy = "runtime-default";
143
+ }
144
+ }
145
+ const transportRisk = transport ? readRisk(riskByTransport?.[transport]) : undefined;
146
+ if (transportRisk) {
147
+ merged.risk = transportRisk;
148
+ }
149
+ const transportHints = transport ? readStringArray(inputRiskHintsByTransport?.[transport]) : [];
150
+ if (transportHints.length > 0) {
151
+ merged.inputRiskHints = Array.from(new Set([...merged.inputRiskHints, ...transportHints]));
152
+ }
153
+ return merged;
154
+ });
155
+ }
105
156
  export function buildRuntimeGovernanceBundles(binding) {
106
- const toolPolicies = applyGovernanceOverrides(binding, getBindingPrimaryTools(binding).map((tool) => {
157
+ const toolPolicies = applyGovernanceOverrides(binding, applyRemoteMcpGovernance(binding, getBindingPrimaryTools(binding).map((tool) => {
107
158
  const requiresApproval = toolRequiresRuntimeApproval(tool);
159
+ const remoteMcp = readRemoteMcpMetadata(tool);
108
160
  return {
109
161
  toolName: tool.name,
110
162
  toolId: tool.id,
111
163
  toolType: tool.type,
112
164
  category: toCategory(tool.type),
165
+ ...(remoteMcp.serverRef ? { mcpServerRef: remoteMcp.serverRef } : {}),
166
+ ...(remoteMcp.transport ? { mcpTransport: remoteMcp.transport } : {}),
113
167
  risk: classifyRisk({
114
168
  toolType: tool.type,
115
169
  requiresApproval,
@@ -122,7 +176,7 @@ export function buildRuntimeGovernanceBundles(binding) {
122
176
  hasInputSchema: typeof tool.inputSchemaRef === "string" && tool.inputSchemaRef.trim().length > 0,
123
177
  inputRiskHints: inputHints(binding, tool),
124
178
  };
125
- }));
179
+ })));
126
180
  if (toolPolicies.length === 0) {
127
181
  return [];
128
182
  }
@@ -12,6 +12,9 @@ export class PolicyEngine {
12
12
  const governance = typeof binding.harnessRuntime.governance === "object" && binding.harnessRuntime.governance
13
13
  ? binding.harnessRuntime.governance
14
14
  : undefined;
15
+ const remoteMcp = typeof governance?.remoteMcp === "object" && governance.remoteMcp
16
+ ? governance.remoteMcp
17
+ : undefined;
15
18
  const denyConfig = typeof governance?.deny === "object" && governance.deny
16
19
  ? governance.deny
17
20
  : undefined;
@@ -38,6 +41,72 @@ export class PolicyEngine {
38
41
  reasons.push(`runtime governance denied tool access: ${blocked.map((tool) => tool.name).join(", ")}`);
39
42
  }
40
43
  }
44
+ if (remoteMcp) {
45
+ const normalizeServerRef = (value) => {
46
+ if (typeof value !== "string" || value.trim().length === 0) {
47
+ return undefined;
48
+ }
49
+ const trimmed = value.trim();
50
+ return trimmed.startsWith("mcp/") ? trimmed : `mcp/${trimmed}`;
51
+ };
52
+ const allowServerRefs = new Set(Array.isArray(remoteMcp.allowServerRefs)
53
+ ? remoteMcp.allowServerRefs
54
+ .map((item) => normalizeServerRef(item))
55
+ .filter((item) => Boolean(item))
56
+ : []);
57
+ const denyServerRefs = new Set(Array.isArray(remoteMcp.denyServerRefs)
58
+ ? remoteMcp.denyServerRefs
59
+ .map((item) => normalizeServerRef(item))
60
+ .filter((item) => Boolean(item))
61
+ : []);
62
+ const denyTransports = new Set(Array.isArray(remoteMcp.denyTransports)
63
+ ? remoteMcp.denyTransports.filter((item) => typeof item === "string" && item.trim().length > 0).map((item) => item.trim())
64
+ : []);
65
+ const tools = binding.execution?.params?.tools ?? binding.langchainAgentParams?.tools ?? binding.deepAgentParams?.tools ?? [];
66
+ const deniedRemoteTools = tools.flatMap((tool) => {
67
+ if (tool.type !== "mcp") {
68
+ return [];
69
+ }
70
+ const config = typeof tool.config === "object" && tool.config && !Array.isArray(tool.config)
71
+ ? tool.config
72
+ : undefined;
73
+ const mcpRef = typeof config?.mcp === "object" && config.mcp && !Array.isArray(config.mcp)
74
+ ? config.mcp
75
+ : undefined;
76
+ const inlineMcpServer = typeof config?.mcpServer === "object" && config.mcpServer && !Array.isArray(config.mcpServer)
77
+ ? config.mcpServer
78
+ : undefined;
79
+ const serverRef = normalizeServerRef(mcpRef?.serverRef);
80
+ const transport = typeof inlineMcpServer?.transport === "string" && inlineMcpServer.transport.trim().length > 0
81
+ ? inlineMcpServer.transport.trim()
82
+ : undefined;
83
+ const serverDenied = serverRef ? denyServerRefs.has(serverRef) || (allowServerRefs.size > 0 && !allowServerRefs.has(serverRef)) : false;
84
+ const transportDenied = transport ? denyTransports.has(transport) : false;
85
+ return serverDenied || transportDenied
86
+ ? [{
87
+ toolName: tool.name,
88
+ ...(serverRef ? { serverRef } : {}),
89
+ ...(transport ? { transport } : {}),
90
+ }]
91
+ : [];
92
+ });
93
+ if (deniedRemoteTools.length > 0) {
94
+ allowed = false;
95
+ const details = deniedRemoteTools.map((tool) => {
96
+ if (tool.serverRef && tool.transport) {
97
+ return `${tool.toolName} (${tool.serverRef}, ${tool.transport})`;
98
+ }
99
+ if (tool.serverRef) {
100
+ return `${tool.toolName} (${tool.serverRef})`;
101
+ }
102
+ if (tool.transport) {
103
+ return `${tool.toolName} (${tool.transport})`;
104
+ }
105
+ return tool.toolName;
106
+ });
107
+ reasons.push(`runtime governance denied remote MCP access: ${details.join(", ")}`);
108
+ }
109
+ }
41
110
  for (const evaluator of getPolicyEvaluators()) {
42
111
  const decision = evaluator.evaluate(binding);
43
112
  if (!decision) {
@@ -1,4 +1,4 @@
1
- import type { ApprovalRecord, ArtifactListing, CancelOptions, HarnessEvent, HarnessStreamItem, RuntimeHealthSnapshot, ListMemoriesInput, ListMemoriesResult, MessageContent, RemoveMemoryInput, RunRecord, RunStartOptions, RestartConversationOptions, RuntimeAdapterOptions, RuntimeEvaluationExport, RuntimeEvaluationExportInput, RuntimeEvaluationReplayInput, RuntimeEvaluationReplayResult, ResumeOptions, RunOptions, RunResult, RunSummary, MemoryRecord, MemorizeInput, MemorizeResult, RecallInput, RecallResult, UpdateMemoryInput, ThreadSummary, ThreadRecord, WorkspaceBundle } from "../contracts/types.js";
1
+ import type { ApprovalRecord, ArtifactListing, CancelOptions, HarnessEvent, HarnessStreamItem, RuntimeHealthSnapshot, ListMemoriesInput, ListMemoriesResult, MessageContent, RemoveMemoryInput, RunRecord, RunStartOptions, RestartConversationOptions, RuntimeAdapterOptions, RuntimeEvaluationExport, RuntimeEvaluationExportInput, RuntimeEvaluationReplayInput, RuntimeEvaluationReplayResult, RuntimeRunPackage, RuntimeRunPackageInput, RuntimeSessionPackage, RuntimeSessionPackageInput, ResumeOptions, RunOptions, RunResult, RunSummary, MemoryRecord, MemorizeInput, MemorizeResult, RecallInput, RecallResult, UpdateMemoryInput, ThreadSummary, ThreadRecord, WorkspaceBundle } from "../contracts/types.js";
2
2
  import { type ToolMcpServerOptions } from "../mcp.js";
3
3
  import { type InventoryAgentRecord, type InventorySkillRecord } from "./harness/system/inventory.js";
4
4
  import type { RequirementAssessmentOptions } from "./harness/system/skill-requirements.js";
@@ -85,6 +85,8 @@ export declare class AgentHarnessRuntime {
85
85
  listArtifacts(threadId: string, runId: string): Promise<ArtifactListing>;
86
86
  readArtifact(threadId: string, runId: string, artifactPath: string): Promise<unknown>;
87
87
  listRunEvents(threadId: string, runId: string): Promise<HarnessEvent[]>;
88
+ exportRunPackage(input: RuntimeRunPackageInput): Promise<RuntimeRunPackage>;
89
+ exportSessionPackage(input: RuntimeSessionPackageInput): Promise<RuntimeSessionPackage>;
88
90
  exportEvaluationBundle(input: RuntimeEvaluationExportInput): Promise<RuntimeEvaluationExport>;
89
91
  replayEvaluationBundle(input: RuntimeEvaluationReplayInput): Promise<RuntimeEvaluationReplayResult>;
90
92
  listAgentSkills(agentId: string, options?: RequirementAssessmentOptions): InventorySkillRecord[];
@@ -484,6 +484,50 @@ export class AgentHarnessRuntime {
484
484
  async listRunEvents(threadId, runId) {
485
485
  return this.persistence.listRunEvents(threadId, runId);
486
486
  }
487
+ async exportRunPackage(input) {
488
+ const thread = await this.getThread(input.sessionId);
489
+ const run = await this.getRun(input.requestId);
490
+ const approvals = await this.listApprovals({ threadId: input.sessionId, runId: input.requestId });
491
+ const transcript = await this.persistence.listThreadMessages(input.sessionId, 500);
492
+ const events = await this.persistence.listRunEvents(input.sessionId, input.requestId);
493
+ const artifactsListing = input.includeArtifacts === false
494
+ ? { items: [] }
495
+ : await this.persistence.listArtifacts(input.sessionId, input.requestId);
496
+ const artifacts = await Promise.all(artifactsListing.items.map(async (artifact) => ({
497
+ ...artifact,
498
+ ...(input.includeArtifactContents === true
499
+ ? { content: await this.persistence.readArtifact(input.sessionId, input.requestId, artifact.path) }
500
+ : {}),
501
+ })));
502
+ return {
503
+ session: thread ? toSessionRecord(thread) : null,
504
+ request: run ? toRequestRecord(run) : null,
505
+ approvals,
506
+ transcript,
507
+ events,
508
+ artifacts,
509
+ ...(input.includeRuntimeHealth === false ? {} : { runtimeHealth: await this.getHealth() }),
510
+ };
511
+ }
512
+ async exportSessionPackage(input) {
513
+ const thread = await this.getThread(input.sessionId);
514
+ const runIds = Array.from(new Set((thread?.runs ?? []).map((item) => item.runId)));
515
+ const runs = await Promise.all(runIds.map((requestId) => this.exportRunPackage({
516
+ sessionId: input.sessionId,
517
+ requestId,
518
+ includeArtifacts: input.includeArtifacts,
519
+ includeArtifactContents: input.includeArtifactContents,
520
+ includeRuntimeHealth: false,
521
+ })));
522
+ return {
523
+ session: thread ? toSessionRecord(thread) : null,
524
+ requests: runs.map((item) => item.request).filter((item) => Boolean(item)),
525
+ approvals: await this.listApprovals({ threadId: input.sessionId }),
526
+ transcript: await this.persistence.listThreadMessages(input.sessionId, 500),
527
+ runs,
528
+ ...(input.includeRuntimeHealth === false ? {} : { runtimeHealth: await this.getHealth() }),
529
+ };
530
+ }
487
531
  async exportEvaluationBundle(input) {
488
532
  const thread = await this.getThread(input.sessionId);
489
533
  const run = await this.getRun(input.requestId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.160",
3
+ "version": "0.0.162",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",