Files
pikasTech-HWLAB/web/hwlab-cloud-web/src/state/trace-renderer.test.ts
T
2026-06-05 22:30:29 +08:00

144 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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目前可用的 HWPOD0 个。", 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 ?? "", /目前可用的 HWPOD0 个/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);
});