diff --git a/frontend/app/components/assistant-ui/thread-list.tsx b/frontend/app/components/assistant-ui/thread-list.tsx index b88a1cf..9ba785a 100644 --- a/frontend/app/components/assistant-ui/thread-list.tsx +++ b/frontend/app/components/assistant-ui/thread-list.tsx @@ -442,7 +442,7 @@ export function toReplayRepository( id, role: message.role, createdAt: new Date(), - content: toThreadMessageContent(parts), + content: toThreadMessageContent(parts, elapsedMs), ...(message.role === "assistant" ? { status: { type: "complete", reason: "stop" }, @@ -797,9 +797,11 @@ function attachStageNote(input: unknown, stageNote?: string) { return { value: input, __claw_stage_note: note }; } -function toThreadMessageContent(parts: UIMessage["parts"]) { +function toThreadMessageContent(parts: UIMessage["parts"], elapsedMs?: number) { return parts.map((part) => { - if (part.type === "text" || part.type === "reasoning") return part; + const elapsedPayload = elapsedMs === undefined ? {} : { elapsedMs }; + if (part.type === "text") return part; + if (part.type === "reasoning") return { ...part, ...elapsedPayload }; if (part.type === "dynamic-tool") { return { type: "tool-call", @@ -807,6 +809,7 @@ function toThreadMessageContent(parts: UIMessage["parts"]) { toolCallId: part.toolCallId, args: part.input ?? {}, argsText: JSON.stringify(part.input ?? {}), + ...elapsedPayload, ...(part.state === "output-available" ? { result: part.output } : {}), }; } diff --git a/frontend/app/components/assistant-ui/thread.tsx b/frontend/app/components/assistant-ui/thread.tsx index 20fa79d..57df736 100644 --- a/frontend/app/components/assistant-ui/thread.tsx +++ b/frontend/app/components/assistant-ui/thread.tsx @@ -1316,6 +1316,13 @@ const ActivityChainSummary: FC<{ ); const storedElapsedMs = useAuiState((s) => { const value = readMetadataValue(s.message.metadata, "elapsedMs"); + const contentValue = findNumberInActivityParts( + s.message.content, + "elapsedMs", + ); + if (typeof value !== "number" || !Number.isFinite(value)) { + return contentValue; + } return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.round(value)) : null; @@ -1376,6 +1383,7 @@ type ActivitySummaryPart = { args?: unknown; toolName?: string; runStartedAtMs?: unknown; + elapsedMs?: unknown; }; function findNumberInActivityParts(