From b28bd11745c65cde103ebfa9bdfc24f6b6086c07 Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 23 Sep 2026 20:26:55 +0800 Subject: [PATCH] Add Session runtime metrics tab --- apps/web/src/App.tsx | 9 +- .../dashboard/RuntimeObservabilityContent.tsx | 131 +------------- .../dashboard/RuntimeTrendCharts.test.tsx | 11 ++ .../features/dashboard/RuntimeTrendCharts.tsx | 4 +- .../features/dashboard/RuntimeTrendPanel.tsx | 160 ++++++++++++++++++ .../src/features/sessions/SessionsView.tsx | 75 +++++++- apps/web/src/style.css | 23 +++ 7 files changed, 281 insertions(+), 132 deletions(-) create mode 100644 apps/web/src/features/dashboard/RuntimeTrendPanel.tsx diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 34cd139e6..14f106421 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -949,6 +949,7 @@ export function App() { void (async () => { const generation = coreGeneration; void refreshAgents(); + void refreshRuntimeSnapshot(); const refreshed = await refreshSessions(); if (!refreshed || generation !== connectionGenerationRef.current) return; const filter = sessionAgentFilterRef.current; @@ -957,7 +958,7 @@ export function App() { const sessionId = selectedIdRef.current; if (sessionId) await refreshSelectedSession(sessionId); })(); - }, [coreGeneration, refreshAgents, refreshFilteredSessions, refreshSelectedSession, refreshSessions]); + }, [coreGeneration, refreshAgents, refreshFilteredSessions, refreshRuntimeSnapshot, refreshSelectedSession, refreshSessions]); const refreshDashboard = useCallback(() => { if (dashboardRefreshInFlightRef.current) return; @@ -1047,7 +1048,7 @@ export function App() { }, [refreshAgents, refreshEnvironmentTemplates, refreshSessions, refreshStartupConfiguration, refreshVaults]); useEffect(() => { - if (view !== "dashboard") return; + if (view !== "dashboard" && view !== "sessions") return; let timer: number | null = null; void (async () => { if ( @@ -2346,6 +2347,10 @@ export function App() { turnError={turnError} turnState={turnState} environmentObservation={environmentObservation} + runtimeSnapshot={runtimeSnapshot} + runtimeError={runtimeCollectionError} + runtimeStale={runtimeCollectionState === "failed"} + loadRuntimeHistory={loadDashboardRuntimeHistory} sendError={sendError} streamError={streamError} streamState={streamState} diff --git a/apps/web/src/features/dashboard/RuntimeObservabilityContent.tsx b/apps/web/src/features/dashboard/RuntimeObservabilityContent.tsx index b11424475..f52cf5c47 100644 --- a/apps/web/src/features/dashboard/RuntimeObservabilityContent.tsx +++ b/apps/web/src/features/dashboard/RuntimeObservabilityContent.tsx @@ -10,7 +10,7 @@ import { Search, Server, } from "lucide-react"; -import { useEffect, useMemo, useState, type ReactNode } from "react"; +import { useMemo, useState, type ReactNode } from "react"; import { flexRender, getCoreRowModel, @@ -35,30 +35,11 @@ import { type RuntimeDashboardRow, } from "./dashboard-model"; import type { RuntimeDashboardSnapshot } from "./runtime-snapshot"; -import { RUNTIME_SNAPSHOT_REFRESH_MS } from "./runtime-snapshot"; -import { RuntimeTrendCharts } from "./RuntimeTrendCharts"; -import type { RuntimeTrendSource } from "./RuntimeTrendCharts"; -import { - RUNTIME_DURABLE_RANGES, - type RuntimeDurableRange, - type RuntimeDurableSnapshot, -} from "./runtime-history"; -import { - appendRuntimeTrendSample, - RUNTIME_TREND_RANGES, - RUNTIME_TREND_WINDOW_MS, - runtimeTrendRange, - type RuntimeTrendRange, - type RuntimeTrendSample, -} from "./runtime-trends"; +import { RuntimeTrendPanel, type RuntimeHistoryLoader } from "./RuntimeTrendPanel"; const PAGE_SIZE = 10; -export type RuntimeHistoryLoader = ( - snapshot: RuntimeDashboardSnapshot, - range: RuntimeDurableRange, - signal: AbortSignal, -) => Promise; +export type { RuntimeHistoryLoader } from "./RuntimeTrendPanel"; function RuntimeMetric({ icon, @@ -308,68 +289,6 @@ export function RuntimeObservabilityContent({ }) { const model = useMemo(() => buildRuntimeDashboardModel(snapshot.sessions, snapshot.observations), [snapshot]); const summary = model.summary; - const [trendSamples, setTrendSamples] = useState(() => appendRuntimeTrendSample([], snapshot)); - const [selectedTrendRange, setSelectedTrendRange] = useState(RUNTIME_TREND_WINDOW_MS); - const [selectedDurableRange, setSelectedDurableRange] = useState(RUNTIME_DURABLE_RANGES[0].milliseconds); - const [durableSnapshot, setDurableSnapshot] = useState(null); - const [durableState, setDurableState] = useState<"connecting" | "ready" | "unavailable" | "failed">("connecting"); - const [durableError, setDurableError] = useState(null); - const visibleTrendSamples = useMemo( - () => runtimeTrendRange(trendSamples, selectedTrendRange), - [selectedTrendRange, trendSamples], - ); - const source: RuntimeTrendSource = durableState === "unavailable" && durableSnapshot === null - ? "live" - : "durable"; - const selectedSamples = source === "durable" - ? durableSnapshot?.samples ?? [] - : visibleTrendSamples; - const latestTrendSample = selectedSamples.at(-1); - const selectedRange = source === "durable" ? selectedDurableRange : selectedTrendRange; - const rangeEnd = source === "durable" && durableSnapshot !== null - ? durableSnapshot.rangeEnd - : latestTrendSample?.sampledAt ?? snapshot.loadedAt; - const rangeStart = source === "durable" && durableSnapshot !== null - ? durableSnapshot.rangeStart - : rangeEnd - selectedTrendRange; - - useEffect(() => { - setTrendSamples((current) => appendRuntimeTrendSample(current, snapshot)); - }, [snapshot]); - - useEffect(() => { - const controller = new AbortController(); - setDurableState("connecting"); - setDurableError(null); - void loadRuntimeHistory(snapshot, selectedDurableRange, controller.signal).then((result) => { - if (controller.signal.aborted) return; - if (result === null) { - setDurableSnapshot(null); - setDurableState("unavailable"); - return; - } - setDurableSnapshot(result); - setDurableState("ready"); - }).catch((error: unknown) => { - if (controller.signal.aborted) return; - setDurableState("failed"); - setDurableError(error instanceof Error ? error.message : "Durable Runtime history request failed."); - }); - return () => controller.abort(); - }, [loadRuntimeHistory, selectedDurableRange, snapshot]); - - const rangeOptions = source === "durable" ? RUNTIME_DURABLE_RANGES : RUNTIME_TREND_RANGES; - const sourceStatus = source === "durable" - ? durableState === "failed" - ? "History stale" - : durableState === "connecting" - ? "History · loading" - : `Durable · ${durableSnapshot?.resolutionSeconds ?? 0}s` - : stale - ? "Stale · retrying" - : `Live · ${RUNTIME_SNAPSHOT_REFRESH_MS / 1_000}s`; - const sourceStatusStale = source === "durable" ? durableState === "failed" : stale; - return ( <>
@@ -379,49 +298,7 @@ export function RuntimeObservabilityContent({ } label="Reported tokens" value={formatDashboardTokens(summary.totalTokens)} detail={`${summary.tokenCoverageCount}/${summary.sessionCount} Sessions report usage`} />
-
-
-
-

Resource trends

-

{source === "durable" ? "Retained samples · durable history" : "Browser-local samples · reset on reload"}

-
-
- -