From b1868a6401ef6e6ad45cdbbcf45b9c96dff91791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E9=98=B3?= Date: Thu, 7 May 2026 17:51:23 +0800 Subject: [PATCH] Improve active run timing and turn limits --- .../components/assistant-ui/thread-list.tsx | 20 +++++++++-- .../app/components/assistant-ui/thread.tsx | 35 ++++++++++++++++--- src/agent_tools.py | 10 +++--- src/agent_types.py | 5 ++- src/main.py | 17 ++++----- src/session_store.py | 3 +- tests/test_session_store.py | 2 +- 7 files changed, 68 insertions(+), 24 deletions(-) diff --git a/frontend/app/components/assistant-ui/thread-list.tsx b/frontend/app/components/assistant-ui/thread-list.tsx index c0d953f..6b38d26 100644 --- a/frontend/app/components/assistant-ui/thread-list.tsx +++ b/frontend/app/components/assistant-ui/thread-list.tsx @@ -426,7 +426,12 @@ export function toReplayRepository( role: message.role, parts, ...(message.role === "assistant" - ? { metadata: { sessionId: session.session_id ?? fallbackSessionId } } + ? { + metadata: { + sessionId: session.session_id ?? fallbackSessionId, + elapsedMs: normalizeElapsedMs(message.metadata?.elapsed_ms), + }, + } : {}), } as UIMessage; const threadMessage = { @@ -442,7 +447,10 @@ export function toReplayRepository( unstable_annotations: [], unstable_data: [], steps: [], - custom: { sessionId: session.session_id ?? fallbackSessionId }, + custom: { + sessionId: session.session_id ?? fallbackSessionId, + elapsedMs: normalizeElapsedMs(message.metadata?.elapsed_ms), + }, }, } : { metadata: { custom: {} } }), @@ -536,8 +544,9 @@ function buildActiveRunParts(runStatus: ClawActiveRunStatus) { ? "当前会话已有任务在执行,本轮正在排队。" : runStatus.current_stage || "后端正在执行这个会话。"; const reasoningText = eventLines.length ? eventLines.join("\n") : fallback; + const runStartedAtMs = normalizeRunTimestampMs(runStatus.started_at); return [ - { type: "reasoning", text: reasoningText }, + { type: "reasoning", text: reasoningText, runStartedAtMs }, ...buildRunToolParts(runStatus.events ?? []), ]; } @@ -694,6 +703,11 @@ function normalizeRunTimestampMs(value: unknown) { return value < 10_000_000_000 ? Math.round(value * 1000) : Math.round(value); } +function normalizeElapsedMs(value: unknown) { + if (typeof value !== "number" || !Number.isFinite(value)) return undefined; + return Math.max(0, Math.round(value)); +} + function findLastAssistantContentIndex(messages: readonly ClawStoredMessage[]) { for (let index = messages.length - 1; index >= 0; index -= 1) { const message = messages[index]; diff --git a/frontend/app/components/assistant-ui/thread.tsx b/frontend/app/components/assistant-ui/thread.tsx index 1209f8d..60747da 100644 --- a/frontend/app/components/assistant-ui/thread.tsx +++ b/frontend/app/components/assistant-ui/thread.tsx @@ -1217,11 +1217,20 @@ const ActivityChainSummary: FC<{ }> = ({ messageId, partIndex }) => { const { openItem } = useActivityPanel(); const startedAtRef = useRef(Date.now()); - const [elapsedMs, setElapsedMs] = useState(0); - const runStartedAtMs = useAuiState((s) => { + const metadataRunStartedAtMs = useAuiState((s) => { const value = readMetadataValue(s.message.metadata, "runStartedAtMs"); return typeof value === "number" && Number.isFinite(value) ? value : null; }); + const contentRunStartedAtMs = useAuiState((s) => + findNumberInActivityParts(s.message.content, "runStartedAtMs"), + ); + const storedElapsedMs = useAuiState((s) => { + const value = readMetadataValue(s.message.metadata, "elapsedMs"); + return typeof value === "number" && Number.isFinite(value) + ? Math.max(0, Math.round(value)) + : null; + }); + const [elapsedMs, setElapsedMs] = useState(storedElapsedMs ?? 0); const label = useAuiState((s) => { const message = s.message; const status = @@ -1238,14 +1247,18 @@ const ActivityChainSummary: FC<{ }); useEffect(() => { - if (!active) return; - const startedAt = runStartedAtMs ?? startedAtRef.current; + if (!active) { + if (storedElapsedMs !== null) setElapsedMs(storedElapsedMs); + return; + } + const startedAt = + metadataRunStartedAtMs ?? contentRunStartedAtMs ?? startedAtRef.current; setElapsedMs(Math.max(0, Date.now() - startedAt)); const timer = window.setInterval(() => { setElapsedMs(Math.max(0, Date.now() - startedAt)); }, 1000); return () => window.clearInterval(timer); - }, [active, runStartedAtMs]); + }, [active, metadataRunStartedAtMs, contentRunStartedAtMs, storedElapsedMs]); return (