import { useCallback, useEffect, useMemo, useState } from 'react'; import { clearUserCapabilityOverrides, clearUserSkillOverrides, getRoleCapabilities, getRoleSkills, getUserCapabilities, getUserSkills, listCapabilityCatalog, listSkillCatalog, updateRoleCapabilities, updateRoleSkills, updateUserCapabilities, updateUserSkills, } from '../api/client'; import type { AdminUserRow, SkillDefinition, SkillMap } from '../types'; const WEB_CAPABILITY_KEY = 'web'; const WEB_SKILL_NAME = 'web'; function SkillGrid({ catalog, values, disabled, onChange, }: { catalog: SkillDefinition[]; values: SkillMap; disabled?: boolean; onChange: (name: string, enabled: boolean) => void; }) { return (

平台技能

); } function WebSearchToolPanel({ roleEnabled, userEnabled, showUser, disabled, onRoleChange, onUserChange, }: { roleEnabled: boolean; userEnabled: boolean; showUser: boolean; disabled?: boolean; onRoleChange: (enabled: boolean) => void; onUserChange: (enabled: boolean) => void; }) { return (

联网搜索工具

platform/web
); } export function SkillSettings({ users, userId, userOnly = false, }: { users: AdminUserRow[]; userId?: string; userOnly?: boolean; }) { const [catalog, setCatalog] = useState([]); const [roleSkills, setRoleSkills] = useState({}); const [roleWebEnabled, setRoleWebEnabled] = useState(false); const [hasWebCapabilityCatalog, setHasWebCapabilityCatalog] = useState(false); const [selectedUserId, setSelectedUserId] = useState(userId ?? ''); const [userSkills, setUserSkills] = useState({}); const [userWebEnabled, setUserWebEnabled] = useState(false); const [userWebOverride, setUserWebOverride] = useState(null); const [userOverrides, setUserOverrides] = useState({}); const [loading, setLoading] = useState(true); const [message, setMessage] = useState(null); const [error, setError] = useState(null); const load = useCallback(async () => { setLoading(true); setError(null); try { const [catalogItems, roleState, capabilityCatalog, roleCapabilities] = await Promise.all([ listSkillCatalog(), getRoleSkills('user'), listCapabilityCatalog(), getRoleCapabilities('user'), ]); setCatalog(catalogItems); setRoleSkills(roleState.skills); setHasWebCapabilityCatalog(capabilityCatalog.some((item) => item.key === WEB_CAPABILITY_KEY)); setRoleWebEnabled(Boolean(roleCapabilities.capabilities[WEB_CAPABILITY_KEY])); } catch (err) { setError(err instanceof Error ? err.message : '加载技能目录失败'); } finally { setLoading(false); } }, []); const loadUserSkills = useCallback(async (userId: string) => { if (!userId) { setUserSkills({}); setUserWebEnabled(false); setUserWebOverride(null); setUserOverrides({}); return; } try { const [state, capabilityState] = await Promise.all([ getUserSkills(userId), getUserCapabilities(userId), ]); setUserSkills(state.skills); setUserOverrides(state.overrides); setUserWebEnabled(Boolean(capabilityState.capabilities[WEB_CAPABILITY_KEY])); setUserWebOverride( Object.prototype.hasOwnProperty.call(capabilityState.overrides, WEB_CAPABILITY_KEY) ? Boolean(capabilityState.overrides[WEB_CAPABILITY_KEY]) : null, ); } catch (err) { setError(err instanceof Error ? err.message : '加载用户技能失败'); } }, []); useEffect(() => { void load(); }, [load]); useEffect(() => { void loadUserSkills(selectedUserId); }, [loadUserSkills, selectedUserId]); useEffect(() => { if (userId) { setSelectedUserId(userId); } }, [userId]); const saveRoleDefaults = async () => { setMessage(null); setError(null); try { const [skillsResult] = await Promise.all([ updateRoleSkills('user', roleSkills), hasWebCapabilityCatalog ? updateRoleCapabilities('user', { [WEB_CAPABILITY_KEY]: roleWebEnabled }) : Promise.resolve(null), ]); setRoleSkills(skillsResult.skills); setMessage('普通用户默认技能与联网工具已保存,并已同步到各用户工作区'); } catch (err) { setError(err instanceof Error ? err.message : '保存失败'); } }; const saveUserOverrides = async () => { if (!selectedUserId) return; setMessage(null); setError(null); try { const [skillsResult] = await Promise.all([ updateUserSkills(selectedUserId, userSkills), hasWebCapabilityCatalog ? updateUserCapabilities(selectedUserId, { [WEB_CAPABILITY_KEY]: userWebEnabled }) : Promise.resolve(null), ]); setUserSkills(skillsResult.skills); await loadUserSkills(selectedUserId); setMessage('用户技能与联网工具已保存,并已安装到其 MindSpace 发布目录'); } catch (err) { setError(err instanceof Error ? err.message : '保存失败'); } }; const resetUserOverrides = async () => { if (!selectedUserId) return; setMessage(null); setError(null); try { await Promise.all([ clearUserSkillOverrides(selectedUserId), hasWebCapabilityCatalog ? clearUserCapabilityOverrides(selectedUserId) : Promise.resolve(), ]); await loadUserSkills(selectedUserId); setMessage('已恢复为角色默认技能与联网工具'); } catch (err) { setError(err instanceof Error ? err.message : '重置失败'); } }; const regularUsers = users.filter((user) => user.role === 'user'); const webSkillEnabledForRole = Boolean(roleSkills[WEB_SKILL_NAME]); const webSkillEnabledForUser = Boolean(userSkills[WEB_SKILL_NAME]); const webMismatchRole = useMemo( () => hasWebCapabilityCatalog && webSkillEnabledForRole && !roleWebEnabled, [hasWebCapabilityCatalog, webSkillEnabledForRole, roleWebEnabled], ); const webMismatchUser = useMemo( () => hasWebCapabilityCatalog && webSkillEnabledForUser && !userWebEnabled, [hasWebCapabilityCatalog, webSkillEnabledForUser, userWebEnabled], ); return (

技能配置

勾选后技能会安装到用户 MindSpace/用户名/.agents/skills/,对话中可通过 load_skill 使用。开通「静态页面发布」技能会自动启用页面写入能力。联网搜索需在下方同时开启{' '} 网页搜索工具web 技能。

{error &&

{error}

} {message &&

{message}

} {loading ? (

加载中…

) : !hasWebCapabilityCatalog ? (

当前 Portal 能力目录缺少 web,请更新 Memind 后端并重启 admin-api。

) : catalog.length === 0 ? (

暂无平台技能,请在 Memind/skills/ 下添加 SKILL.md

) : ( <> {!userOnly && ( <>

普通用户默认

{webMismatchRole ? (

已开启 web 技能但未启用网页搜索工具,Agent 将无法使用 web_search。

) : null}

普通用户默认技能

setRoleSkills((prev) => ({ ...prev, [name]: enabled }))} />
)} {!userOnly &&

单用户覆盖

} {!userOnly && (
)} {selectedUserId && ( <> {userWebOverride != null && (

联网工具已单独覆盖为 {userWebOverride ? '开启' : '关闭'}。

)} {webMismatchUser ? (

该用户已开启 web 技能但未启用网页搜索工具。

) : null} {Object.keys(userOverrides).length > 0 && (

已设置 {Object.keys(userOverrides).length} 项技能单独覆盖。

)}

用户技能

setUserSkills((prev) => ({ ...prev, [name]: enabled })) } />
)} )}
); }