diff --git a/web/src/components/Settings/ConfigPage.tsx b/web/src/components/Settings/ConfigPage.tsx index 3b72a9e..73117d8 100644 --- a/web/src/components/Settings/ConfigPage.tsx +++ b/web/src/components/Settings/ConfigPage.tsx @@ -1,7 +1,7 @@ import { useState, useEffect, useCallback } from 'react' import { Settings, Save, X, Plus, Trash2, AlertTriangle, Loader2, Wifi, - CheckCircle, RefreshCw, UserCheck, Pencil, + CheckCircle, RefreshCw, UserCheck, Pencil, Bot, } from 'lucide-react' // ── Extracted modules ───────────────────────────────── @@ -10,16 +10,19 @@ import type { ProviderConfig, ModelConfig, AgentConfig, McpServerConfig, McpStatusResponse, SkillListResponse, - SubagentListResponse, + SubagentListResponse, SubagentItem, + ToolsListResponse, ExpertItem, ExpertListResponse, + CapabilityPolicy, KnownSource, SchedulerConfig, ChannelConfig, } from './types' import { TABS, inputCls, selectCls, TIMEZONE_OPTIONS } from './constants' -import { Field, Toggle, TagEditor, SectionCard, SourceEditor, MapEntryHeader } from './ui' +import { Field, Toggle, TagEditor, SectionCard, SourceEditor, MapEntryHeader, CheckboxList } from './ui' import { getAppConfig, updateAppConfig, restartGateway, checkHealth } from '../../api/config' import { listSkills, toggleSkill } from '../../api/skills' -import { listSubagents, toggleSubagent } from '../../api/subagents' +import { listTools } from '../../api/tools' +import { listSubagents, toggleSubagent, updateSubagent } from '../../api/subagents' import { listExperts, toggleExpert, createExpert, updateExpert, deleteExpert } from '../../api/experts' import { getMcpStatus } from '../../api/mcp' export { getSelectedExpert, selectExpert } from '../../api/experts' @@ -47,6 +50,8 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage const [mcpStatus, setMcpStatus] = useState(null) const [skillList, setSkillList] = useState(null) const [skillListLoading, setSkillListLoading] = useState(false) + const [toolList, setToolList] = useState(null) + const [toolListLoading, setToolListLoading] = useState(false) const [subagentList, setSubagentList] = useState(null) const [subagentListLoading, setSubagentListLoading] = useState(false) const [expertList, setExpertList] = useState(null) @@ -58,9 +63,23 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage nameField: string description: string body: string + allowedSkills: string[] + deniedSkills: string[] + allowedTools: string[] + deniedTools: string[] } | null>(null) const [editingExpertError, setEditingExpertError] = useState('') const [savingExpert, setSavingExpert] = useState(false) + const [editingSubagent, setEditingSubagent] = useState<{ + name: string + description: string + allowedSkills: string[] + deniedSkills: string[] + allowedTools: string[] + deniedTools: string[] + } | null>(null) + const [editingSubagentError, setEditingSubagentError] = useState('') + const [savingSubagent, setSavingSubagent] = useState(false) const fetchMcpStatus = useCallback(async () => { const data = await getMcpStatus() @@ -74,6 +93,13 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage setSkillListLoading(false) }, []) + const fetchToolList = useCallback(async () => { + setToolListLoading(true) + const data = await listTools() + if (data) setToolList(data) + setToolListLoading(false) + }, []) + const toggleSkillCb = useCallback(async (name: string, scope: string, enabled: boolean) => { return toggleSkill(name, scope, enabled) }, []) @@ -89,6 +115,10 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage return toggleSubagent(name, scope, enabled) }, []) + const updateSubagentCb = useCallback(async (payload: { name: string; description?: string; body?: string; capability?: CapabilityPolicy }) => { + return updateSubagent(payload) + }, []) + const fetchExpertList = useCallback(async () => { setExpertListLoading(true) const data = await listExperts() @@ -100,11 +130,11 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage return toggleExpert(name, scope, enabled) }, []) - const createExpertCb = useCallback(async (payload: { name: string; description: string; body: string; scope: string }) => { + const createExpertCb = useCallback(async (payload: { name: string; description: string; body: string; scope: string; capability?: CapabilityPolicy }) => { return createExpert(payload) }, []) - const updateExpertCb = useCallback(async (payload: { name: string; scope: string; description?: string; body?: string }) => { + const updateExpertCb = useCallback(async (payload: { name: string; scope: string; description?: string; body?: string; capability?: CapabilityPolicy }) => { return updateExpert(payload) }, []) @@ -141,6 +171,14 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage if (activeTab === 'subagents') fetchSubagentList() }, [activeTab, fetchSubagentList]) + // Fetch skills + tools when experts/subagents tab is selected (for capability CheckboxList) + useEffect(() => { + if (activeTab === 'experts' || activeTab === 'subagents') { + if (!skillList) fetchSkillList() + if (!toolList) fetchToolList() + } + }, [activeTab, fetchSkillList, fetchToolList, skillList, toolList]) + // Fetch expert list when experts tab is selected useEffect(() => { if (activeTab === 'experts') fetchExpertList() @@ -568,6 +606,7 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage /> {renderDiscoveredSubagents()} + {renderSubagentModal()} ) @@ -636,6 +675,18 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage ) } + const handleEditSubagent = (subagent: SubagentItem) => { + setEditingSubagentError('') + setEditingSubagent({ + name: subagent.name, + description: subagent.description, + allowedSkills: subagent.capability?.allowed_skills ?? [], + deniedSkills: subagent.capability?.denied_skills ?? [], + allowedTools: subagent.capability?.allowed_tools ?? [], + deniedTools: subagent.capability?.denied_tools ?? [], + }) + } + return ( {subagentListLoading && subagents.length === 0 ? ( @@ -648,6 +699,7 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage
{subagents.map(subagent => { const isEnabled = subagent.disabled_in_scopes.length === 0 + const isBuiltin = subagent.source === 'builtin' return (
@@ -656,9 +708,18 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage {subagent.source}

{subagent.description}

- {renderToolTags('允许', subagent.allowed_tools, 'allow')} - {renderToolTags('禁用', subagent.denied_tools, 'deny')} + {renderToolTags('允许', subagent.capability?.allowed_tools, 'allow')} + {renderToolTags('禁用', subagent.capability?.denied_tools, 'deny')}
+ {!isBuiltin && ( + + )} handleToggle(subagent.name, isEnabled)} />
) @@ -691,7 +752,7 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage + + + + + ) + } + const renderMcp = () => { const entries = Object.entries(config.mcpServers) const statusFor = (key: string) => mcpStatus?.servers?.find(s => s.key === key)