@botbotgo/agent-harness 0.0.186 → 0.0.187
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 +1 -1
- package/README.zh.md +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 +41 -0
- package/dist/protocol/ag-ui/http.js +181 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -954,7 +954,7 @@ ACP transport notes:
|
|
|
954
954
|
- `serveAcpStdio(runtime)` exposes newline-delimited JSON-RPC over stdio for local IDE, CLI, or subprocess clients.
|
|
955
955
|
- `serveAcpHttp(runtime)` exposes JSON-RPC over HTTP plus SSE runtime events so remote operator surfaces can connect without importing the runtime in-process.
|
|
956
956
|
- `serveA2aHttp(runtime)` exposes a minimal A2A-compatible HTTP JSON-RPC bridge plus agent card discovery, mapping `message/send`, `tasks/get`, and `tasks/cancel` onto the existing session/request runtime surface.
|
|
957
|
-
- `serveAgUiHttp(runtime)` exposes
|
|
957
|
+
- `serveAgUiHttp(runtime)` exposes an AG-UI-compatible HTTP SSE bridge that projects runtime lifecycle, text output, upstream thinking, step progress, and tool calls onto `RUN_*`, `TEXT_MESSAGE_*`, `THINKING_TEXT_MESSAGE_*`, `STEP_*`, and `TOOL_CALL_*` events for UI clients.
|
|
958
958
|
- `createRuntimeMcpServer(runtime)` and `serveRuntimeMcpOverStdio(runtime)` expose the persisted runtime control surface itself as MCP tools, including sessions, requests, approvals, artifacts, events, and package export helpers.
|
|
959
959
|
- `listRequestEvents(...)` and `exportRequestPackage(...)` are the request-first inspection helpers.
|
|
960
960
|
- `exportRequestPackage(...)` and `exportSessionPackage(...)` package stable runtime records, transcript, approvals, events, and artifacts for operator tooling without reaching into persistence internals.
|
package/README.zh.md
CHANGED
|
@@ -913,7 +913,7 @@ ACP transport 说明:
|
|
|
913
913
|
- `serveAcpStdio(runtime)` 提供基于 stdio 的 newline-delimited JSON-RPC,适合本地 IDE、CLI 或子进程客户端。
|
|
914
914
|
- `serveAcpHttp(runtime)` 提供基于 HTTP 的 JSON-RPC 与 SSE runtime events,适合远程 operator surface 或独立控制面接入。
|
|
915
915
|
- `serveA2aHttp(runtime)` 提供最小可用的 A2A HTTP JSON-RPC bridge 与 agent card discovery,把 `message/send`、`tasks/get`、`tasks/cancel` 映射到现有 session/request runtime surface。
|
|
916
|
-
- `serveAgUiHttp(runtime)`
|
|
916
|
+
- `serveAgUiHttp(runtime)` 提供 AG-UI HTTP SSE bridge,把 runtime 生命周期、文本输出、upstream thinking、step 进度与 tool call 投影成 `RUN_*`、`TEXT_MESSAGE_*`、`THINKING_TEXT_MESSAGE_*`、`STEP_*` 与 `TOOL_CALL_*` 事件,便于 UI 客户端直接接入。
|
|
917
917
|
- `createRuntimeMcpServer(runtime)` 与 `serveRuntimeMcpOverStdio(runtime)` 会把持久化 runtime 控制面本身暴露成 MCP tools,包括 sessions、requests、approvals、artifacts、events 与 package export helpers。
|
|
918
918
|
- `listRequestEvents(...)` 与 `exportRequestPackage(...)` 是 request-first 的检查 helper。
|
|
919
919
|
- `exportRequestPackage(...)` 与 `exportSessionPackage(...)` 可把稳定 runtime 记录、transcript、approvals、events 和 artifacts 打包给 operator tooling,而不必直接访问 persistence 内部实现。
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.186";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.186";
|
|
@@ -20,6 +20,28 @@ export type AgUiEvent = (AgUiBaseEvent & {
|
|
|
20
20
|
threadId: string;
|
|
21
21
|
runId: string;
|
|
22
22
|
input?: MessageContent;
|
|
23
|
+
}) | (AgUiBaseEvent & {
|
|
24
|
+
type: "STEP_STARTED";
|
|
25
|
+
stepId: string;
|
|
26
|
+
title: string;
|
|
27
|
+
category: "llm" | "tool" | "skill" | "memory" | "chain" | "approval";
|
|
28
|
+
}) | (AgUiBaseEvent & {
|
|
29
|
+
type: "STEP_FINISHED";
|
|
30
|
+
stepId: string;
|
|
31
|
+
title: string;
|
|
32
|
+
category: "llm" | "tool" | "skill" | "memory" | "chain" | "approval";
|
|
33
|
+
status: "completed" | "failed";
|
|
34
|
+
}) | (AgUiBaseEvent & {
|
|
35
|
+
type: "THINKING_TEXT_MESSAGE_START";
|
|
36
|
+
messageId: string;
|
|
37
|
+
role: "assistant";
|
|
38
|
+
}) | (AgUiBaseEvent & {
|
|
39
|
+
type: "THINKING_TEXT_MESSAGE_CONTENT";
|
|
40
|
+
messageId: string;
|
|
41
|
+
delta: string;
|
|
42
|
+
}) | (AgUiBaseEvent & {
|
|
43
|
+
type: "THINKING_TEXT_MESSAGE_END";
|
|
44
|
+
messageId: string;
|
|
23
45
|
}) | (AgUiBaseEvent & {
|
|
24
46
|
type: "TEXT_MESSAGE_START";
|
|
25
47
|
messageId: string;
|
|
@@ -31,6 +53,25 @@ export type AgUiEvent = (AgUiBaseEvent & {
|
|
|
31
53
|
}) | (AgUiBaseEvent & {
|
|
32
54
|
type: "TEXT_MESSAGE_END";
|
|
33
55
|
messageId: string;
|
|
56
|
+
}) | (AgUiBaseEvent & {
|
|
57
|
+
type: "TOOL_CALL_START";
|
|
58
|
+
toolCallId: string;
|
|
59
|
+
toolName: string;
|
|
60
|
+
}) | (AgUiBaseEvent & {
|
|
61
|
+
type: "TOOL_CALL_ARGS";
|
|
62
|
+
toolCallId: string;
|
|
63
|
+
toolName: string;
|
|
64
|
+
args: unknown;
|
|
65
|
+
}) | (AgUiBaseEvent & {
|
|
66
|
+
type: "TOOL_CALL_RESULT";
|
|
67
|
+
toolCallId: string;
|
|
68
|
+
toolName: string;
|
|
69
|
+
result: unknown;
|
|
70
|
+
isError?: boolean;
|
|
71
|
+
}) | (AgUiBaseEvent & {
|
|
72
|
+
type: "TOOL_CALL_END";
|
|
73
|
+
toolCallId: string;
|
|
74
|
+
toolName: string;
|
|
34
75
|
}) | (AgUiBaseEvent & {
|
|
35
76
|
type: "RUN_FINISHED";
|
|
36
77
|
threadId: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createServer } from "node:http";
|
|
2
2
|
import { createPersistentId } from "../../utils/id.js";
|
|
3
|
+
import { createUpstreamTimelineReducer } from "../../upstream-events.js";
|
|
3
4
|
function normalizePath(value, fallback) {
|
|
4
5
|
const source = typeof value === "string" && value.trim().length > 0 ? value.trim() : fallback;
|
|
5
6
|
return source.startsWith("/") ? source : `/${source}`;
|
|
@@ -55,6 +56,63 @@ function toRunStarted(event, input) {
|
|
|
55
56
|
input,
|
|
56
57
|
};
|
|
57
58
|
}
|
|
59
|
+
function asObject(value) {
|
|
60
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
61
|
+
}
|
|
62
|
+
function readUpstreamEventName(event) {
|
|
63
|
+
return typeof asObject(event)?.event === "string" ? String(asObject(event)?.event) : "";
|
|
64
|
+
}
|
|
65
|
+
function readUpstreamToolName(event) {
|
|
66
|
+
return typeof asObject(event)?.name === "string" ? String(asObject(event)?.name) : "";
|
|
67
|
+
}
|
|
68
|
+
function readUpstreamRunType(event) {
|
|
69
|
+
return typeof asObject(event)?.run_type === "string" ? String(asObject(event)?.run_type) : "";
|
|
70
|
+
}
|
|
71
|
+
function isToolStartEvent(event) {
|
|
72
|
+
const eventName = readUpstreamEventName(event);
|
|
73
|
+
return eventName === "on_tool_start" || (eventName === "on_chain_start" && readUpstreamRunType(event) === "tool");
|
|
74
|
+
}
|
|
75
|
+
function isToolTerminalEvent(event) {
|
|
76
|
+
const eventName = readUpstreamEventName(event);
|
|
77
|
+
return eventName === "on_tool_end"
|
|
78
|
+
|| eventName === "on_tool_error"
|
|
79
|
+
|| ((eventName === "on_chain_end" || eventName === "on_chain_error") && readUpstreamRunType(event) === "tool");
|
|
80
|
+
}
|
|
81
|
+
function readToolArgs(event) {
|
|
82
|
+
const typed = asObject(event);
|
|
83
|
+
const data = asObject(typed?.data);
|
|
84
|
+
if (!data || !("input" in data)) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
return data.input;
|
|
88
|
+
}
|
|
89
|
+
function createToolCallState() {
|
|
90
|
+
const activeToolCallIds = new Map();
|
|
91
|
+
return {
|
|
92
|
+
start(toolName) {
|
|
93
|
+
const toolCallId = `tool-${createPersistentId()}`;
|
|
94
|
+
const activeIds = activeToolCallIds.get(toolName) ?? [];
|
|
95
|
+
activeIds.push(toolCallId);
|
|
96
|
+
activeToolCallIds.set(toolName, activeIds);
|
|
97
|
+
return toolCallId;
|
|
98
|
+
},
|
|
99
|
+
peek(toolName) {
|
|
100
|
+
const activeIds = activeToolCallIds.get(toolName);
|
|
101
|
+
return activeIds?.at(-1);
|
|
102
|
+
},
|
|
103
|
+
finish(toolName) {
|
|
104
|
+
const activeIds = activeToolCallIds.get(toolName);
|
|
105
|
+
if (!activeIds || activeIds.length === 0) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
const toolCallId = activeIds.pop();
|
|
109
|
+
if (!activeIds.length) {
|
|
110
|
+
activeToolCallIds.delete(toolName);
|
|
111
|
+
}
|
|
112
|
+
return toolCallId;
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
58
116
|
export async function serveAgUiOverHttp(runtime, options = {}) {
|
|
59
117
|
const hostname = options.hostname?.trim() || "127.0.0.1";
|
|
60
118
|
const port = typeof options.port === "number" && Number.isFinite(options.port) ? options.port : 0;
|
|
@@ -74,7 +132,11 @@ export async function serveAgUiOverHttp(runtime, options = {}) {
|
|
|
74
132
|
let runId;
|
|
75
133
|
let threadId;
|
|
76
134
|
let messageId;
|
|
135
|
+
let thinkingMessageId;
|
|
77
136
|
let textMessageStarted = false;
|
|
137
|
+
let thinkingMessageStarted = false;
|
|
138
|
+
const toolCalls = createToolCallState();
|
|
139
|
+
const upstreamReducer = createUpstreamTimelineReducer();
|
|
78
140
|
const ensureTextStart = async () => {
|
|
79
141
|
if (textMessageStarted) {
|
|
80
142
|
return;
|
|
@@ -88,6 +150,37 @@ export async function serveAgUiOverHttp(runtime, options = {}) {
|
|
|
88
150
|
role: "assistant",
|
|
89
151
|
});
|
|
90
152
|
};
|
|
153
|
+
const ensureThinkingTextStart = async () => {
|
|
154
|
+
if (thinkingMessageStarted) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
thinkingMessageId = thinkingMessageId ?? `thinking-${createPersistentId()}`;
|
|
158
|
+
thinkingMessageStarted = true;
|
|
159
|
+
await writeSseEvent(response, {
|
|
160
|
+
type: "THINKING_TEXT_MESSAGE_START",
|
|
161
|
+
timestamp: createTimestamp(),
|
|
162
|
+
messageId: thinkingMessageId,
|
|
163
|
+
role: "assistant",
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
const closeOpenMessages = async () => {
|
|
167
|
+
if (thinkingMessageStarted) {
|
|
168
|
+
await writeSseEvent(response, {
|
|
169
|
+
type: "THINKING_TEXT_MESSAGE_END",
|
|
170
|
+
timestamp: createTimestamp(),
|
|
171
|
+
messageId: thinkingMessageId,
|
|
172
|
+
});
|
|
173
|
+
thinkingMessageStarted = false;
|
|
174
|
+
}
|
|
175
|
+
if (textMessageStarted) {
|
|
176
|
+
await writeSseEvent(response, {
|
|
177
|
+
type: "TEXT_MESSAGE_END",
|
|
178
|
+
timestamp: createTimestamp(),
|
|
179
|
+
messageId: messageId,
|
|
180
|
+
});
|
|
181
|
+
textMessageStarted = false;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
91
184
|
try {
|
|
92
185
|
const body = await readRequestBody(request);
|
|
93
186
|
const input = parseRunInput(JSON.parse(body));
|
|
@@ -120,6 +213,92 @@ export async function serveAgUiOverHttp(runtime, options = {}) {
|
|
|
120
213
|
});
|
|
121
214
|
}
|
|
122
215
|
},
|
|
216
|
+
onUpstreamEvent: async (event) => {
|
|
217
|
+
const eventName = readUpstreamEventName(event);
|
|
218
|
+
const toolName = readUpstreamToolName(event);
|
|
219
|
+
if (isToolStartEvent(event) && toolName) {
|
|
220
|
+
const toolCallId = toolCalls.start(toolName);
|
|
221
|
+
await writeSseEvent(response, {
|
|
222
|
+
type: "TOOL_CALL_START",
|
|
223
|
+
timestamp: createTimestamp(),
|
|
224
|
+
toolCallId,
|
|
225
|
+
toolName,
|
|
226
|
+
});
|
|
227
|
+
const args = readToolArgs(event);
|
|
228
|
+
if (args !== undefined) {
|
|
229
|
+
await writeSseEvent(response, {
|
|
230
|
+
type: "TOOL_CALL_ARGS",
|
|
231
|
+
timestamp: createTimestamp(),
|
|
232
|
+
toolCallId,
|
|
233
|
+
toolName,
|
|
234
|
+
args,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const projections = upstreamReducer.consume(event);
|
|
239
|
+
for (const projection of projections) {
|
|
240
|
+
if (projection.type === "thinking") {
|
|
241
|
+
if (!projection.text) {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
await ensureThinkingTextStart();
|
|
245
|
+
await writeSseEvent(response, {
|
|
246
|
+
type: "THINKING_TEXT_MESSAGE_CONTENT",
|
|
247
|
+
timestamp: createTimestamp(),
|
|
248
|
+
messageId: thinkingMessageId,
|
|
249
|
+
delta: projection.text,
|
|
250
|
+
});
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (projection.type === "step") {
|
|
254
|
+
if (projection.status === "started") {
|
|
255
|
+
await writeSseEvent(response, {
|
|
256
|
+
type: "STEP_STARTED",
|
|
257
|
+
timestamp: createTimestamp(),
|
|
258
|
+
stepId: projection.key,
|
|
259
|
+
title: projection.step,
|
|
260
|
+
category: projection.category,
|
|
261
|
+
});
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
await writeSseEvent(response, {
|
|
265
|
+
type: "STEP_FINISHED",
|
|
266
|
+
timestamp: createTimestamp(),
|
|
267
|
+
stepId: projection.key,
|
|
268
|
+
title: projection.step,
|
|
269
|
+
category: projection.category,
|
|
270
|
+
status: projection.status,
|
|
271
|
+
});
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const toolCallId = toolCalls.peek(projection.toolName) ?? toolCalls.start(projection.toolName);
|
|
275
|
+
await writeSseEvent(response, {
|
|
276
|
+
type: "TOOL_CALL_RESULT",
|
|
277
|
+
timestamp: createTimestamp(),
|
|
278
|
+
toolCallId,
|
|
279
|
+
toolName: projection.toolName,
|
|
280
|
+
result: projection.output,
|
|
281
|
+
...(projection.isError !== undefined ? { isError: projection.isError } : {}),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
if (isToolTerminalEvent(event) && toolName) {
|
|
285
|
+
const toolCallId = toolCalls.finish(toolName) ?? `tool-${createPersistentId()}`;
|
|
286
|
+
await writeSseEvent(response, {
|
|
287
|
+
type: "TOOL_CALL_END",
|
|
288
|
+
timestamp: createTimestamp(),
|
|
289
|
+
toolCallId,
|
|
290
|
+
toolName,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
if (eventName === "on_chat_model_end" && thinkingMessageStarted) {
|
|
294
|
+
await writeSseEvent(response, {
|
|
295
|
+
type: "THINKING_TEXT_MESSAGE_END",
|
|
296
|
+
timestamp: createTimestamp(),
|
|
297
|
+
messageId: thinkingMessageId,
|
|
298
|
+
});
|
|
299
|
+
thinkingMessageStarted = false;
|
|
300
|
+
}
|
|
301
|
+
},
|
|
123
302
|
},
|
|
124
303
|
});
|
|
125
304
|
runId = runId ?? result.runId;
|
|
@@ -133,13 +312,7 @@ export async function serveAgUiOverHttp(runtime, options = {}) {
|
|
|
133
312
|
delta: result.output,
|
|
134
313
|
});
|
|
135
314
|
}
|
|
136
|
-
|
|
137
|
-
await writeSseEvent(response, {
|
|
138
|
-
type: "TEXT_MESSAGE_END",
|
|
139
|
-
timestamp: createTimestamp(),
|
|
140
|
-
messageId: messageId,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
315
|
+
await closeOpenMessages();
|
|
143
316
|
await writeSseEvent(response, {
|
|
144
317
|
type: "RUN_FINISHED",
|
|
145
318
|
timestamp: createTimestamp(),
|
|
@@ -151,6 +324,7 @@ export async function serveAgUiOverHttp(runtime, options = {}) {
|
|
|
151
324
|
});
|
|
152
325
|
}
|
|
153
326
|
catch (error) {
|
|
327
|
+
await closeOpenMessages();
|
|
154
328
|
await writeSseEvent(response, {
|
|
155
329
|
type: "RUN_ERROR",
|
|
156
330
|
timestamp: createTimestamp(),
|