@botbotgo/agent-harness 0.0.163 → 0.0.165

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
@@ -26,6 +26,11 @@
26
26
  (static page in <code>docs/</code>, publish with GitHub Pages; EN / 中文 toggle)
27
27
  </p>
28
28
 
29
+ <p align="center">
30
+ <a href="https://botbotgo.github.io/agent-harness/development/">Developer docs</a>
31
+ (multi-page static docs in <code>docs/development/</code>)
32
+ </p>
33
+
29
34
  <p align="center">
30
35
  <em
31
36
  >We specialize in AI solutions. If you have a product idea you want to ship,
@@ -142,6 +147,13 @@ The default rule is:
142
147
 
143
148
  Boundary documents live in:
144
149
 
150
+ - `docs/development/index.html`
151
+ - `docs/development/getting-started.html`
152
+ - `docs/development/application-model.html`
153
+ - `docs/development/workspace-and-yaml.html`
154
+ - `docs/development/extensions.html`
155
+ - `docs/development/runtime-operations.html`
156
+ - `docs/development/testing-and-release.html`
145
157
  - `docs/upstream-feature-matrix.md`
146
158
  - `docs/product-boundary.md`
147
159
  - `docs/runtime-blueprint-assessment.md`
@@ -890,6 +902,7 @@ Primary exports:
890
902
  - `createAcpServer`
891
903
  - `serveAcpHttp`
892
904
  - `serveAcpStdio`
905
+ - `serveAgUiHttp`
893
906
  - `createToolMcpServer`
894
907
  - `serveToolsOverStdio`
895
908
  - `stop`
@@ -911,5 +924,6 @@ ACP transport notes:
911
924
 
912
925
  - `serveAcpStdio(runtime)` exposes newline-delimited JSON-RPC over stdio for local IDE, CLI, or subprocess clients.
913
926
  - `serveAcpHttp(runtime)` exposes JSON-RPC over HTTP plus SSE runtime events so remote operator surfaces can connect without importing the runtime in-process.
927
+ - `serveAgUiHttp(runtime)` exposes a minimal AG-UI-compatible HTTP SSE bridge that projects `run + output.delta + final result` onto `RUN_STARTED`, `TEXT_MESSAGE_*`, and `RUN_FINISHED` events for UI clients.
914
928
  - `exportRunPackage(...)` and `exportSessionPackage(...)` package stable runtime records, transcript, approvals, events, and artifacts for operator tooling without reaching into persistence internals.
915
929
  - `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
@@ -26,6 +26,11 @@
26
26
  (<code>docs/</code> 中的静态页,通过 GitHub Pages 发布;支持中英文切换)
27
27
  </p>
28
28
 
29
+ <p align="center">
30
+ <a href="https://botbotgo.github.io/agent-harness/development/">开发文档</a>
31
+ (多页面静态文档位于 <code>docs/development/</code>)
32
+ </p>
33
+
29
34
  <p align="center">
30
35
  <em
31
36
  >我们专注于 AI 解决方案。若有希望落地的产品想法,欢迎来信
@@ -142,6 +147,13 @@ AI 让 agent 逻辑、工具调用和工作流代码更容易生成,真正更
142
147
 
143
148
  边界说明见:
144
149
 
150
+ - `docs/development/index.html`
151
+ - `docs/development/getting-started.html`
152
+ - `docs/development/application-model.html`
153
+ - `docs/development/workspace-and-yaml.html`
154
+ - `docs/development/extensions.html`
155
+ - `docs/development/runtime-operations.html`
156
+ - `docs/development/testing-and-release.html`
145
157
  - `docs/upstream-feature-matrix.md`
146
158
  - `docs/product-boundary.md`
147
159
  - `docs/runtime-blueprint-assessment.md`
@@ -849,6 +861,7 @@ spec:
849
861
  - `createAcpServer`
850
862
  - `serveAcpHttp`
851
863
  - `serveAcpStdio`
864
+ - `serveAgUiHttp`
852
865
  - `createToolMcpServer`
853
866
  - `serveToolsOverStdio`
854
867
  - `stop`
@@ -870,5 +883,6 @@ ACP transport 说明:
870
883
 
871
884
  - `serveAcpStdio(runtime)` 提供基于 stdio 的 newline-delimited JSON-RPC,适合本地 IDE、CLI 或子进程客户端。
872
885
  - `serveAcpHttp(runtime)` 提供基于 HTTP 的 JSON-RPC 与 SSE runtime events,适合远程 operator surface 或独立控制面接入。
886
+ - `serveAgUiHttp(runtime)` 提供最小可用的 AG-UI HTTP SSE bridge,把现有 `run + output.delta + final result` 投影成 `RUN_STARTED`、`TEXT_MESSAGE_*` 与 `RUN_FINISHED` 事件,便于 UI 客户端直接接入。
873
887
  - `exportRunPackage(...)` 与 `exportSessionPackage(...)` 可把稳定 runtime 记录、transcript、approvals、events 和 artifacts 打包给 operator tooling,而不必直接访问 persistence 内部实现。
874
888
  - `runtime/default.governance.remoteMcp` 现在可以按 MCP server 或 transport 做 allow/deny、审批升级,并把 transport 风险等级写进 runtime governance bundles。
package/dist/api.d.ts CHANGED
@@ -11,6 +11,7 @@ export { createUpstreamTimelineReducer } from "./upstream-events.js";
11
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
12
  export type { AcpHttpServer, AcpHttpServerOptions } from "./protocol/acp/http.js";
13
13
  export type { AcpStdioServer, AcpStdioServerOptions } from "./protocol/acp/stdio.js";
14
+ export type { AgUiEvent, AgUiHttpServer, AgUiHttpServerOptions, AgUiRunAgentInput } from "./protocol/ag-ui/http.js";
14
15
  type PublicApprovalRecord = {
15
16
  approvalId: string;
16
17
  pendingActionId: string;
@@ -97,6 +98,7 @@ export declare function exportEvaluationBundle(runtime: AgentHarnessRuntime, inp
97
98
  export declare function replayEvaluationBundle(runtime: AgentHarnessRuntime, input: RuntimeEvaluationReplayInput): Promise<RuntimeEvaluationReplayResult>;
98
99
  export declare function serveAcpStdio(runtime: AgentHarnessRuntime, options?: import("./protocol/acp/stdio.js").AcpStdioServerOptions): import("./protocol/acp/stdio.js").AcpStdioServer;
99
100
  export declare function serveAcpHttp(runtime: AgentHarnessRuntime, options?: import("./protocol/acp/http.js").AcpHttpServerOptions): Promise<import("./protocol/acp/http.js").AcpHttpServer>;
101
+ export declare function serveAgUiHttp(runtime: AgentHarnessRuntime, options?: import("./protocol/ag-ui/http.js").AgUiHttpServerOptions): Promise<import("./protocol/ag-ui/http.js").AgUiHttpServer>;
100
102
  export declare function listAgentSkills(runtime: AgentHarnessRuntime, agentId: string, options?: RequirementAssessmentOptions): InventorySkillRecord[];
101
103
  export declare function getAgent(runtime: AgentHarnessRuntime, agentId: string, options?: RequirementAssessmentOptions): InventoryAgentRecord | null;
102
104
  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 { serveAgUiOverHttp } from "./protocol/ag-ui/http.js";
2
3
  import { serveAcpOverHttp } from "./protocol/acp/http.js";
3
4
  import { serveAcpOverStdio } from "./protocol/acp/stdio.js";
4
5
  import { normalizeMessageContent } from "./utils/message-content.js";
@@ -212,6 +213,9 @@ export function serveAcpStdio(runtime, options) {
212
213
  export async function serveAcpHttp(runtime, options) {
213
214
  return serveAcpOverHttp(runtime, options);
214
215
  }
216
+ export async function serveAgUiHttp(runtime, options) {
217
+ return serveAgUiOverHttp(runtime, options);
218
+ }
215
219
  export function listAgentSkills(runtime, agentId, options) {
216
220
  return runtime.listAgentSkills(agentId, options);
217
221
  }
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 { serveAgUiOverHttp } from "./protocol/ag-ui/http.js";
3
4
  import { serveAcpOverHttp } from "./protocol/acp/http.js";
4
5
  import { serveAcpOverStdio } from "./protocol/acp/stdio.js";
5
6
  type CliIo = {
@@ -9,6 +10,7 @@ type CliIo = {
9
10
  };
10
11
  type CliDeps = {
11
12
  createAgentHarness?: typeof createAgentHarness;
13
+ serveAgUiOverHttp?: typeof serveAgUiOverHttp;
12
14
  serveAcpOverHttp?: typeof serveAcpOverHttp;
13
15
  serveAcpOverStdio?: typeof serveAcpOverStdio;
14
16
  };
package/dist/cli.js CHANGED
@@ -3,12 +3,14 @@ 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 { serveAgUiOverHttp } from "./protocol/ag-ui/http.js";
6
7
  import { serveAcpOverHttp } from "./protocol/acp/http.js";
7
8
  import { serveAcpOverStdio } from "./protocol/acp/stdio.js";
8
9
  function renderUsage() {
9
10
  return `Usage:
10
11
  agent-harness init <project-name> [--template deep-research|single-agent] [--provider <provider>] [--model <model>] [--with-web-search|--no-web-search]
11
12
  agent-harness acp serve [--workspace <path>] [--transport stdio|http] [--host <hostname>] [--port <port>]
13
+ agent-harness ag-ui serve [--workspace <path>] [--host <hostname>] [--port <port>]
12
14
  `;
13
15
  }
14
16
  function isTemplate(value) {
@@ -104,12 +106,54 @@ function parseAcpServeOptions(args) {
104
106
  }
105
107
  return { workspaceRoot, transport, hostname, port };
106
108
  }
109
+ function parseHttpServeOptions(args) {
110
+ let workspaceRoot;
111
+ let hostname;
112
+ let port;
113
+ for (let index = 0; index < args.length; index += 1) {
114
+ const arg = args[index];
115
+ if (arg === "--workspace") {
116
+ const value = args[index + 1];
117
+ if (!value) {
118
+ return { hostname, port, error: "Missing value for --workspace" };
119
+ }
120
+ workspaceRoot = value;
121
+ index += 1;
122
+ continue;
123
+ }
124
+ if (arg === "--host") {
125
+ const value = args[index + 1];
126
+ if (!value) {
127
+ return { workspaceRoot, port, error: "Missing value for --host" };
128
+ }
129
+ hostname = value;
130
+ index += 1;
131
+ continue;
132
+ }
133
+ if (arg === "--port") {
134
+ const value = args[index + 1];
135
+ if (!value) {
136
+ return { workspaceRoot, hostname, error: "Missing value for --port" };
137
+ }
138
+ const parsedPort = Number.parseInt(value, 10);
139
+ if (!Number.isFinite(parsedPort) || parsedPort < 0) {
140
+ return { workspaceRoot, hostname, error: `Invalid AG-UI port: ${value}` };
141
+ }
142
+ port = parsedPort;
143
+ index += 1;
144
+ continue;
145
+ }
146
+ return { workspaceRoot, hostname, port, error: `Unknown option: ${arg}` };
147
+ }
148
+ return { workspaceRoot, hostname, port };
149
+ }
107
150
  export async function runCli(argv, io = {}, deps = {}) {
108
151
  const cwd = io.cwd ?? process.cwd();
109
152
  const stdout = io.stdout ?? ((message) => process.stdout.write(message));
110
153
  const stderr = io.stderr ?? ((message) => process.stderr.write(message));
111
154
  const [command, projectName, ...rest] = argv;
112
155
  const createHarness = deps.createAgentHarness ?? createAgentHarness;
156
+ const serveAgUi = deps.serveAgUiOverHttp ?? serveAgUiOverHttp;
113
157
  const serveAcpHttp = deps.serveAcpOverHttp ?? serveAcpOverHttp;
114
158
  const serveAcp = deps.serveAcpOverStdio ?? serveAcpOverStdio;
115
159
  if (command === "init") {
@@ -179,6 +223,36 @@ export async function runCli(argv, io = {}, deps = {}) {
179
223
  return 1;
180
224
  }
181
225
  }
226
+ if (command === "ag-ui") {
227
+ const [subcommand, ...subcommandArgs] = [projectName, ...rest];
228
+ if (subcommand !== "serve") {
229
+ stderr(renderUsage());
230
+ return 1;
231
+ }
232
+ const parsed = parseHttpServeOptions(subcommandArgs);
233
+ if (parsed.error) {
234
+ stderr(`${parsed.error}\n`);
235
+ stderr(renderUsage());
236
+ return 1;
237
+ }
238
+ try {
239
+ const runtime = await createHarness(path.resolve(cwd, parsed.workspaceRoot ?? "."));
240
+ const workspacePath = path.resolve(cwd, parsed.workspaceRoot ?? ".");
241
+ const server = await serveAgUi(runtime, {
242
+ hostname: parsed.hostname,
243
+ port: parsed.port,
244
+ });
245
+ stderr(`Serving AG-UI over http from ${workspacePath} at ${server.runUrl}\n`);
246
+ await server.completed;
247
+ await runtime.stop();
248
+ return 0;
249
+ }
250
+ catch (error) {
251
+ const message = error instanceof Error ? error.message : String(error);
252
+ stderr(`${message}\n`);
253
+ return 1;
254
+ }
255
+ }
182
256
  stderr(renderUsage());
183
257
  return 1;
184
258
  }
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
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";
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, serveAgUiHttp, 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
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 { AcpHttpServer, AcpHttpServerOptions, AcpStdioServer, AcpStdioServerOptions } from "./api.js";
5
+ export type { AcpHttpServer, AcpHttpServerOptions, AcpStdioServer, AcpStdioServerOptions, AgUiEvent, AgUiHttpServer, AgUiHttpServerOptions, AgUiRunAgentInput, } 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, 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";
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, serveAgUiHttp, 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.162";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.164";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.162";
1
+ export const AGENT_HARNESS_VERSION = "0.0.164";
@@ -0,0 +1,56 @@
1
+ import type { AgentHarnessRuntime } from "../../runtime/harness.js";
2
+ import type { MessageContent } from "../../contracts/types.js";
3
+ export type AgUiHttpServerOptions = {
4
+ hostname?: string;
5
+ port?: number;
6
+ runPath?: string;
7
+ };
8
+ export type AgUiRunAgentInput = {
9
+ input: MessageContent;
10
+ agentId?: string;
11
+ sessionId?: string;
12
+ invocation?: Record<string, unknown>;
13
+ };
14
+ type AgUiBaseEvent = {
15
+ type: string;
16
+ timestamp: string;
17
+ };
18
+ export type AgUiEvent = (AgUiBaseEvent & {
19
+ type: "RUN_STARTED";
20
+ threadId: string;
21
+ runId: string;
22
+ input?: MessageContent;
23
+ }) | (AgUiBaseEvent & {
24
+ type: "TEXT_MESSAGE_START";
25
+ messageId: string;
26
+ role: "assistant";
27
+ }) | (AgUiBaseEvent & {
28
+ type: "TEXT_MESSAGE_CONTENT";
29
+ messageId: string;
30
+ delta: string;
31
+ }) | (AgUiBaseEvent & {
32
+ type: "TEXT_MESSAGE_END";
33
+ messageId: string;
34
+ }) | (AgUiBaseEvent & {
35
+ type: "RUN_FINISHED";
36
+ threadId: string;
37
+ runId: string;
38
+ state?: string;
39
+ approvalId?: string;
40
+ pendingActionId?: string;
41
+ }) | (AgUiBaseEvent & {
42
+ type: "RUN_ERROR";
43
+ message: string;
44
+ runId?: string;
45
+ threadId?: string;
46
+ });
47
+ export type AgUiHttpServer = {
48
+ hostname: string;
49
+ port: number;
50
+ runPath: string;
51
+ runUrl: string;
52
+ completed: Promise<void>;
53
+ close: () => Promise<void>;
54
+ };
55
+ export declare function serveAgUiOverHttp(runtime: AgentHarnessRuntime, options?: AgUiHttpServerOptions): Promise<AgUiHttpServer>;
56
+ export {};
@@ -0,0 +1,194 @@
1
+ import { createServer } from "node:http";
2
+ import { createPersistentId } from "../../utils/id.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 readRequestBody(request) {
8
+ return new Promise((resolve, reject) => {
9
+ const chunks = [];
10
+ request.on("data", (chunk) => {
11
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
12
+ });
13
+ request.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
14
+ request.on("error", reject);
15
+ });
16
+ }
17
+ function writeSseEvent(response, payload) {
18
+ return new Promise((resolve, reject) => {
19
+ response.write(`data: ${JSON.stringify(payload)}\n\n`, (error) => {
20
+ if (error) {
21
+ reject(error);
22
+ return;
23
+ }
24
+ resolve();
25
+ });
26
+ });
27
+ }
28
+ function parseRunInput(payload) {
29
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
30
+ throw new Error("AG-UI run input must be an object.");
31
+ }
32
+ const typed = payload;
33
+ if (typeof typed.input !== "string"
34
+ && !(Array.isArray(typed.input))) {
35
+ throw new Error("AG-UI run input requires `input`.");
36
+ }
37
+ return {
38
+ input: typed.input,
39
+ ...(typeof typed.agentId === "string" && typed.agentId.trim().length > 0 ? { agentId: typed.agentId.trim() } : {}),
40
+ ...(typeof typed.sessionId === "string" && typed.sessionId.trim().length > 0 ? { sessionId: typed.sessionId.trim() } : {}),
41
+ ...(typed.invocation && typeof typed.invocation === "object" && !Array.isArray(typed.invocation)
42
+ ? { invocation: typed.invocation }
43
+ : {}),
44
+ };
45
+ }
46
+ function createTimestamp() {
47
+ return new Date().toISOString();
48
+ }
49
+ function toRunStarted(event, input) {
50
+ return {
51
+ type: "RUN_STARTED",
52
+ timestamp: createTimestamp(),
53
+ threadId: event.threadId,
54
+ runId: event.runId,
55
+ input,
56
+ };
57
+ }
58
+ export async function serveAgUiOverHttp(runtime, options = {}) {
59
+ const hostname = options.hostname?.trim() || "127.0.0.1";
60
+ const port = typeof options.port === "number" && Number.isFinite(options.port) ? options.port : 0;
61
+ const runPath = normalizePath(options.runPath, "/run");
62
+ const httpServer = createServer(async (request, response) => {
63
+ const url = new URL(request.url ?? "/", `http://${hostname}`);
64
+ if (request.method !== "POST" || url.pathname !== runPath) {
65
+ response.statusCode = 404;
66
+ response.setHeader("content-type", "application/json; charset=utf-8");
67
+ response.end(JSON.stringify({ error: "Not Found", runPath }));
68
+ return;
69
+ }
70
+ response.statusCode = 200;
71
+ response.setHeader("content-type", "text/event-stream; charset=utf-8");
72
+ response.setHeader("cache-control", "no-cache, no-transform");
73
+ response.setHeader("connection", "keep-alive");
74
+ let runId;
75
+ let threadId;
76
+ let messageId;
77
+ let textMessageStarted = false;
78
+ const ensureTextStart = async () => {
79
+ if (textMessageStarted) {
80
+ return;
81
+ }
82
+ messageId = messageId ?? `msg-${createPersistentId()}`;
83
+ textMessageStarted = true;
84
+ await writeSseEvent(response, {
85
+ type: "TEXT_MESSAGE_START",
86
+ timestamp: createTimestamp(),
87
+ messageId,
88
+ role: "assistant",
89
+ });
90
+ };
91
+ try {
92
+ const body = await readRequestBody(request);
93
+ const input = parseRunInput(JSON.parse(body));
94
+ const result = await runtime.run({
95
+ agentId: input.agentId,
96
+ input: input.input,
97
+ ...(input.sessionId ? { threadId: input.sessionId } : {}),
98
+ ...(input.invocation ? { invocation: input.invocation } : {}),
99
+ listeners: {
100
+ onEvent: async (event) => {
101
+ if (event.eventType === "run.created") {
102
+ runId = event.runId;
103
+ threadId = event.threadId;
104
+ await writeSseEvent(response, toRunStarted(event, input.input));
105
+ return;
106
+ }
107
+ if (event.eventType === "output.delta") {
108
+ runId = runId ?? event.runId;
109
+ threadId = threadId ?? event.threadId;
110
+ const delta = typeof event.payload.content === "string" ? event.payload.content : "";
111
+ if (!delta) {
112
+ return;
113
+ }
114
+ await ensureTextStart();
115
+ await writeSseEvent(response, {
116
+ type: "TEXT_MESSAGE_CONTENT",
117
+ timestamp: createTimestamp(),
118
+ messageId: messageId,
119
+ delta,
120
+ });
121
+ }
122
+ },
123
+ },
124
+ });
125
+ runId = runId ?? result.runId;
126
+ threadId = threadId ?? result.threadId;
127
+ if (!textMessageStarted && result.output) {
128
+ await ensureTextStart();
129
+ await writeSseEvent(response, {
130
+ type: "TEXT_MESSAGE_CONTENT",
131
+ timestamp: createTimestamp(),
132
+ messageId: messageId,
133
+ delta: result.output,
134
+ });
135
+ }
136
+ if (textMessageStarted) {
137
+ await writeSseEvent(response, {
138
+ type: "TEXT_MESSAGE_END",
139
+ timestamp: createTimestamp(),
140
+ messageId: messageId,
141
+ });
142
+ }
143
+ await writeSseEvent(response, {
144
+ type: "RUN_FINISHED",
145
+ timestamp: createTimestamp(),
146
+ threadId: threadId,
147
+ runId: runId,
148
+ state: result.state,
149
+ ...(result.approvalId ? { approvalId: result.approvalId } : {}),
150
+ ...(result.pendingActionId ? { pendingActionId: result.pendingActionId } : {}),
151
+ });
152
+ }
153
+ catch (error) {
154
+ await writeSseEvent(response, {
155
+ type: "RUN_ERROR",
156
+ timestamp: createTimestamp(),
157
+ ...(threadId ? { threadId } : {}),
158
+ ...(runId ? { runId } : {}),
159
+ message: error instanceof Error ? error.message : "AG-UI run failed.",
160
+ });
161
+ }
162
+ finally {
163
+ response.end();
164
+ }
165
+ });
166
+ const completed = new Promise((resolve, reject) => {
167
+ httpServer.once("close", resolve);
168
+ httpServer.once("error", reject);
169
+ });
170
+ await new Promise((resolve, reject) => {
171
+ httpServer.listen(port, hostname, () => resolve());
172
+ httpServer.once("error", reject);
173
+ });
174
+ const address = httpServer.address();
175
+ const resolvedPort = typeof address === "object" && address ? address.port : port;
176
+ return {
177
+ hostname,
178
+ port: resolvedPort,
179
+ runPath,
180
+ runUrl: `http://${hostname}:${resolvedPort}${runPath}`,
181
+ completed,
182
+ close: async () => {
183
+ await new Promise((resolve, reject) => {
184
+ httpServer.close((error) => {
185
+ if (error) {
186
+ reject(error);
187
+ return;
188
+ }
189
+ resolve();
190
+ });
191
+ });
192
+ },
193
+ };
194
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.163",
3
+ "version": "0.0.165",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",