@botbotgo/agent-harness 0.0.164 → 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 +2 -0
- package/README.zh.md +2 -0
- package/dist/api.d.ts +2 -0
- package/dist/api.js +4 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +74 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/protocol/ag-ui/http.d.ts +56 -0
- package/dist/protocol/ag-ui/http.js +194 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -902,6 +902,7 @@ Primary exports:
|
|
|
902
902
|
- `createAcpServer`
|
|
903
903
|
- `serveAcpHttp`
|
|
904
904
|
- `serveAcpStdio`
|
|
905
|
+
- `serveAgUiHttp`
|
|
905
906
|
- `createToolMcpServer`
|
|
906
907
|
- `serveToolsOverStdio`
|
|
907
908
|
- `stop`
|
|
@@ -923,5 +924,6 @@ ACP transport notes:
|
|
|
923
924
|
|
|
924
925
|
- `serveAcpStdio(runtime)` exposes newline-delimited JSON-RPC over stdio for local IDE, CLI, or subprocess clients.
|
|
925
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.
|
|
926
928
|
- `exportRunPackage(...)` and `exportSessionPackage(...)` package stable runtime records, transcript, approvals, events, and artifacts for operator tooling without reaching into persistence internals.
|
|
927
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
|
@@ -861,6 +861,7 @@ spec:
|
|
|
861
861
|
- `createAcpServer`
|
|
862
862
|
- `serveAcpHttp`
|
|
863
863
|
- `serveAcpStdio`
|
|
864
|
+
- `serveAgUiHttp`
|
|
864
865
|
- `createToolMcpServer`
|
|
865
866
|
- `serveToolsOverStdio`
|
|
866
867
|
- `stop`
|
|
@@ -882,5 +883,6 @@ ACP transport 说明:
|
|
|
882
883
|
|
|
883
884
|
- `serveAcpStdio(runtime)` 提供基于 stdio 的 newline-delimited JSON-RPC,适合本地 IDE、CLI 或子进程客户端。
|
|
884
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 客户端直接接入。
|
|
885
887
|
- `exportRunPackage(...)` 与 `exportSessionPackage(...)` 可把稳定 runtime 记录、transcript、approvals、events 和 artifacts 打包给 operator tooling,而不必直接访问 persistence 内部实现。
|
|
886
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.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.164";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
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
|
+
}
|