'use client' import { useState, useEffect, useCallback } from 'react' import { createClientLogger } from '@/lib/client-logger' const log = createClientLogger('AgentSquadPanel') interface Agent { id: number name: string role: string session_key?: string soul_content?: string status: 'offline' | 'idle' | 'busy' | 'error' last_seen?: number last_activity?: string created_at: number updated_at: number config?: any taskStats?: { total: number assigned: number in_progress: number completed: number } } const statusColors: Record = { offline: 'bg-gray-500', idle: 'bg-green-500', busy: 'bg-yellow-500', error: 'bg-red-500', } const statusIcons: Record = { offline: '⚫', idle: '🟢', busy: '🟡', error: '🔴', } export function AgentSquadPanel() { const [agents, setAgents] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [selectedAgent, setSelectedAgent] = useState(null) const [showCreateModal, setShowCreateModal] = useState(false) const [autoRefresh, setAutoRefresh] = useState(true) // Fetch agents const fetchAgents = useCallback(async () => { try { setError(null) if (agents.length === 0) setLoading(true) const response = await fetch('/api/agents') if (!response.ok) throw new Error('Failed to fetch agents') const data = await response.json() setAgents(data.agents || []) } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred') } finally { setLoading(false) } }, [agents.length]) // Initial load useEffect(() => { fetchAgents() }, [fetchAgents]) // Auto-refresh useEffect(() => { if (!autoRefresh) return const interval = setInterval(fetchAgents, 10000) // Every 10 seconds return () => clearInterval(interval) }, [autoRefresh, fetchAgents]) // Update agent status const updateAgentStatus = async (agentName: string, status: Agent['status'], activity?: string) => { try { const response = await fetch('/api/agents', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: agentName, status, last_activity: activity || `Status changed to ${status}` }) }) if (!response.ok) throw new Error('Failed to update agent status') // Update local state setAgents(prev => prev.map(agent => agent.name === agentName ? { ...agent, status, last_activity: activity || `Status changed to ${status}`, last_seen: Math.floor(Date.now() / 1000), updated_at: Math.floor(Date.now() / 1000) } : agent )) } catch (error) { log.error('Failed to update agent status:', error) setError('Failed to update agent status') } } // Format last seen time const formatLastSeen = (timestamp?: number) => { if (!timestamp) return 'Never' const now = Date.now() const diffMs = now - (timestamp * 1000) const diffMinutes = Math.floor(diffMs / (1000 * 60)) const diffHours = Math.floor(diffMs / (1000 * 60 * 60)) const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)) if (diffMinutes < 1) return 'Just now' if (diffMinutes < 60) return `${diffMinutes}m ago` if (diffHours < 24) return `${diffHours}h ago` if (diffDays < 7) return `${diffDays}d ago` return new Date(timestamp * 1000).toLocaleDateString() } // Get status distribution for summary const statusCounts = agents.reduce((acc, agent) => { acc[agent.status] = (acc[agent.status] || 0) + 1 return acc }, {} as Record) if (loading && agents.length === 0) { return (
Loading agents...
) } return (
{/* Header */}

Agent Squad

{/* Status Summary */}
{Object.entries(statusCounts).map(([status, count]) => (
{count}
))}
{/* Error Display */} {error && (
{error}
)} {/* Agent Grid */}
{agents.length === 0 ? (
🤖

No agents found

Add your first agent to get started

) : (
{agents.map(agent => (
setSelectedAgent(agent)} > {/* Agent Header */}

{agent.name}

{agent.role}

{agent.status}
{/* Session Info */} {agent.session_key && (
Session: {agent.session_key}
)} {/* Task Stats */} {agent.taskStats && (
{agent.taskStats.total}
Total Tasks
{agent.taskStats.in_progress}
In Progress
)} {/* Last Activity */}
Last seen: {formatLastSeen(agent.last_seen)}
{agent.last_activity && (
Activity: {agent.last_activity}
)}
{/* Quick Actions */}
))}
)}
{/* Agent Detail Modal */} {selectedAgent && ( setSelectedAgent(null)} onUpdate={fetchAgents} onStatusUpdate={updateAgentStatus} /> )} {/* Create Agent Modal */} {showCreateModal && ( setShowCreateModal(false)} onCreated={fetchAgents} /> )}
) } // Agent Detail Modal function AgentDetailModal({ agent, onClose, onUpdate, onStatusUpdate }: { agent: Agent onClose: () => void onUpdate: () => void onStatusUpdate: (name: string, status: Agent['status'], activity?: string) => Promise }) { const [editing, setEditing] = useState(false) const [formData, setFormData] = useState({ role: agent.role, session_key: agent.session_key || '', soul_content: agent.soul_content || '', }) const handleSave = async () => { try { const response = await fetch('/api/agents', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: agent.name, ...formData }) }) if (!response.ok) throw new Error('Failed to update agent') setEditing(false) onUpdate() } catch (error) { log.error('Failed to update agent:', error) } } return (

{agent.name}

{agent.role}

{agent.status}
{/* Status Controls */}

Status Control

{(['idle', 'busy', 'offline'] as const).map(status => ( ))}
{/* Agent Details */}
{editing ? ( setFormData(prev => ({ ...prev, role: e.target.value }))} className="w-full bg-gray-700 text-white rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" /> ) : (

{agent.role}

)}
{editing ? ( setFormData(prev => ({ ...prev, session_key: e.target.value }))} className="w-full bg-gray-700 text-white rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" /> ) : (

{agent.session_key || 'Not set'}

)}
{editing ? (