From 4e0e52af80425445555980a2ec12486916fd25fb Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Tue, 23 Jun 2026 13:27:16 +0800 Subject: [PATCH] fix(workbench): project message timing facts from events (#1967) --- internal/cloud/workbench-projection-writer.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/internal/cloud/workbench-projection-writer.ts b/internal/cloud/workbench-projection-writer.ts index 742232f1..1053ddaa 100644 --- a/internal/cloud/workbench-projection-writer.ts +++ b/internal/cloud/workbench-projection-writer.ts @@ -134,6 +134,28 @@ export async function writeWorkbenchProjectionEvent({ runtimeStore = null, event const projectedSeq = nonNegativeInteger(allocation?.projectedSeq); if (projectedSeq <= 0) throw new Error("Workbench projection allocator returned an invalid projectedSeq."); const eventId = textValue(event.id) || stableFactId("wte", { traceId, sourceEventId }); + const messageFinalText = finalResponseTextValue(event.finalResponse, event.assistantText, event.reply); + const messageFact = sessionId ? normalizeMessageFact({ + messageId: eventProjectionAssistantMessageId(traceId, event), + role: "agent", + status, + traceId, + turnId, + text: terminal && messageFinalText ? messageFinalText : "", + projectedSeq, + sourceSeq, + sourceEventId, + terminal, + sealed: terminal, + timing, + startedAt: timing.startedAt, + lastEventAt: timing.lastEventAt, + finishedAt: timing.finishedAt, + durationMs: timing.durationMs, + createdAt: occurredAt, + updatedAt: projectedAt, + source: "workbench-event-projection" + }, 0, { traceId, sessionId, turnId, terminal, terminalStatus: status, finalText: messageFinalText, timestamp: projectedAt, timing }) : null; const facts = { sessions: sessionId ? [{ sessionId, @@ -172,6 +194,7 @@ export async function writeWorkbenchProjectionEvent({ runtimeStore = null, event occurredAt, updatedAt: projectedAt }], + messages: messageFact ? [messageFact] : [], checkpoints: [{ traceId, sessionId, @@ -206,6 +229,16 @@ export async function writeWorkbenchProjectionEvent({ runtimeStore = null, event }); } +function eventProjectionAssistantMessageId(traceId, event = {}) { + const explicit = textValue(event.messageId ?? event.assistantMessageId ?? event.assistantMessage?.messageId); + if (explicit) return explicit; + const traceSuffix = (safeTraceId(traceId) || String(traceId || "trace")) + .replace(/^trc_/u, "") + .replace(/[^A-Za-z0-9_.:-]/gu, "_") + .slice(0, 48) || "trace"; + return `msg_${traceSuffix}_agent`; +} + async function writeWorkbenchProjectionFacts({ runtimeStore = null, traceStore = defaultCodeAgentTraceStore, traceId = null, ownerUserId = null, ownerRole = null, sessionId = null, projectId = null, conversationId = null, threadId = null, status = "active", session = {}, payload = null, params = {} } = {}) { if (typeof runtimeStore?.writeWorkbenchFacts !== "function") return null; const facts = buildWorkbenchProjectionFacts({ traceId, ownerUserId, ownerRole, sessionId, projectId, conversationId, threadId, status, session, payload, params });