From 936884bdc17cbdd7460cfd5254b2f38a86fa6676 Mon Sep 17 00:00:00 2001 From: Codex Date: Tue, 2 Jun 2026 00:48:38 +0800 Subject: [PATCH] fix: reduce AgentRun trace noise for HWLAB CLI --- internal/cloud/code-agent-agentrun-adapter.ts | 9 ++++++ internal/cloud/code-agent-trace-store.test.ts | 14 ++++++++ internal/cloud/code-agent-trace-store.ts | 12 +++++-- internal/cloud/server-agent-chat.test.ts | 1 + web/hwlab-cloud-web/app-trace.test.ts | 32 +++++++++++++------ web/hwlab-cloud-web/app-trace.ts | 22 +++++++++++-- 6 files changed, 75 insertions(+), 15 deletions(-) diff --git a/internal/cloud/code-agent-agentrun-adapter.ts b/internal/cloud/code-agent-agentrun-adapter.ts index 577ca3c7..3b187136 100644 --- a/internal/cloud/code-agent-agentrun-adapter.ts +++ b/internal/cloud/code-agent-agentrun-adapter.ts @@ -665,11 +665,13 @@ function agentRunResultToCodeAgentPayload({ base, result, traceStore, traceId }) } const now = nowIso(); const runnerTrace = traceStore.snapshot(traceId, agentRunTraceMeta({}, {})); + const terminalEventCreatedAt = agentRunResultTraceCreatedAt(runnerTrace, now); if (terminalStatus === "completed" && String(result?.reply ?? "").trim()) { traceStore.append(traceId, { type: "result", status: "completed", label: "agentrun:result:completed", + createdAt: terminalEventCreatedAt, message: "AgentRun result is ready for HWLAB short-connection polling.", runId: base.agentRun.runId, commandId: base.agentRun.commandId, @@ -706,6 +708,7 @@ function agentRunResultToCodeAgentPayload({ base, result, traceStore, traceId }) type: "result", status: canceled ? "canceled" : "failed", label: `agentrun:result:${canceled ? "canceled" : terminalStatus || "failed"}`, + createdAt: terminalEventCreatedAt, errorCode: canceled ? "agentrun_canceled" : result?.failureKind ?? "agentrun_failed", message: result?.failureMessage ?? result?.blocker?.message ?? (canceled ? "AgentRun command was canceled." : "AgentRun command failed without a completed reply."), runId: base.agentRun.runId, @@ -753,6 +756,11 @@ function agentRunResultToCodeAgentPayload({ base, result, traceStore, traceId }) }; } +function agentRunResultTraceCreatedAt(runnerTrace = {}, fallback) { + const last = String(runnerTrace?.lastEvent?.createdAt ?? runnerTrace?.updatedAt ?? ""); + return Number.isFinite(Date.parse(last)) ? last : fallback; +} + function decorateAgentRunRunningResult({ base, mapping, traceStore, traceId }) { return { ...base, @@ -780,6 +788,7 @@ function mapAgentRunEvent(event, mapping = {}) { if (!event || typeof event !== "object") return null; const payload = event.payload && typeof event.payload === "object" ? event.payload : {}; const base = { + createdAt: event.createdAt ?? null, source: "agentrun", sourceSeq: event.seq ?? null, runId: event.runId ?? mapping.runId ?? null, diff --git a/internal/cloud/code-agent-trace-store.test.ts b/internal/cloud/code-agent-trace-store.test.ts index d4b4bcd6..4c26a7a7 100644 --- a/internal/cloud/code-agent-trace-store.test.ts +++ b/internal/cloud/code-agent-trace-store.test.ts @@ -35,3 +35,17 @@ test("code agent trace store retains six thousand regular events", () => { assert.equal(snapshot.events[0].label, "event:1"); assert.equal(snapshot.events.at(-1).label, "event:6000"); }); + +test("code agent trace store preserves upstream event timestamps", () => { + const traceStore = createCodeAgentTraceStore({ maxEvents: 10 }); + const traceId = "trc_trace-store-upstream-time"; + traceStore.append(traceId, { + type: "backend", + status: "running", + label: "agentrun:backend:turn/completed", + createdAt: "2026-06-01T16:35:40.939Z" + }, { now: "2026-06-01T16:35:56.171Z" }); + const snapshot = traceStore.snapshot(traceId); + assert.equal(snapshot.events[0].createdAt, "2026-06-01T16:35:40.939Z"); + assert.equal(snapshot.elapsedMs, 0); +}); diff --git a/internal/cloud/code-agent-trace-store.ts b/internal/cloud/code-agent-trace-store.ts index 88c2f0c7..1e13a633 100644 --- a/internal/cloud/code-agent-trace-store.ts +++ b/internal/cloud/code-agent-trace-store.ts @@ -306,6 +306,7 @@ function normalizeTraceEvent(event, { traceId, seq, now, fallbackRunnerKind } = const status = safeToken(event.status ?? "observed"); const toolName = safeText(event.toolName ?? event.name, 120); const label = safeText(event.label, 180) || labelFor({ type, status, toolName }); + const eventCreatedAt = safeTimestamp(event.createdAt) ?? timestampFor(now); return dropUndefined({ seq, traceId, @@ -313,7 +314,7 @@ function normalizeTraceEvent(event, { traceId, seq, now, fallbackRunnerKind } = stage: safeToken(event.stage ?? type), status, label, - createdAt: timestampFor(now), + createdAt: eventCreatedAt, elapsedMs: typeof event.elapsedMs === "number" ? Math.max(0, Math.trunc(event.elapsedMs)) : null, runnerKind: safeText(event.runnerKind ?? fallbackRunnerKind, 140), source: safeText(event.source, 120), @@ -362,7 +363,7 @@ function normalizeAssistantDelta(delta, { traceId, now, fallbackRunnerKind } = { traceId, itemId: safeText(delta.itemId, 180), chunk, - createdAt: timestampFor(now), + createdAt: safeTimestamp(delta.createdAt) ?? timestampFor(now), runnerKind: safeText(delta.runnerKind ?? fallbackRunnerKind, 140), sessionId: safeText(delta.sessionId, 180), sessionStatus: safeText(delta.sessionStatus, 80), @@ -374,6 +375,13 @@ function normalizeAssistantDelta(delta, { traceId, now, fallbackRunnerKind } = { }); } +function safeTimestamp(value) { + const text = safeText(value, 80); + if (!text) return null; + const ms = Date.parse(text); + return Number.isFinite(ms) ? new Date(ms).toISOString() : null; +} + function assistantStreamsSnapshot(trace) { if (!(trace?.assistantStreams instanceof Map)) return []; return [...trace.assistantStreams.values()].map((stream) => ({ ...stream })); diff --git a/internal/cloud/server-agent-chat.test.ts b/internal/cloud/server-agent-chat.test.ts index 94be3444..ce4f60cf 100644 --- a/internal/cloud/server-agent-chat.test.ts +++ b/internal/cloud/server-agent-chat.test.ts @@ -355,6 +355,7 @@ test("cloud api /v1/agent/chat delegates v0.2 turns to AgentRun v0.1 over adapte assert.ok(traceBody.events.some((event) => event.label === "agentrun:assistant:message")); const commandTraceEvent = traceBody.events.find((event) => event.label === "item/commandExecution:completed"); assert.equal(commandTraceEvent.toolName, "commandExecution"); + assert.equal(commandTraceEvent.createdAt, "2026-06-01T00:00:00.500Z"); assert.match(commandTraceEvent.command, /device-pod-cli\.ts profile list/u); assert.match(commandTraceEvent.stdoutSummary, /profile\.list/u); diff --git a/web/hwlab-cloud-web/app-trace.test.ts b/web/hwlab-cloud-web/app-trace.test.ts index 95d63e5e..3d55e62f 100644 --- a/web/hwlab-cloud-web/app-trace.test.ts +++ b/web/hwlab-cloud-web/app-trace.test.ts @@ -70,12 +70,18 @@ test("trace display rows render assistant item text when stream snapshot is abse test("trace display rows suppress low-value AgentRun backend noise", () => { const events = [ event(1, "agentrun:request:accepted", { message: "accepted" }), - event(2, "agentrun:backend:command-created"), - event(3, "agentrun:backend:thread/status/changed"), - event(4, "agentrun:backend:thread/tokenUsage/updated"), - event(5, "agentrun:backend:account/rateLimits/updated"), - event(6, "agentrun:backend:codex-app-server-starting"), - commandEvent(7, "item/commandExecution:completed", { + event(2, "agentrun:run:reused"), + event(3, "agentrun:command:created"), + event(4, "agentrun:runner-job:reused"), + event(5, "agentrun:backend:command-created"), + event(6, "agentrun:backend:thread/status/changed"), + event(7, "agentrun:backend:thread/tokenUsage/updated"), + event(8, "agentrun:backend:account/rateLimits/updated"), + event(9, "agentrun:backend:codex-app-server-starting"), + event(10, "agentrun:backend:thread/goal/cleared"), + event(11, "agentrun:backend:item/agentMessage:started"), + event(12, "agentrun:backend:item/agentMessage:completed"), + commandEvent(13, "item/commandExecution:completed", { itemId: "call_1", command: "/bin/sh -lc 'bun tools/device-pod-cli.ts profile list --api-base-url http://74.48.78.17:19666'", status: "completed", @@ -83,24 +89,30 @@ test("trace display rows suppress low-value AgentRun backend noise", () => { durationMs: 708, stdoutSummary: '{"ok":true,"action":"profile.list","body":{"devicePods":[{"devicePodId":"D601-F103-V2"}]}}' }), - event(8, "agentrun:output:stdout", { + event(14, "agentrun:output:stdout", { type: "output", status: "running", message: '{"ok":true,"action":"profile.list"}' }), - event(9, "agentrun:assistant:message", { + event(15, "agentrun:assistant:message", { type: "assistant", status: "running", message: "当前可见 1 个 device-pod。" }), - event(10, "agentrun:terminal:completed", { type: "result", terminal: true, status: "completed" }) + event(16, "agentrun:terminal:completed", { type: "result", terminal: true, status: "completed" }) ]; const rows = traceDisplayRows({ traceId: "trc_agentrun_noise", events }, events); const text = rows.map((row) => `${row.header}\n${row.body ?? ""}`).join("\n---\n"); - assert.equal(traceNoiseEventCount(events), 4); + assert.equal(traceNoiseEventCount(events), 12); + assert.equal(/agentrun:request:accepted/u.test(text), false); + assert.equal(/agentrun:run:reused/u.test(text), false); + assert.equal(/runner-job:reused/u.test(text), false); assert.equal(/thread\/status\/changed/u.test(text), false); + assert.equal(/thread\/goal\/cleared/u.test(text), false); + assert.equal(/item\/agentMessage/u.test(text), false); + assert.equal(/terminal completed/u.test(text), false); assert.equal(/tokenUsage/u.test(text), false); assert.match(text, /工具调用/u); assert.match(text, /device-pod-cli\.ts profile list/u); diff --git a/web/hwlab-cloud-web/app-trace.ts b/web/hwlab-cloud-web/app-trace.ts index 2f61c75c..d152db57 100644 --- a/web/hwlab-cloud-web/app-trace.ts +++ b/web/hwlab-cloud-web/app-trace.ts @@ -595,7 +595,7 @@ function traceRequestSummaryRow(trace, event, promptEvent = event) { return { rowId: `trace-request:${event.seq ?? "accepted"}`, seq: event.seq ?? null, - tone: "warn", + tone: traceEventTone(event), header: `${clock} 请求接受${prompt ? `,提示词:"${compactTraceOneLine(prompt, 120)}"` : ""}`, body: null }; @@ -923,7 +923,8 @@ function isTerminalAssistantTraceEvent(event) { function isRequestTraceEvent(event) { const label = String(event?.label ?? ""); - return label === "request:accepted-short-connection" || + return label === "agentrun:request:accepted" || + label === "request:accepted-short-connection" || label === "request:accepted" || label === "request:received"; } @@ -950,6 +951,7 @@ function traceNonEmptyString(value) { function isNoisyTraceEvent(event) { const label = String(event?.label ?? ""); + if (isAgentRunLifecycleNoiseEvent(event)) return true; if (isToolOutputChunkTraceEvent(event)) return false; if (isCommandExecutionTraceEvent(event) || isAssistantMessageTraceEvent(event)) return false; if (isRequestTraceEvent(event) || isSetupTraceEvent(event) || isCompletionTraceEvent(event)) return false; @@ -960,8 +962,22 @@ function isNoisyTraceEvent(event) { return false; } +function isAgentRunLifecycleNoiseEvent(event) { + const label = String(event?.label ?? ""); + if (!label.startsWith("agentrun:")) return false; + const status = String(event?.status ?? "").toLowerCase(); + if (["failed", "blocked", "canceled", "cancelled", "error", "timeout", "degraded"].includes(status)) return false; + if (event?.errorCode) return false; + return label === "agentrun:request:accepted" || + label === "agentrun:run:reused" || + label === "agentrun:command:created" || + label === "agentrun:runner-job:reused" || + label === "agentrun:terminal:completed" || + /^agentrun:backend:(item\/agentMessage:(started|completed)|thread\/goal\/cleared)$/u.test(label); +} + function isLowValueAgentRunBackendEvent(label) { - return /^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|session-updated|command-terminal)$/u.test(String(label ?? "")); + return /^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|session-updated|command-terminal|item\/agentMessage:(started|completed)|thread\/goal\/cleared)$/u.test(String(label ?? "")); } function isToolOutputChunkTraceEvent(event) {