import { useState, useEffect, useCallback } from "react"; import { CheckCircle, XCircle, Loader2, Square, Clock } from "lucide-react"; import type { AgentHeartbeatRun } from "../api"; import { fetchAgentRuns, stopAgentRun } from "../api"; interface AgentRunHistoryProps { agentId: string; projectId?: string; /** Optional callback when a run row is clicked */ onRunClick?: (runId: string) => void; } const STATUS_ICONS: Record = { completed: { icon: CheckCircle, color: "var(--color-success, #3fb950)" }, failed: { icon: XCircle, color: "var(--color-error, #f85149)" }, active: { icon: Loader2, color: "var(--in-progress, #bc8cff)" }, terminated: { icon: Square, color: "var(--text-muted, #8b949e)" }, }; export function AgentRunHistory({ agentId, projectId, onRunClick }: AgentRunHistoryProps) { const [runs, setRuns] = useState([]); const [isLoading, setIsLoading] = useState(true); const loadRuns = useCallback(async () => { setIsLoading(true); try { const data = await fetchAgentRuns(agentId, 50, projectId); setRuns(data); } catch { setRuns([]); } finally { setIsLoading(false); } }, [agentId, projectId]); useEffect(() => { void loadRuns(); }, [loadRuns]); const handleStop = useCallback(async () => { if (!confirm("Stop this run?")) { return; } try { await stopAgentRun(agentId, projectId); await loadRuns(); } catch { // No-op: keep history view usable even if stop fails. } }, [agentId, projectId, loadRuns]); if (isLoading) { return
Loading runs...
; } if (runs.length === 0) { return
No runs yet
; } return (
{runs.map(run => { const statusInfo = STATUS_ICONS[run.status] ?? STATUS_ICONS.terminated; const StatusIcon = statusInfo.icon; const duration = run.endedAt ? Math.round((new Date(run.endedAt).getTime() - new Date(run.startedAt).getTime()) / 1000) : null; const usage = run.usageJson; return (
onRunClick(run.id) : undefined} role={onRunClick ? "button" : undefined} tabIndex={onRunClick ? 0 : undefined} aria-label={onRunClick ? `Run ${run.id.slice(0, 8)}, ${run.status}` : undefined} onKeyDown={onRunClick ? (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onRunClick(run.id); } } : undefined} >
{run.id} {new Date(run.startedAt).toLocaleString()}
{duration !== null && ( {duration}s )} {usage && ( {((usage.inputTokens + usage.outputTokens) / 1000).toFixed(1)}k tokens )} {run.triggerDetail && ( {run.triggerDetail} )}
{run.status === "active" && ( )}
); })}
); }