144 lines
14 KiB
TypeScript
144 lines
14 KiB
TypeScript
import assert from "node:assert/strict";
|
||
import { test } from "bun:test";
|
||
|
||
import { traceDisplayRows } from "../../../../tools/src/hwlab-cli/trace-renderer";
|
||
|
||
test("web trace rows use assistant content instead of terminal/result boilerplate", () => {
|
||
const events: Record<string, unknown>[] = [
|
||
{ seq: 1, label: "agentrun:request:accepted", status: "accepted", createdAt: "2026-06-04T08:31:11.567Z" },
|
||
{ seq: 22, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "你好,真实回复", createdAt: "2026-06-04T08:31:27.644Z" },
|
||
{ seq: 24, label: "agentrun:backend:turn/completed", status: "running", message: "turn/completed", createdAt: "2026-06-04T08:31:27.715Z" },
|
||
{ seq: 25, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "你好,真实回复", createdAt: "2026-06-04T08:31:27.721Z" },
|
||
{ seq: 27, label: "agentrun:terminal:completed", type: "result", status: "completed", terminal: true, message: "AgentRun terminal status completed", createdAt: "2026-06-04T08:31:27.736Z" },
|
||
{ seq: 31, label: "agentrun:result:completed", type: "result", status: "completed", terminal: true, message: "AgentRun result is ready for HWLAB short-connection polling.", createdAt: "2026-06-04T08:31:27.765Z" }
|
||
];
|
||
|
||
const rows = traceDisplayRows({ startedAt: "2026-06-04T08:31:11.567Z" }, events);
|
||
const terminalRows = rows.filter((row) => row.terminal === true);
|
||
|
||
assert.equal(terminalRows.length, 1);
|
||
assert.equal(terminalRows[0]?.body, "你好,真实回复");
|
||
assert.doesNotMatch(rows.map((row) => row.body ?? "").join("\n"), /AgentRun terminal status completed|short-connection polling/u);
|
||
});
|
||
|
||
test("web trace rows keep every assistant message and mark only the last one terminal", () => {
|
||
const events: Record<string, unknown>[] = [
|
||
{ seq: 1, label: "agentrun:request:accepted", status: "accepted", createdAt: "2026-06-04T12:13:58.212Z" },
|
||
{ seq: 42, label: "agentrun:assistant:message", type: "assistant", status: "running", messageIndex: 1, messageCount: 3, message: "我先读取 hwpod-cli 技能文档。", createdAt: "2026-06-04T12:14:55.199Z" },
|
||
{ seq: 43, label: "agentrun:assistant:message", type: "assistant", status: "running", messageIndex: 2, messageCount: 3, message: "`hwpod` 已就位,runtime 已锁定到 `hwlab-v02`。", createdAt: "2026-06-04T12:14:55.203Z" },
|
||
{ seq: 44, label: "agentrun:assistant:message", type: "assistant", status: "running", messageIndex: 3, messageCount: 3, message: "**可用 HWPOD 列表**\n\n- **hwpod-freq**\n- **hwpod-f103**", createdAt: "2026-06-04T12:14:55.208Z" },
|
||
{ seq: 49, label: "agentrun:backend:backend-turn-finished", status: "running", message: "backend-turn-finished", createdAt: "2026-06-04T12:14:55.231Z" },
|
||
{ seq: 50, label: "agentrun:result:completed", type: "result", status: "completed", terminal: true, message: "AgentRun result is ready for HWLAB short-connection polling.", createdAt: "2026-06-04T12:14:55.231Z" }
|
||
];
|
||
|
||
const rows = traceDisplayRows({ startedAt: "2026-06-04T12:13:58.212Z" }, events);
|
||
const assistantRows = rows.filter((row) => row.bodyFormat === "markdown");
|
||
const terminalRows = assistantRows.filter((row) => row.terminal === true);
|
||
const text = assistantRows.map((row) => row.body ?? "").join("\n");
|
||
|
||
assert.equal(assistantRows.length, 3);
|
||
assert.equal(terminalRows.length, 1);
|
||
assert.match(text, /我先读取 hwpod-cli/u);
|
||
assert.match(text, /runtime 已锁定到 `hwlab-v02`/u);
|
||
assert.match(text, /hwpod-f103/u);
|
||
assert.match(terminalRows[0]?.header ?? "", /助手消息 3\/3,轮次完成/u);
|
||
assert.doesNotMatch(JSON.stringify(rows), /backend-turn-finished|short-connection polling/u);
|
||
});
|
||
|
||
test("web trace rows do not fold progress assistant text into the final response (#934)", () => {
|
||
const events: Record<string, unknown>[] = [
|
||
{ seq: 1, label: "agentrun:request:accepted", status: "accepted", createdAt: "2026-06-05T13:39:36.000Z" },
|
||
{ seq: 10, label: "agentrun:assistant:message", type: "assistant", status: "running", messageIndex: 1, messageCount: 3, message: "Let me check the available HWPODs using the hwpod CLI tool.", createdAt: "2026-06-05T13:39:38.000Z" },
|
||
{ seq: 20, label: "agentrun:assistant:message", type: "assistant", status: "running", messageIndex: 2, messageCount: 3, message: "当前工作区还没有 .hwlab/hwpod-spec.yaml 文件,所以 hwpod inspect 无法获取可用 HWPOD 信息。", createdAt: "2026-06-05T13:39:40.000Z" },
|
||
{ seq: 30, label: "agentrun:assistant:message", type: "assistant", status: "completed", messageIndex: 3, messageCount: 3, replyAuthority: true, final: true, terminal: true, message: "当前工作区没有 .hwlab/hwpod-spec.yaml,也没有 .hwlab 目录或现成的 hwpod-spec 文件。\n\n目前可用的 HWPOD:0 个。", createdAt: "2026-06-05T13:39:44.000Z" },
|
||
{ seq: 34, label: "agentrun:result:completed", type: "result", status: "completed", terminal: true, message: "AgentRun result is ready for HWLAB short-connection polling.", createdAt: "2026-06-05T13:39:44.280Z" }
|
||
];
|
||
|
||
const rows = traceDisplayRows({ startedAt: "2026-06-05T13:39:36.000Z" }, events);
|
||
const assistantRows = rows.filter((row) => row.bodyFormat === "markdown");
|
||
const terminalRows = assistantRows.filter((row) => row.terminal === true);
|
||
|
||
assert.equal(assistantRows.length, 3);
|
||
assert.equal(terminalRows.length, 1);
|
||
assert.match(assistantRows[0]?.body ?? "", /Let me check/u);
|
||
assert.match(assistantRows[1]?.body ?? "", /当前工作区还没有/u);
|
||
assert.match(terminalRows[0]?.body ?? "", /目前可用的 HWPOD:0 个/u);
|
||
assert.doesNotMatch(terminalRows[0]?.body ?? "", /Let me check|当前工作区还没有/u);
|
||
});
|
||
|
||
test("web trace rows collapse tool start/completed and clean shell command escaping", () => {
|
||
const events: Record<string, unknown>[] = [
|
||
{ seq: 1, label: "agentrun:request:accepted", status: "accepted", createdAt: "2026-06-04T12:13:58.212Z" },
|
||
{ seq: 2, label: "agentrun:tool:item/started", type: "tool_call", toolName: "commandExecution", status: "started", itemId: "call_build", command: "/bin/sh -lc \"hwpod build --target Debug --reason \\\"agent 主动 build 复测\\\"\"", createdAt: "2026-06-04T12:14:00.000Z" },
|
||
{ seq: 3, label: "agentrun:tool:item/completed", type: "tool_call", toolName: "commandExecution", status: "completed", itemId: "call_build", command: "/bin/sh -lc \"hwpod build --target Debug --reason \\\"agent 主动 build 复测\\\"\"", stdoutSummary: "{\"ok\":true,\"action\":\"hwpod-cli.invoke\",\"status\":\"completed\"}", exitCode: 0, createdAt: "2026-06-04T12:14:01.000Z" }
|
||
];
|
||
|
||
const rows = traceDisplayRows({ startedAt: "2026-06-04T12:13:58.212Z" }, events);
|
||
const toolRows = rows.filter((row) => row.rowId.startsWith("tool:"));
|
||
const body = toolRows.map((row) => row.body ?? "").join("\n");
|
||
const text = JSON.stringify(toolRows);
|
||
|
||
assert.equal(toolRows.length, 1);
|
||
assert.match(body, /hwpod build --target Debug --reason "agent 主动 build 复测"/u);
|
||
assert.match(body, /hwpod-cli\.invoke/u);
|
||
assert.doesNotMatch(body, /\/bin\/sh -lc|\\"/u);
|
||
assert.match(body, /stdout:/u);
|
||
assert.match(body, /exitCode=0/u);
|
||
assert.doesNotMatch(text, /agentrun:tool:item\/started/u);
|
||
});
|
||
|
||
test("web trace rows dedupe AgentRun replayed source events and keep one tool row", () => {
|
||
const events: Record<string, unknown>[] = [
|
||
{ seq: 1, label: "agentrun:request:accepted", status: "accepted", createdAt: "2026-06-04T13:40:28.612Z" },
|
||
{ seq: 9, label: "agentrun:backend:run-claimed", type: "backend", status: "running", source: "agentrun", sourceSeq: 4, message: "run-claimed", createdAt: "2026-06-04T13:40:34.601Z" },
|
||
{ seq: 22, label: "agentrun:assistant:message", type: "assistant", status: "running", source: "agentrun", sourceSeq: 17, message: "我先确认 `hwpod` 可用。", createdAt: "2026-06-04T13:40:43.286Z" },
|
||
{ seq: 23, label: "item/commandExecution:started", type: "tool_call", toolName: "commandExecution", status: "started", source: "agentrun", sourceSeq: 18, itemId: "call_function_gsuuqbm1yp0s_1", command: "/bin/sh -lc 'command -v hwpod && hwpod inspect --dry-run'", createdAt: "2026-06-04T13:40:43.458Z" },
|
||
{ seq: 24, label: "agentrun:output:stdout", type: "output", status: "running", source: "agentrun", sourceSeq: 19, message: "raw stdout should stay inside the completed tool row", createdAt: "2026-06-04T13:40:44.852Z" },
|
||
{ seq: 25, label: "item/commandExecution:completed", type: "tool_call", toolName: "commandExecution", status: "completed", source: "agentrun", sourceSeq: 20, itemId: "call_function_gsuuqbm1yp0s_1", command: "/bin/sh -lc 'command -v hwpod && hwpod inspect --dry-run'", stdoutSummary: '{"ok":true,"action":"hwpod-cli.plan"}', exitCode: 0, createdAt: "2026-06-04T13:40:44.972Z" },
|
||
{ seq: 29, label: "agentrun:backend:run-claimed", type: "backend", status: "running", source: "agentrun", sourceSeq: 4, message: "run-claimed", createdAt: "2026-06-04T13:40:34.601Z" },
|
||
{ seq: 42, label: "agentrun:assistant:message", type: "assistant", status: "running", source: "agentrun", sourceSeq: 17, message: "我先确认 `hwpod` 可用。", createdAt: "2026-06-04T13:40:43.286Z" },
|
||
{ seq: 43, label: "item/commandExecution:started", type: "tool_call", toolName: "commandExecution", status: "started", source: "agentrun", sourceSeq: 18, itemId: "call_function_gsuuqbm1yp0s_1", command: "/bin/sh -lc 'command -v hwpod && hwpod inspect --dry-run'", createdAt: "2026-06-04T13:40:43.458Z" },
|
||
{ seq: 44, label: "agentrun:output:stdout", type: "output", status: "running", source: "agentrun", sourceSeq: 19, message: "raw stdout should stay inside the completed tool row", createdAt: "2026-06-04T13:40:44.852Z" },
|
||
{ seq: 45, label: "item/commandExecution:completed", type: "tool_call", toolName: "commandExecution", status: "completed", source: "agentrun", sourceSeq: 20, itemId: "call_function_gsuuqbm1yp0s_1", command: "/bin/sh -lc 'command -v hwpod && hwpod inspect --dry-run'", stdoutSummary: '{"ok":true,"action":"hwpod-cli.plan"}', exitCode: 0, createdAt: "2026-06-04T13:40:44.972Z" }
|
||
];
|
||
|
||
const rows = traceDisplayRows({ startedAt: "2026-06-04T13:40:28.612Z" }, events);
|
||
const text = JSON.stringify(rows);
|
||
const toolRows = rows.filter((row) => row.rowId === "tool:call_function_gsuuqbm1yp0s_1");
|
||
const assistantRows = rows.filter((row) => row.bodyFormat === "markdown");
|
||
|
||
assert.equal(toolRows.length, 1);
|
||
assert.equal(assistantRows.length, 1);
|
||
assert.equal(rows.filter((row) => /run-claimed/u.test(row.body ?? "")).length, 1);
|
||
assert.match(toolRows[0]?.body ?? "", /command -v hwpod && hwpod inspect --dry-run/u);
|
||
assert.match(toolRows[0]?.body ?? "", /hwpod-cli\.plan/u);
|
||
assert.doesNotMatch(toolRows[0]?.body ?? "", /\/bin\/sh -lc/u);
|
||
assert.doesNotMatch(text, /agentrun:output:stdout|raw stdout should stay inside/u);
|
||
});
|
||
|
||
test("web trace rows collapse duplicate assistant snapshots while preserving distinct messages", () => {
|
||
const events: Record<string, unknown>[] = [
|
||
{ seq: 1, label: "agentrun:request:accepted", status: "accepted", createdAt: "2026-06-04T13:56:01.440Z" },
|
||
{ seq: 22, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "我先确认 `hwpod` 在 PATH 中可用,再执行 `hwpod inspect --dry-run`。", createdAt: "2026-06-04T13:56:15.000Z" },
|
||
{ seq: 24, label: "item/commandExecution:completed", type: "tool_call", toolName: "commandExecution", status: "completed", itemId: "call_profile", command: "/bin/sh -lc 'hwpod inspect --dry-run'", stdoutSummary: '{"ok":true,"action":"hwpod-cli.plan"}', exitCode: 0, createdAt: "2026-06-04T13:56:20.000Z" },
|
||
{ seq: 34, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "ISSUE857_FINAL_MARKDOWN_OK\n\n| 检查项 | 结果 | 证据 |\n| --- | --- | --- |\n| CLI 可达 | PASS | `hwpod inspect --dry-run` |", createdAt: "2026-06-04T13:56:31.000Z" },
|
||
{ seq: 35, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "ISSUE857_FINAL_MARKDOWN_OK\n\n| 检查项 | 结果 | 证据 |\n| --- | --- | --- |\n| CLI 可达 | PASS | `hwpod inspect --dry-run` |\n\n- 已执行唯一真实工具命令。\n- 未触碰写路径。", createdAt: "2026-06-04T13:56:33.000Z" },
|
||
{ seq: 38, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "我先确认 `hwpod` 在 PATH 中可用,再执行 `hwpod inspect --dry-run`。", createdAt: "2026-06-04T13:56:33.100Z" },
|
||
{ seq: 39, label: "agentrun:assistant:message", type: "assistant", status: "completed", final: true, terminal: true, replyAuthority: true, message: "ISSUE857_FINAL_MARKDOWN_OK\n\n| 检查项 | 结果 | 证据 |\n| --- | --- | --- |\n| CLI 可达 | PASS | `hwpod inspect --dry-run` |\n\n- 已执行唯一真实工具命令。\n- 未触碰写路径。", createdAt: "2026-06-04T13:56:33.200Z" },
|
||
{ seq: 40, label: "agentrun:result:completed", type: "result", status: "completed", terminal: true, message: "AgentRun result is ready for HWLAB short-connection polling.", createdAt: "2026-06-04T13:56:33.300Z" }
|
||
];
|
||
|
||
const rows = traceDisplayRows({ startedAt: "2026-06-04T13:56:01.440Z" }, events);
|
||
const assistantRows = rows.filter((row) => row.bodyFormat === "markdown");
|
||
const finalRows = assistantRows.filter((row) => /ISSUE857_FINAL_MARKDOWN_OK/u.test(row.body ?? ""));
|
||
const progressRows = assistantRows.filter((row) => /我先确认 `hwpod`/u.test(row.body ?? ""));
|
||
const toolRows = rows.filter((row) => row.rowId === "tool:call_profile");
|
||
|
||
assert.equal(toolRows.length, 1);
|
||
assert.equal(progressRows.length, 1);
|
||
assert.equal(finalRows.length, 1);
|
||
assert.equal(finalRows[0]?.terminal, true);
|
||
assert.match(finalRows[0]?.body ?? "", /未触碰写路径/u);
|
||
assert.doesNotMatch(JSON.stringify(rows), /short-connection polling/u);
|
||
});
|