"use client"; export type SessionReplaySnapshot = { sessionId: string; session: TSession; runStatus: TRun | null; signature: string; fetchedAt: number; }; const MAX_REPLAY_CACHE_SIZE = 50; const replayCache = new Map(); export function readCachedSessionReplay( sessionId: string, ): SessionReplaySnapshot | null { const cached = replayCache.get(sessionId); if (!cached) return null; // Refresh recency for LRU behavior. replayCache.delete(sessionId); replayCache.set(sessionId, cached); return cached as SessionReplaySnapshot; } export function invalidateCachedSessionReplay(sessionId: string) { replayCache.delete(sessionId); } export async function fetchSessionReplaySnapshot< TSession = unknown, TRun = unknown, >( sessionId: string, options: { signal?: AbortSignal } = {}, ): Promise | null> { const [sessionResponse, runStatus] = await Promise.all([ fetch(`/api/claw/sessions/${encodeURIComponent(sessionId)}`, { cache: "no-store", signal: options.signal, }), fetchLatestRunStatusForCache(sessionId, options.signal), ]); if (!sessionResponse.ok) return null; const session = (await sessionResponse.json()) as TSession; return writeCachedSessionReplay(sessionId, session, runStatus); } function writeCachedSessionReplay( sessionId: string, session: TSession, runStatus: TRun | null, ): SessionReplaySnapshot { const snapshot: SessionReplaySnapshot = { sessionId, session, runStatus, signature: sessionReplaySignature(session, runStatus), fetchedAt: Date.now(), }; replayCache.set(sessionId, snapshot as SessionReplaySnapshot); while (replayCache.size > MAX_REPLAY_CACHE_SIZE) { const oldest = replayCache.keys().next().value; if (!oldest) break; replayCache.delete(oldest); } return snapshot; } async function fetchLatestRunStatusForCache( sessionId: string, signal?: AbortSignal, ): Promise { try { const url = new URL("/api/claw/runs/latest", window.location.origin); url.searchParams.set("session_id", sessionId); const response = await fetch(url, { cache: "no-store", signal }); if (!response.ok) return null; return (await response.json()) as TRun; } catch (error) { if (error instanceof DOMException && error.name === "AbortError") { throw error; } return null; } } function sessionReplaySignature(session: unknown, runStatus: unknown) { const sessionObject = isObject(session) ? session : {}; const runObject = isObject(runStatus) ? runStatus : {}; const messages = Array.isArray(sessionObject.messages) ? sessionObject.messages : []; const lastMessage = messages.at(-1); const lastObject = isObject(lastMessage) ? lastMessage : {}; const metadata = isObject(lastObject.metadata) ? lastObject.metadata : {}; return [ sessionObject.session_id, sessionObject.turns, sessionObject.tool_calls, messages.length, lastObject.role, lastObject.message_id, typeof lastObject.content === "string" ? lastObject.content.length : "", metadata.elapsed_ms, runObject.run_id, runObject.status, runObject.updated_at, runObject.finished_at, runObject.elapsed_ms, ].join("|"); } function isObject(value: unknown): value is Record { return Boolean(value) && typeof value === "object" && !Array.isArray(value); }