fix(v02): dedupe replayed trace tool rows (#860)

Co-authored-by: Codex Agent <codex@hwlab.local>
This commit is contained in:
Lyon
2026-06-04 21:49:57 +08:00
committed by GitHub
parent 7652b39235
commit c4ff24f60a
2 changed files with 50 additions and 0 deletions
+21
View File
@@ -12,15 +12,27 @@ type TraceEvent = Record<string, unknown>;
export function traceDisplayRows(trace: Record<string, unknown> = {}, events: TraceEvent[] = []): TraceEventRow[] {
const rows: TraceEventRow[] = [];
const renderedSourceEvents = new Set<string>();
const renderedToolIdentities = new Set<string>();
let requestRendered = false;
let setupRendered = false;
let lastAssistantRowIndex = -1;
let completionEvent: TraceEvent | null = null;
for (let index = 0; index < events.length; index += 1) {
const event = events[index];
const sourceEventKey = traceSourceEventKey(event);
if (sourceEventKey) {
if (renderedSourceEvents.has(sourceEventKey)) continue;
renderedSourceEvents.add(sourceEventKey);
}
if (!event || isNoisyTraceEvent(event)) continue;
if (isToolTraceEvent(event)) {
if (isSupersededToolStart(event, events, index)) continue;
const identity = toolIdentity(event);
if (identity) {
if (renderedToolIdentities.has(identity)) continue;
renderedToolIdentities.add(identity);
}
rows.push(traceToolCallRow(trace, event));
continue;
}
@@ -222,6 +234,14 @@ function toolIdentity(event: TraceEvent): string | null {
return nonEmptyString(event.itemId ?? event.id ?? event.command);
}
function traceSourceEventKey(event: TraceEvent | null | undefined): string | null {
if (!event || typeof event !== "object") return null;
const source = nonEmptyString(event.source);
const sourceSeq = nonEmptyString(event.sourceSeq);
if (!source || !sourceSeq) return null;
return `${source}:${sourceSeq}`;
}
function isSupersededToolStart(event: TraceEvent, events: TraceEvent[], index: number): boolean {
if (!isToolStartTraceEvent(event)) return false;
const identity = toolIdentity(event);
@@ -233,6 +253,7 @@ function isNoisyTraceEvent(event: TraceEvent): boolean {
const label = String(event.label ?? "");
if (isRequestTraceEvent(event) || isSetupTraceEvent(event) || isCompletionTraceEvent(event) || isTerminalAssistantTraceEvent(event) || isAssistantTraceEvent(event)) return false;
if (/token_count|outputDelta:chunk/iu.test(label)) return true;
if (/^agentrun:output:(stdout|stderr)$/u.test(label)) return true;
if (/^agentrun:backend:(run-created|command-created|runner-job-created|thread\/status\/changed|thread\/tokenUsage\/updated|account\/rateLimits\/updated|remoteControl\/status\/changed|configWarning|codex-app-server-closed|codex-app-server-notifications-suppressed|session-updated|command-terminal|backend-turn-finished|item\/agentMessage:(started|completed)|thread\/goal\/cleared)$/u.test(label)) return true;
return event.type === "event" && !event.outputSummary && !event.message && !event.errorCode;
}
@@ -65,3 +65,32 @@ test("web trace rows collapse tool start/completed and clean shell command escap
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 profile list'", 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 profile list'", stdoutSummary: '{"ok":true,"action":"profile.list"}', 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 profile list'", 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 profile list'", stdoutSummary: '{"ok":true,"action":"profile.list"}', 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 profile list/u);
assert.match(toolRows[0]?.body ?? "", /profile\.list/u);
assert.doesNotMatch(toolRows[0]?.body ?? "", /\/bin\/sh -lc/u);
assert.doesNotMatch(text, /agentrun:output:stdout|raw stdout should stay inside/u);
});