/** * Agent Lifecycle Insights Dashboard (Phase 2 — Feature 7) * * Shows per-agent: tool call chains, delegation flows, health score drift. * Table: agent name, total sessions, avg score, tool usage, delegation count. * Click agent to see detail: recent sessions, tool usage distribution, health trend. * * Route: /agents/insights */ import React, { useState } from 'react'; import { useApi } from '../hooks/useApi'; import { getAgents } from '../api/agents'; import { getAgentInsights, type AgentInsightsData } from '../api/agent-insights'; // ─── Helpers ───────────────────────────────────────────── function timeAgo(iso: string): string { const diff = Date.now() - new Date(iso).getTime(); const seconds = Math.floor(diff / 1000); if (seconds < 60) return `${seconds}s ago`; const minutes = Math.floor(seconds / 60); if (minutes < 60) return `${minutes}m ago`; const hours = Math.floor(minutes / 60); if (hours < 24) return `${hours}h ago`; const days = Math.floor(hours / 24); return `${days}d ago`; } function topTools(usage: Record, max = 3): string { return Object.entries(usage) .sort(([, a], [, b]) => b - a) .slice(0, max) .map(([name, count]) => `${name} (${count})`) .join(', ') || 'None'; } // ─── Agent Detail Panel ────────────────────────────────── function AgentDetailPanel({ data, onClose, }: { data: AgentInsightsData; onClose: () => void; }): React.ReactElement { const toolEntries = Object.entries(data.toolUsage).sort(([, a], [, b]) => b - a); const totalToolCalls = toolEntries.reduce((sum, [, count]) => sum + count, 0); return (

{data.agent.name}

{data.agent.description && (

{data.agent.description}

)}
{/* Summary stats */}

Total Sessions

{data.totalSessions}

Avg Score

{data.avgScore != null ? data.avgScore : '--'}

Tool Calls

{totalToolCalls}

Delegations

{data.delegationCount}

{/* Tool Usage Distribution */}

Tool Usage Distribution

{toolEntries.length === 0 ? (

No tool calls recorded

) : (
{toolEntries.slice(0, 10).map(([name, count]) => { const pct = totalToolCalls > 0 ? Math.round((count / totalToolCalls) * 100) : 0; return (
{name}
{count} ({pct}%)
); })}
)}
{/* Health Trend */} {data.healthTrend.length > 0 && (

Health Score Trend

{data.healthTrend.map((point, idx) => { const score = point.score ?? 0; const height = Math.max(4, (score / 100) * 80); const color = score >= 70 ? 'bg-green-400' : score >= 40 ? 'bg-yellow-400' : 'bg-red-400'; return (
); })}
Oldest Latest
)} {/* Recent Sessions */}

Recent Sessions

{data.recentSessions.map((s) => ( ))}
Session Started Events
{s.id} {timeAgo(s.startedAt)} {s.eventCount}
); } // ─── Main Component ────────────────────────────────────── export function AgentInsights(): React.ReactElement { const [selectedAgent, setSelectedAgent] = useState(null); const agentsQuery = useApi(() => getAgents(), []); const agents = agentsQuery.data ?? []; const insightsQuery = useApi( () => (selectedAgent ? getAgentInsights(selectedAgent) : Promise.resolve(null)), [selectedAgent], ); return (

Agent Insights

Per-agent lifecycle metrics: tool usage, delegations, health trends

{/* Detail panel */} {selectedAgent && insightsQuery.data && ( setSelectedAgent(null)} /> )} {selectedAgent && insightsQuery.loading && (
Loading insights...
)} {selectedAgent && insightsQuery.error && (
{insightsQuery.error}
)} {/* Agents table */} {agentsQuery.loading && (
Loading agents...
)} {!agentsQuery.loading && agents.length === 0 && (

No agents found

Agents appear here once they start sending telemetry

)} {!agentsQuery.loading && agents.length > 0 && (
{agents.map((agent) => ( setSelectedAgent(selectedAgent === agent.name ? null : agent.name) } > ))}
Agent Sessions Last Seen Actions
{agent.name}
{agent.description && (
{agent.description}
)}
{agent.sessionCount} {timeAgo(agent.lastSeenAt)}
)}
); } export default AgentInsights;