From fbc23d20a989693d1f01374ef4a3cd0d71a46dae Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:01:46 +0800 Subject: [PATCH] fix(workbench): persist user message through event projection (#2045) --- internal/cloud/workbench-projection-writer.ts | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/internal/cloud/workbench-projection-writer.ts b/internal/cloud/workbench-projection-writer.ts index 8f357048..63010c36 100644 --- a/internal/cloud/workbench-projection-writer.ts +++ b/internal/cloud/workbench-projection-writer.ts @@ -144,6 +144,7 @@ export async function writeWorkbenchProjectionEvent({ runtimeStore = null, event const previousFinalText = finalResponseTextValue(previousCheckpoint?.finalResponse, previousCheckpoint?.assistantText, previousCheckpoint?.finalText); const messageFinalText = finalResponseTextValue(event.finalResponse, event.assistantText, event.reply, previousFinalText); const checkpointFinalResponse = messageFinalText ? { text: messageFinalText, status, traceId, valuesPrinted: false } : null; + const userMessageFact = sessionId && !suppressedAfterSeal && previousCheckpoint?.userMessage ? normalizeMessageFact(previousCheckpoint.userMessage, 0, { traceId, sessionId, turnId, terminal: false, terminalStatus: "sent", finalText: null, timestamp: projectedAt, timing }) : null; const messageFact = sessionId && !suppressedAfterSeal ? normalizeMessageFact({ messageId: eventProjectionAssistantMessageId(traceId, event), role: "agent", @@ -164,7 +165,7 @@ export async function writeWorkbenchProjectionEvent({ runtimeStore = null, event createdAt: occurredAt, updatedAt: projectedAt, source: "workbench-event-projection" - }, 0, { traceId, sessionId, turnId, terminal, terminalStatus: status, finalText: messageFinalText, timestamp: projectedAt, timing }) : null; + }, userMessageFact ? 1 : 0, { traceId, sessionId, turnId, terminal, terminalStatus: status, finalText: messageFinalText, timestamp: projectedAt, timing }) : null; const facts = { sessions: sessionId && !suppressedAfterSeal ? [{ sessionId, @@ -204,7 +205,7 @@ export async function writeWorkbenchProjectionEvent({ runtimeStore = null, event occurredAt, updatedAt: projectedAt }], - messages: messageFact ? [messageFact] : [], + messages: [userMessageFact, messageFact].filter(Boolean), checkpoints: !suppressedAfterSeal ? [{ traceId, sessionId, @@ -363,6 +364,7 @@ function buildWorkbenchProjectionFacts({ traceId = null, ownerUserId = null, own diagnostic, finalResponse: checkpointFinalResponse, assistantText: messageFinalText, + userMessage: previousCheckpoint?.userMessage ?? null, timing, startedAt: timing.startedAt, lastEventAt: timing.lastEventAt, @@ -386,6 +388,7 @@ function buildWorkbenchProjectionFacts({ traceId = null, ownerUserId = null, own diagnostic, finalResponse: projectedFinalResponse, assistantText: finalText, + userMessage: messages.find((message) => message.role === "user") ?? null, timing, startedAt: timing.startedAt, lastEventAt: timing.lastEventAt, @@ -401,24 +404,11 @@ function workbenchProjectionInputMessages({ session = {}, payload = {}, params = if (Array.isArray(payload?.messages) && payload.messages.length > 0) return payload.messages; if (!traceId) return []; const turnId = textValue(payload?.turnId) || traceId; - const userText = textValue(params?.message ?? params?.prompt ?? payload?.prompt ?? payload?.message); - const userMessageId = textValue(payload?.userMessageId ?? payload?.userMessage?.messageId) || stableFactId("msg", { traceId, role: "user" }); const assistantMessageId = textValue(payload?.assistantMessageId ?? payload?.messageId ?? payload?.assistantMessage?.messageId) || stableFactId("msg", { traceId, role: "agent" }); const createdAt = timestampValue(timestamp ?? payload?.createdAt ?? payload?.updatedAt); const messages = []; - if (userText) { - messages.push({ - messageId: userMessageId, - role: "user", - status: "sent", - text: userText, - traceId, - turnId, - createdAt, - updatedAt: createdAt, - valuesPrinted: false - }); - } + const userMessage = workbenchProjectionUserMessage({ payload, params, traceId, turnId, timestamp: createdAt }); + if (userMessage) messages.push(userMessage); messages.push({ messageId: assistantMessageId, role: "agent", @@ -438,6 +428,23 @@ function workbenchProjectionInputMessages({ session = {}, payload = {}, params = return messages; } +function workbenchProjectionUserMessage({ payload = {}, params = {}, traceId = null, turnId = null, timestamp = null } = {}) { + const userText = textValue(params?.message ?? params?.prompt ?? payload?.prompt ?? payload?.message); + if (!traceId || !userText) return null; + const createdAt = timestampValue(timestamp ?? payload?.createdAt ?? payload?.updatedAt); + return { + messageId: textValue(payload?.userMessageId ?? payload?.userMessage?.messageId) || stableFactId("msg", { traceId, role: "user" }), + role: "user", + status: "sent", + text: userText, + traceId, + turnId: textValue(turnId) || traceId, + createdAt, + updatedAt: createdAt, + valuesPrinted: false + }; +} + function normalizeMessages(messages, context) { const normalized = Array.isArray(messages) ? messages.map((message, index) => normalizeMessageFact(message, index, context)).filter(Boolean) : []; if (context?.terminal && context?.finalText && context?.traceId && !normalized.some((message) => message.role !== "user" && message.traceId === context.traceId)) {