/** * AgentsPanel Component * * Agent 预设管理面板:列出所有 Agent、查看详情、创建/编辑/删除 */ import { useState, useEffect, useCallback } from 'react'; import { X, RefreshCw, Bot, Plus, Settings, ChevronDown, ChevronRight, Eye, Copy, Trash2, Edit3, Sparkles, Cpu, Layers, Lock, } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; import { toast } from 'sonner'; import { cn } from '../utils/cn'; import { modalOverlay, modalContent, smoothTransition } from '../utils/animations'; import { Button } from '../primitives/Button'; import { Skeleton } from './Skeleton'; import { AgentEditor } from './AgentEditor'; import { AgentDefaultsEditor } from './AgentDefaultsEditor'; import { listAgents, getAgent, deleteAgent, type AgentListItem, type AgentDetail, } from '../api/client.js'; interface AgentsPanelProps { onClose: () => void; /** 是否启用响应式布局 */ responsive?: boolean; } // 模式颜色映射 function getModeColor(mode: AgentListItem['mode']) { switch (mode) { case 'primary': return 'bg-blue-500/20 text-blue-400'; case 'subagent': return 'bg-purple-500/20 text-purple-400'; case 'all': return 'bg-green-500/20 text-green-400'; case 'internal': return 'bg-slate-500/20 text-slate-400'; default: return 'bg-surface-muted/20 text-fg-muted'; } } // 模式文字 function getModeText(mode: AgentListItem['mode']) { switch (mode) { case 'primary': return 'Primary'; case 'subagent': return 'Subagent'; case 'all': return 'Both'; case 'internal': return 'Internal'; default: return mode; } } export function AgentsPanel({ onClose, responsive = false }: AgentsPanelProps) { // 数据状态 const [agents, setAgents] = useState([]); const [expandedAgents, setExpandedAgents] = useState>(new Set()); const [agentDetails, setAgentDetails] = useState>({}); // UI 状态 const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [actionLoading, setActionLoading] = useState(null); // 编辑器状态 const [editingAgent, setEditingAgent] = useState<{ name: string; isNew: boolean } | null>(null); const [showDefaultsEditor, setShowDefaultsEditor] = useState(false); // 加载 Agent 列表 const loadAgents = useCallback(async (showToast = false) => { try { const result = await listAgents(); if (result.success) { setAgents(result.data); if (showToast) { toast.success('Agents refreshed'); } } else { toast.error(result.error || 'Failed to load agents'); } } catch (err) { toast.error(err instanceof Error ? err.message : 'Failed to load agents'); } }, []); // 初始加载 useEffect(() => { setLoading(true); loadAgents().finally(() => setLoading(false)); }, [loadAgents]); // 刷新 const handleRefresh = async () => { setRefreshing(true); await loadAgents(true); setRefreshing(false); }; // 加载 Agent 详情 const loadAgentDetail = async (name: string) => { if (agentDetails[name]) return agentDetails[name]; try { const result = await getAgent(name); if (result.success && result.data) { setAgentDetails((prev) => ({ ...prev, [name]: result.data! })); return result.data; } } catch (err) { toast.error(`Failed to load agent details: ${err instanceof Error ? err.message : 'Unknown error'}`); } return null; }; // 切换展开 const toggleExpanded = async (name: string) => { const newExpanded = new Set(expandedAgents); if (newExpanded.has(name)) { newExpanded.delete(name); } else { newExpanded.add(name); // 懒加载详情 await loadAgentDetail(name); } setExpandedAgents(newExpanded); }; // 删除 Agent const handleDelete = async (name: string) => { if (!confirm(`Are you sure you want to delete agent "${name}"?`)) { return; } setActionLoading(name); try { const result = await deleteAgent(name); if (result.success) { toast.success(`Agent "${name}" deleted`); await loadAgents(); // 清除详情缓存 setAgentDetails((prev) => { const newDetails = { ...prev }; delete newDetails[name]; return newDetails; }); } else { toast.error(result.error || 'Delete failed'); } } catch (err) { toast.error(err instanceof Error ? err.message : 'Delete failed'); } finally { setActionLoading(null); } }; // 复制 Agent(打开编辑器,以新名称创建) const handleCopy = async (name: string) => { const detail = await loadAgentDetail(name); if (detail) { setEditingAgent({ name: `${name}-copy`, isNew: true }); } }; // 编辑器保存成功回调 const handleEditorSave = () => { setEditingAgent(null); loadAgents(); // 清除所有详情缓存以获取最新数据 setAgentDetails({}); }; // 统计 - 按分类过滤 const internalAgents = agents.filter((a) => a.mode === 'internal'); const presetAgents = agents.filter((a) => a.isPreset && a.mode !== 'internal'); const customAgents = agents.filter((a) => !a.isPreset && a.mode !== 'internal'); // Loading 骨架屏 const LoadingSkeleton = () => (
{[1, 2, 3, 4].map((i) => (
))}
); // Agent 项组件 const AgentItem = ({ agent }: { agent: AgentListItem }) => { const isExpanded = expandedAgents.has(agent.name); const isLoading = actionLoading === agent.name; const detail = agentDetails[agent.name]; const isInternal = agent.mode === 'internal'; return ( {/* Agent Header */}
toggleExpanded(agent.name)} > {/* Expand Icon */} {/* Icon */} {isInternal ? ( ) : agent.isPreset ? ( ) : ( )} {/* Info */}
{agent.name} {getModeText(agent.mode)} {agent.isCustomized && ( Customized )}

{agent.description}

{/* Actions */}
e.stopPropagation()}> {isLoading ? (
) : ( <> {/* View/Edit - Internal agents can be edited (model config) */} {isInternal ? ( ) : agent.isPreset && !agent.isCustomized ? ( ) : ( )} {/* Copy - not for internal agents */} {!isInternal && ( )} {/* Delete (only for custom agents, not internal) */} {!agent.isPreset && !isInternal && ( )} )}
{/* Expanded Content */} {isExpanded && (
{detail ? ( <> {/* Model Info */} {detail.model && (
Model:{' '} {detail.model.provider && `${detail.model.provider}/`} {detail.model.model || 'default'} {detail.model.temperature !== undefined && ( temp: {detail.model.temperature} )}
)} {/* Tools Config */} {detail.tools && (
Tools:{' '} {detail.tools.enabled ? ( Only: {detail.tools.enabled.join(', ')} ) : detail.tools.disabled ? ( Disabled: {detail.tools.disabled.join(', ')} ) : ( All enabled )} {detail.tools.noTask && ( (No nested tasks) )}
)} {/* Max Steps */} {detail.maxSteps && (
Max Steps:{' '} {detail.maxSteps}
)} {/* Prompt Preview */} {detail.prompt && (
System Prompt:
                          {detail.prompt.slice(0, 500)}
                          {detail.prompt.length > 500 && '...'}
                        
)} ) : (
)}
)} ); }; // 如果正在编辑 Agent if (editingAgent) { return ( setEditingAgent(null)} onSave={handleEditorSave} responsive={responsive} /> ); } // 如果正在编辑全局默认配置 if (showDefaultsEditor) { return ( setShowDefaultsEditor(false)} onSave={() => { setShowDefaultsEditor(false); loadAgents(); }} responsive={responsive} /> ); } return ( e.stopPropagation()} className={cn( 'bg-surface-subtle max-h-[90vh] overflow-hidden flex flex-col', responsive ? 'w-full md:w-full md:max-w-2xl md:mx-4 rounded-t-2xl md:rounded-lg' : 'rounded-lg w-full max-w-2xl mx-4' )} > {/* Header */}
{responsive && (
)}

Agent Presets

{agents.length} agents ({internalAgents.length} system, {presetAgents.length} preset, {customAgents.length} custom)

{/* Agent List */}
{loading ? ( ) : agents.length === 0 ? (

No agents available

) : ( {/* System Agents (Internal) */} {internalAgents.length > 0 && (

System Agents

{internalAgents.map((agent) => ( ))}
)} {/* Preset Agents */} {presetAgents.length > 0 && (

Preset Agents

{presetAgents.map((agent) => ( ))}
)} {/* Custom Agents */}

Custom Agents

{customAgents.length > 0 ? (
{customAgents.map((agent) => ( ))}
) : (

No custom agents yet

)}
)}
{/* Footer Info */}
Config stored in{' '} .ai-assist/agents.json
); }