From cd981355c626a65b3be5b330c1b950bd9d199717 Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Sun, 21 Jun 2026 00:56:12 +0800 Subject: [PATCH] fix(workbench): keep per-turn message timing isolated (#1768) --- internal/cloud/server-workbench-http.ts | 4 +++- internal/cloud/workbench-projection-writer.ts | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/internal/cloud/server-workbench-http.ts b/internal/cloud/server-workbench-http.ts index 8a7c6757..b60671ad 100644 --- a/internal/cloud/server-workbench-http.ts +++ b/internal/cloud/server-workbench-http.ts @@ -585,7 +585,9 @@ function factMessageDto(message, parts = [], facts = {}) { const traceId = safeTraceId(message?.traceId) ?? null; const role = textValue(message?.role) || "agent"; const status = normalizeStatus(message?.status); - const timing = factTimingProjection(message, status); + const turn = traceId ? factTurnForTrace(facts, traceId) : null; + const checkpoint = traceId ? factCheckpointForTrace(facts, traceId) : null; + const timing = factTimingProjection(isAssistantLikeRole(role) ? turn ?? checkpoint ?? message : message, status); const text = projectionText(message?.text, message?.content, message?.message, message?.finalResponse); const normalizedParts = parts.length > 0 ? [...parts].sort(compareFactPartsAsc).map((part) => factPartDto(part, messageId, traceId)).filter(Boolean) diff --git a/internal/cloud/workbench-projection-writer.ts b/internal/cloud/workbench-projection-writer.ts index 32e368bc..fdebce3c 100644 --- a/internal/cloud/workbench-projection-writer.ts +++ b/internal/cloud/workbench-projection-writer.ts @@ -348,7 +348,11 @@ function normalizeMessageFact(message = {}, index, { traceId, sessionId, convers const role = textValue(message.role) || "agent"; const status = normalizeWorkbenchStatus(message.status ?? (terminal && role !== "user" ? "completed" : role === "user" ? "sent" : "running")); const messageTerminal = role !== "user" && TERMINAL_STATUSES.has(status); - const timing = role === "user" ? normalizeTimingProjection(message.timing) ?? emptyTimingProjection() : normalizeTimingProjection(contextTiming) ?? emptyTimingProjection(); + const messageTraceId = safeTraceId(message.traceId ?? message.turnId); + const appliesToContextTrace = role !== "user" && Boolean(traceId && messageTraceId && messageTraceId === traceId); + const messageTiming = normalizeTimingProjection(message.timing) ?? normalizeTimingProjection(message); + const contextTimingProjection = appliesToContextTrace ? normalizeTimingProjection(contextTiming) : null; + const timing = role === "user" ? messageTiming ?? emptyTimingProjection() : messageTiming ?? contextTimingProjection ?? emptyTimingProjection(); return { ...message, messageId,