import assert from "node:assert/strict"; import test from "node:test"; import type { AgentRunProvenance, ChatMessage, TraceEvent } from "../src/types/index.ts"; import { canCancelMessage, canRetryMessage, messageTraceId, renderSafeMarkdown, traceEventBody, traceEventLabel, traceIdentityText, visibleTraceEvents } from "../src/components/workbench/message-rendering.ts"; import { assistantTextFromTraceEvents, mergeRunnerTrace } from "../src/composables/useTraceSubscription.ts"; import { traceDisplayRows, traceNoiseEventCount } from "../../../tools/src/hwlab-cli/trace-renderer.ts"; test("R1 markdown rendering keeps structure and strips unsafe HTML", () => { const html = renderSafeMarkdown("# 标题\n\n- 项目\n\n\n\n[link](https://example.com)"); assert.match(html, /

标题<\/h1>/u); assert.match(html, /
  • 项目<\/li>/u); assert.match(html, /href="https:\/\/example.com"/u); assert.doesNotMatch(html, / { const running = agentMessage({ status: "running", traceId: "trc_running", retryInput: "retry me" }); assert.equal(messageTraceId(running), "trc_running"); assert.equal(canCancelMessage(running), true); assert.equal(canRetryMessage(running), true); const completed = agentMessage({ status: "completed", traceId: "trc_done", retryInput: "retry me" }); assert.equal(canCancelMessage(completed), false); assert.equal(canRetryMessage(completed), false); const failed = agentMessage({ status: "failed", traceId: "trc_failed", retryInput: "retry me" }); assert.equal(canCancelMessage(failed), false); assert.equal(canRetryMessage(failed), true); }); test("R1 trace rendering hides chunk noise without dropping meaningful events", () => { const events: TraceEvent[] = [ { label: "assistant:delta", message: "token" }, { label: "command:completed", command: "npm test", exitCode: 0, elapsedMs: 42 }, { type: "assistant_message", text: "final", final: true } ]; const compact = visibleTraceEvents(events, true); assert.equal(compact.length, 2); assert.equal(traceEventLabel(compact[0]!), "command:completed"); assert.equal(traceEventBody(compact[0]!), "npm test"); assert.equal(traceEventLabel(compact[1]!), "assistant_message"); assert.equal(traceEventBody(compact[1]!), "final"); assert.equal(visibleTraceEvents(events, false).length, 3); }); test("R1 shared trace renderer suppresses pure AgentRun backend noise", () => { const events = Array.from({ length: 12 }, (_, index) => ({ label: index % 3 === 0 ? "agentrun:backend:run-created" : index % 3 === 1 ? "agentrun:backend:resource-bundle-materialized" : "agentrun:backend:mcpServer/startupStatus/updated", status: "running", createdAt: "2026-06-15T02:00:00.000Z" })); const rows = traceDisplayRows({ traceId: "trc_noise", status: "running" }, events); assert.equal(traceNoiseEventCount(events), 12); assert.equal(rows.length, 0); }); test("R1 shared trace renderer starts at the first user-meaningful event", () => { const rows = traceDisplayRows({ traceId: "trc_ok", status: "completed" }, [ { label: "agentrun:request:accepted", status: "accepted" }, { label: "agentrun:runner-job:created", status: "running", message: "runner created" }, { label: "agentrun:backend:resource-bundle-materialized", status: "running", source: "agentrun", sourceSeq: 1, message: "bundle" }, { label: "agentrun:assistant:message", type: "assistant", message: "OK", source: "agentrun", sourceSeq: 2 } ]); assert.equal(rows.length, 1); assert.equal(rows[0]?.body, "OK"); assert.doesNotMatch(rows[0]?.header ?? "", /runner-job|resource-bundle/u); }); test("R1 shared trace renderer suppresses AgentRun reuse diagnostics", () => { const rows = traceDisplayRows({ traceId: "trc_reuse", status: "running" }, [ { label: "agentrun:run:reused", status: "reused", message: "run reused" }, { label: "agentrun:runner-job:ensured", status: "runner-job-ensured", message: "runner ensured" }, { label: "agentrun:assistant:message", type: "assistant", message: "OK", source: "agentrun", sourceSeq: 3 } ]); assert.equal(rows.length, 1); assert.equal(rows[0]?.body, "OK"); assert.doesNotMatch(rows[0]?.header ?? "", /reused|ensured|runner-job/u); }); test("R1 trace API snapshots stay authoritative over compact result traces", () => { const previous = { traceId: "trc_merge", status: "running", eventSource: "trace-api", events: [ { seq: 1, label: "agentrun:backend:resource-bundle-materialized" }, { seq: 2, label: "agentrun:assistant:message", message: "OK" } ], eventCount: 2 } as NonNullable; const next = { traceId: "trc_merge", status: "completed", eventsCompacted: true, events: [{ label: "trace:compacted", message: "compacted" }], eventCount: 99 } as NonNullable; const merged = mergeRunnerTrace(previous, next); assert.equal(merged.status, "completed"); assert.equal(merged.eventCount, 2); assert.equal(merged.events?.length, 2); assert.equal(merged.events?.[1]?.message, "OK"); }); test("R1 trace event assistant text prefers authoritative final assistant message", () => { const events: TraceEvent[] = [ { seq: 1, label: "agentrun:backend:resource-bundle-materialized", message: "bundle ready" }, { seq: 2, label: "agentrun:assistant:message", type: "assistant", status: "running", replyAuthority: false, final: false, message: "我先检查当前状态。" }, { seq: 3, label: "agentrun:assistant:message", type: "assistant", status: "completed", terminal: true, message: "OK" } ]; assert.equal(assistantTextFromTraceEvents(events), "OK"); }); test("R1 status summary keeps the restored 23-row floor", () => { const message = agentMessage({ status: "failed", traceId: "trc_summary", conversationId: "cnv_summary", sessionId: "ses_summary", threadId: "thr_summary", runnerTrace: { traceId: "trc_summary", status: "failed", eventCount: 2, eventsCompacted: false, fullTraceLoaded: true, lastEventLabel: "assistant:completed", waitingFor: "result", runnerKind: "agentrun", sessionMode: "long-lived", agentRun: { runId: "run_1", commandId: "cmd_1", attemptId: "att_1", runnerId: "runner_1", jobName: "job_1", namespace: "hwlab-v03", terminalStatus: "failed" } }, error: { code: "provider_timeout", category: "provider", providerStatus: 504 } }); const values = [ message.status, message.runnerTrace?.status, message.traceId, message.conversationId, message.sessionId, message.threadId, message.runnerTrace?.runnerKind, message.runnerTrace?.sessionMode, message.runnerTrace?.waitingFor, message.runnerTrace?.lastEventLabel, String(message.runnerTrace?.eventCount), String(message.runnerTrace?.eventsCompacted), message.runnerTrace?.fullTraceLoaded === true ? "loaded" : "unverified", asAgentRun(message.runnerTrace?.agentRun)?.runId, asAgentRun(message.runnerTrace?.agentRun)?.commandId, asAgentRun(message.runnerTrace?.agentRun)?.attemptId, asAgentRun(message.runnerTrace?.agentRun)?.runnerId, asAgentRun(message.runnerTrace?.agentRun)?.jobName, asAgentRun(message.runnerTrace?.agentRun)?.namespace, asAgentRun(message.runnerTrace?.agentRun)?.terminalStatus, message.error?.code, message.error?.category, String(message.error?.providerStatus) ]; assert.equal(values.length, 23); assert.equal(values.every((value) => value != null && value !== ""), true); assert.match(traceIdentityText(message), /traceId=trc_summary/u); assert.match(traceIdentityText(message), /sessionId=ses_summary/u); }); function agentMessage(patch: Partial): ChatMessage { return { id: "msg_agent", role: "agent", title: "Code Agent", text: "done", status: "completed", createdAt: "2026-01-01T00:00:00.000Z", ...patch } as ChatMessage; } function asAgentRun(value: unknown): AgentRunProvenance | null { return value && typeof value === "object" ? value as AgentRunProvenance : null; }