fix: polish caserun aggregate trace markdown

This commit is contained in:
Codex Agent
2026-06-08 13:04:29 +08:00
parent 225269b5ad
commit a309f06867
3 changed files with 52 additions and 15 deletions
+11 -5
View File
@@ -684,14 +684,16 @@ test("case aggregate writes one registry markdown entry without grading or broad
artifacts: [],
decisions: { runnerPostAgentCompileCheck: "recorded" }
});
const longTraceCommand = "hwpod workspace cat projects/01_baseline/Middlewares/Arm-2D/Library/include/arm_2d_types.h --spec .hwlab/hwpod-spec.yaml 2>&1 | grep -A 20 \"typedef struct arm_2d_tile_t\" 2>/dev/null || hwpod workspace cat projects/01_baseline/Middlewares/Arm-2D/Library/include/arm_2d_types.h --spec .hwlab/hwpod-spec.yaml 2>&1 | grep";
await writeJsonFile(path.join(registryRunDir, "agent-messages.json"), {
renderer: "tools/src/hwlab-cli/trace-renderer:traceDisplayRows",
sourceEventCount: 3,
renderedRowCount: 3,
sourceEventCount: 4,
renderedRowCount: 4,
rows: [
{ rowId: "row-1", seq: 1, header: "请求接受", body: "CaseRun started" },
{ rowId: "row-2", seq: 2, header: "ok commandExecution", body: "hwpod build --spec .hwlab/hwpod-spec.yaml\nexitCode=0" },
{ rowId: "row-3", seq: 3, header: "助手最终消息", terminal: true, bodyFormat: "markdown", body: "Build completed" }
{ rowId: "row-3", seq: 3, header: "ok commandExecution", body: `${longTraceCommand} stdout: first line\nsecond line exitCode=0` },
{ rowId: "row-4", seq: 4, header: "助手最终消息", terminal: true, bodyFormat: "markdown", body: "Build completed" }
]
});
await writeJsonFile(path.join(registryRunDir, "artifact-manifest.json"), {
@@ -743,8 +745,12 @@ test("case aggregate writes one registry markdown entry without grading or broad
assert.match(aggregate, /## Prompt/u);
assert.match(aggregate, /## Trace/u);
assert.doesNotMatch(aggregate, / 2\. ok commandExecution/u);
assert.match(aggregate, /<details>\n<summary> hwpod build --spec \.hwlab\/hwpod-spec\.yaml<\/summary>/u);
assert.match(aggregate, /<summary> hwpod build --spec \.hwlab\/hwpod-spec\.yaml<\/summary>[\s\S]*```text\nhwpod build --spec \.hwlab\/hwpod-spec\.yaml\nexitCode=0[\s\S]*<\/details>\n\nBuild completed\n\n## Final Response/u);
assert.match(aggregate, /\*\*CaseRun started\*\*/u);
assert.match(aggregate, /- <details>\n <summary> hwpod build --spec \.hwlab\/hwpod-spec\.yaml<\/summary>/u);
assert.match(aggregate, / ```text\n hwpod build --spec \.hwlab\/hwpod-spec\.yaml\n exitCode=0[\s\S]* <\/details>/u);
assert.match(aggregate, /<summary> hwpod workspace cat projects\/01_baseline\/Middlewares\/Arm-2D\/Library\/include\/arm_2d_type\.\.\.<\/summary>/u);
assert.match(aggregate, / ```text\n hwpod workspace cat [^\n]+\n stdout:\n first line\n second line\n exitCode=0[\s\S]* <\/details>\n\n\*\*Build completed\*\*\n\n## Final Response/u);
for (const [, summary] of aggregate.matchAll(/<summary>([\s\S]*?)<\/summary>/gu)) assert.doesNotMatch(summary, /\n|stdout:|stderr:|exitCode=|grep -A 20/u);
assert.doesNotMatch(aggregate, /<summary>.*/u);
assert.match(aggregate, /## Final Response/u);
assert.match(aggregate, /## Diff/u);
+39 -8
View File
@@ -86,21 +86,21 @@ export function renderTraceRowsMarkdown(rows: TraceEventRow[] = []): string {
export function renderTraceRowMarkdown(row: TraceEventRow, index = 1): string {
if (!isToolTraceRow(row)) return renderTraceMessageRowMarkdown(row);
const body = renderTraceRowDetailBody(row) || row.header || `trace row ${index}`;
const body = renderTraceToolDetailBody(row) || row.header || `trace row ${index}`;
return [
`<details>`,
`<summary>${escapeHtml(`已运行 ${traceToolSummary(row)}`)}</summary>`,
`- <details>`,
` <summary>${escapeHtml(`已运行 ${traceToolSummary(row)}`)}</summary>`,
``,
traceMarkdownFence(row.bodyFormat === "markdown" ? "markdown" : "text", body),
indentTraceMarkdownBlock(traceMarkdownFence(row.bodyFormat === "markdown" ? "markdown" : "text", body)),
``,
`</details>`
` </details>`
].join("\n");
}
function renderTraceMessageRowMarkdown(row: TraceEventRow): string {
const body = row.body?.trimEnd();
if (body) return body;
return row.header || `_No readable trace row body._`;
if (body) return traceBoldMarkdown(body);
return traceBoldMarkdown(row.header || `_No readable trace row body._`);
}
function isToolTraceRow(row: TraceEventRow): boolean {
@@ -109,7 +109,7 @@ function isToolTraceRow(row: TraceEventRow): boolean {
function traceToolSummary(row: TraceEventRow): string {
const firstLine = traceMarkdownPreviewLines(row.body).find((line) => !/^(stdout|stderr|exitCode|rowId|terminal):/iu.test(line.trim()));
return compactTraceOneLine(stripTraceToolOutputFromSummary(firstLine || row.header || "tool call"), 320);
return compactTraceOneLine(stripTraceToolOutputFromSummary(firstLine || row.header || "tool call"), 90);
}
function stripTraceToolOutputFromSummary(value: string): string {
@@ -483,6 +483,37 @@ function renderTraceRowDetailBody(row: TraceEventRow): string {
].filter(Boolean).join("\n");
}
function renderTraceToolDetailBody(row: TraceEventRow): string {
return normalizeTraceToolDetailBody(renderTraceRowDetailBody(row));
}
function normalizeTraceToolDetailBody(value: string): string {
return String(value ?? "")
.replace(/\r\n|\r/gu, "\n")
.replace(/[^\S\n]+(stdout:)[^\S\n]*/giu, "\n$1\n")
.replace(/[^\S\n]+(stderr:)[^\S\n]*/giu, "\n$1\n")
.replace(/[^\S\n]+(exitCode=)/giu, "\n$1")
.replace(/\n{3,}/gu, "\n\n")
.trimEnd();
}
function traceBoldMarkdown(value: string): string {
const body = String(value ?? "").trimEnd();
if (!body) return "";
return body.split("\n").map((line) => {
if (!line.trim()) return "";
return `**${escapeMarkdownStrongText(line)}**`;
}).join("\n");
}
function escapeMarkdownStrongText(value: string): string {
return escapeHtml(value).replace(/\\/gu, "\\\\").replace(/\*/gu, "\\*");
}
function indentTraceMarkdownBlock(value: string): string {
return String(value).split("\n").map((line) => ` ${line}`).join("\n");
}
function escapeHtml(value: string): string {
return String(value).replace(/&/gu, "&amp;").replace(/</gu, "&lt;").replace(/>/gu, "&gt;");
}