import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { fetchConversations, ConversationSummary } from "../api"; export function ReplayListPage() { const navigate = useNavigate(); const [page, setPage] = useState(1); const [perPage] = useState(20); const [total, setTotal] = useState(0); const [conversations, setConversations] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { setIsLoading(true); setError(null); fetchConversations(page, perPage) .then((result) => { setConversations(result.conversations); setTotal(result.total); }) .catch((err: Error) => setError(err.message)) .finally(() => setIsLoading(false)); }, [page, perPage]); const totalPages = Math.max(1, Math.ceil(total / perPage)); function formatDate(iso: string): string { try { return new Date(iso).toLocaleString(); } catch { return iso; } } function formatCost(usd: number): string { return `$${usd.toFixed(2)}`; } return (

Conversation Replay

Review autonomous agent sessions and audit MCP action execution trails.

{error ? (

Failed to load conversations

{error}

) : isLoading ? (
{[1, 2, 3, 4, 5].map(i => (
))}
) : conversations.length === 0 ? (

No conversations yet

Start a chat session to see conversations here.

) : (
{conversations.map((c, i) => ( navigate(`/replay/${c.thread_id}`)} style={{ borderBottom: i === conversations.length - 1 ? "none" : "1px solid var(--border-light)", cursor: "pointer", transition: "background-color 0.2s" }} className="replay-row-hover" > ))}
Thread Created Last Activity Status Cost
{c.thread_id}
{formatDate(c.created_at)} {formatDate(c.last_activity)} {c.status ?? "active"} {c.total_tokens.toLocaleString()} tokens / {formatCost(c.total_cost_usd)}
Showing {(page - 1) * perPage + 1}-{Math.min(page * perPage, total)} of {total} sessions
)}
); }