/** * SwarmDashboard - Multi-Agent Collaboration Task Dashboard * * Visualizes swarm tasks, multi-agent collaboration) with real-time * status updates, task history, and manual trigger functionality. * * Part of ZCLAW L4 Self-Evolution capability. */ import { useState, useEffect, useCallback, useMemo } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Users, Play, CheckCircle, XCircle, Clock, ChevronDown, ChevronRight, Layers, GitBranch, MessageSquare, RefreshCw, Plus, History, Sparkles, } from 'lucide-react'; import { AgentSwarm, type SwarmTask, type Subtask, type SwarmTaskStatus, type CommunicationStyle, } from '../lib/agent-swarm'; import { useAgentStore } from '../store/agentStore'; // === Types === interface SwarmDashboardProps { className?: string; onTaskSelect?: (task: SwarmTask) => void; } type FilterType = 'all' | 'active' | 'completed' | 'failed'; // === Status Config === const TASK_STATUS_CONFIG: Record = { planning: { label: '规划中', className: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400', dotClass: 'bg-purple-500', icon: Layers, }, executing: { label: '执行中', className: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400', dotClass: 'bg-blue-500 animate-pulse', icon: Play, }, aggregating: { label: '汇总中', className: 'bg-cyan-100 text-cyan-700 dark:bg-cyan-900/30 dark:text-cyan-400', dotClass: 'bg-cyan-500 animate-pulse', icon: RefreshCw, }, done: { label: '已完成', className: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400', dotClass: 'bg-green-500', icon: CheckCircle, }, failed: { label: '失败', className: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400', dotClass: 'bg-red-500', icon: XCircle, }, }; const SUBTASK_STATUS_CONFIG: Record = { pending: { label: '待执行', dotClass: 'bg-gray-400' }, running: { label: '执行中', dotClass: 'bg-blue-500 animate-pulse' }, done: { label: '完成', dotClass: 'bg-green-500' }, failed: { label: '失败', dotClass: 'bg-red-500' }, }; const COMMUNICATION_STYLE_CONFIG: Record = { sequential: { label: '顺序执行', icon: GitBranch, description: '每个 Agent 依次处理,输出传递给下一个', }, parallel: { label: '并行执行', icon: Layers, description: '多个 Agent 同时处理不同子任务', }, debate: { label: '辩论模式', icon: MessageSquare, description: '多个 Agent 提供观点,协调者综合', }, }; // === Components === function TaskStatusBadge({ status }: { status: SwarmTaskStatus }) { const config = TASK_STATUS_CONFIG[status]; const Icon = config.icon; return ( {config.label} ); } function SubtaskStatusDot({ status }: { status: string }) { const config = SUBTASK_STATUS_CONFIG[status] || SUBTASK_STATUS_CONFIG.pending; return ; } function CommunicationStyleBadge({ style }: { style: CommunicationStyle }) { const config = COMMUNICATION_STYLE_CONFIG[style]; const Icon = config.icon; return ( {config.label} ); } function SubtaskItem({ subtask, agentName, isExpanded, onToggle, }: { subtask: Subtask; agentName: string; isExpanded: boolean; onToggle: () => void; }) { const duration = useMemo(() => { if (!subtask.startedAt) return null; const start = new Date(subtask.startedAt).getTime(); const end = subtask.completedAt ? new Date(subtask.completedAt).getTime() : Date.now(); return Math.round((end - start) / 1000); }, [subtask.startedAt, subtask.completedAt]); return (
{isExpanded && subtask.result && (
{subtask.result}
)}
{subtask.error && (

{subtask.error}

)}
); } function TaskCard({ task, isSelected, onSelect, }: { task: SwarmTask; isSelected: boolean; onSelect: () => void; }) { const [expandedSubtasks, setExpandedSubtasks] = useState>(new Set()); const { clones } = useAgentStore(); const toggleSubtask = useCallback((subtaskId: string) => { setExpandedSubtasks((prev) => { const next = new Set(prev); if (next.has(subtaskId)) { next.delete(subtaskId); } else { next.add(subtaskId); } return next; }); }, []); const completedCount = task.subtasks.filter((s) => s.status === 'done').length; const totalDuration = useMemo(() => { if (!task.completedAt) return null; const start = new Date(task.createdAt).getTime(); const end = new Date(task.completedAt).getTime(); return Math.round((end - start) / 1000); }, [task.createdAt, task.completedAt]); const getAgentName = (agentId: string) => { const agent = clones.find((a) => a.id === agentId); return agent?.name || agentId; }; return (
{isSelected && (

子任务

{task.subtasks.map((subtask) => ( toggleSubtask(subtask.id)} /> ))}
{task.finalResult && (

最终结果

{task.finalResult}

)}
)}
); } function CreateTaskForm({ onSubmit, onCancel, }: { onSubmit: (description: string, style: CommunicationStyle) => void; onCancel: () => void; }) { const [description, setDescription] = useState(''); const [style, setStyle] = useState('sequential'); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (description.trim()) { onSubmit(description.trim(), style); } }; return (