import { useMemo, type ReactNode } from 'react'; import { Activity, AlertTriangle, ArrowRight, BarChart3, CheckCircle2, Cog, GitBranch, ShieldAlert, Sparkles, Users, Zap } from 'lucide-react'; import { buildAgentActivityRows, buildGroupedActivityRows, formatDuration, formatNanoAiu, formatTokenCount, type SessionActivityState, type SessionRequestUsageState, type TurnEventLog, } from '@renderer/lib/sessionActivity'; import { resolveWorkflowAgentHierarchy, type AgentNodeConfig, type WorkflowDefinition } from '@shared/domain/workflow'; import type { SessionRecord } from '@shared/domain/session'; import { AgentRow } from './activity/AgentRow'; import { SubWorkflowGroup } from './activity/SubWorkflowGroup'; import { modeAccent, modeLabels } from './activity/constants'; /* ── Section header ────────────────────────────────────────── */ function SectionHeader({ children }: { children: ReactNode }) { return (

{children}

); } /* ── Turn event helpers ─────────────────────────────────────── */ import type { SessionEventKind } from '@shared/domain/event'; function TurnEventIcon({ kind, phase, success }: { kind: SessionEventKind; phase?: string; success?: boolean }) { const base = 'size-3'; switch (kind) { case 'agent-activity': return ; case 'subagent': return ; case 'hook-lifecycle': return ; case 'skill-invoked': return ; case 'session-compaction': return ; case 'workflow-diagnostic': return ; default: return ; } } function formatTurnEventTimestamp(iso: string): string { try { const d = new Date(iso); return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }); } catch { return ''; } } /* ── ActivityPanel ─────────────────────────────────────────── */ interface ActivityPanelProps { activity?: SessionActivityState; workflow: WorkflowDefinition; workflows?: ReadonlyArray; session: SessionRecord; sessionRequestUsage?: SessionRequestUsageState; turnEvents?: TurnEventLog; } export function ActivityPanel({ activity, workflow, workflows, session, sessionRequestUsage, turnEvents, }: ActivityPanelProps) { const resolveOptions = useMemo(() => ({ resolveWorkflow: (id: string) => workflows?.find((w) => w.id === id), }), [workflows]); const hierarchy = useMemo( () => resolveWorkflowAgentHierarchy(workflow, resolveOptions), [workflow, resolveOptions], ); const groupedRows = useMemo( () => buildGroupedActivityRows(activity, hierarchy), [activity, hierarchy], ); const workflowMode = workflow.settings.orchestrationMode ?? 'single'; const totalAgentCount = hierarchy.topLevelAgents.length + hierarchy.subWorkflows.reduce((sum, sw) => sum + sw.agents.length, 0); const isBusy = session.status === 'running'; const hasPendingApproval = session.pendingApproval?.status === 'pending'; const queuedCount = (session.pendingApprovalQueue ?? []).filter((a) => a.status === 'pending').length; const totalApprovalCount = (hasPendingApproval ? 1 : 0) + queuedCount; const accent = modeAccent[workflowMode] ?? modeAccent.single; const hasSubWorkflows = groupedRows.subWorkflows.length > 0; return (
{/* Header */}
Activity {hasPendingApproval ? ( Approval{totalApprovalCount > 1 ? `s (${totalApprovalCount})` : ''} ) : isBusy ? ( ) : null}
{/* ── Agents section ───────────────────────────────── */}
Agents {totalAgentCount} {modeLabels[workflowMode]} {/* Top-level agents */} {groupedRows.topLevelAgents.length > 0 && (
{groupedRows.topLevelAgents.map((row, index) => { const agent = hierarchy.topLevelAgents.find((a) => a.id === row.key || a.name === row.agentName); const agentKey = row.activity?.agentId ?? row.key; const agentUsage = sessionRequestUsage?.perAgent[agentKey] ?? sessionRequestUsage?.perAgent[row.agentName]; return ( ); })}
)} {/* Sub-workflow groups */} {hasSubWorkflows && (
0 ? 'mt-2' : ''}`}> {groupedRows.subWorkflows.map((group) => { const subDef = hierarchy.subWorkflows.find((sw) => sw.nodeId === group.nodeId); return ( ); })}
)} {totalAgentCount === 0 && !hasSubWorkflows && (

No agents configured

)}
{/* ── Session usage section ──────────────────────────── */} {sessionRequestUsage && sessionRequestUsage.requestCount > 0 && (
Session Usage
{sessionRequestUsage.requestCount} premium request{sessionRequestUsage.requestCount === 1 ? '' : 's'} {sessionRequestUsage.totalNanoAiu > 0 && ( <> · {formatNanoAiu(sessionRequestUsage.totalNanoAiu)} AIU )}
{formatTokenCount(sessionRequestUsage.totalInputTokens)} in · {formatTokenCount(sessionRequestUsage.totalOutputTokens)} out {sessionRequestUsage.totalCost > 0 && ( <> · {sessionRequestUsage.totalCost.toFixed(2)} cost )} {sessionRequestUsage.totalDurationMs > 0 && ( <> · {formatDuration(sessionRequestUsage.totalDurationMs)} total )}
)} {/* ── Turn events section ─────────────────────────── */} {turnEvents && turnEvents.length > 0 && (
Events {turnEvents.length}
{turnEvents.slice().reverse().map((entry, index) => (
{entry.label} {formatTurnEventTimestamp(entry.occurredAt)}
{entry.detail && (

{entry.detail}

)}
))}
)}
); }