diff --git a/src/components/SkillSettings.tsx b/src/components/SkillSettings.tsx
index b91ebe6..5e90208 100644
--- a/src/components/SkillSettings.tsx
+++ b/src/components/SkillSettings.tsx
@@ -1,14 +1,23 @@
-import { useCallback, useEffect, useState } from 'react';
+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,
@@ -51,6 +60,75 @@ function SkillGrid({
);
}
+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,
@@ -62,8 +140,12 @@ export function SkillSettings({
}) {
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);
@@ -73,12 +155,16 @@ export function SkillSettings({
setLoading(true);
setError(null);
try {
- const [catalogItems, roleState] = await Promise.all([
+ 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 {
@@ -89,13 +175,24 @@ export function SkillSettings({
const loadUserSkills = useCallback(async (userId: string) => {
if (!userId) {
setUserSkills({});
+ setUserWebEnabled(false);
+ setUserWebOverride(null);
setUserOverrides({});
return;
}
try {
- const state = await getUserSkills(userId);
+ 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 : '加载用户技能失败');
}
@@ -119,9 +216,14 @@ export function SkillSettings({
setMessage(null);
setError(null);
try {
- const result = await updateRoleSkills('user', roleSkills);
- setRoleSkills(result.skills);
- setMessage('普通用户默认技能已保存,并已同步到各用户工作区');
+ 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 : '保存失败');
}
@@ -132,10 +234,15 @@ export function SkillSettings({
setMessage(null);
setError(null);
try {
- const result = await updateUserSkills(selectedUserId, userSkills);
- setUserSkills(result.skills);
+ 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 发布目录');
+ setMessage('用户技能与联网工具已保存,并已安装到其 MindSpace 发布目录');
} catch (err) {
setError(err instanceof Error ? err.message : '保存失败');
}
@@ -146,15 +253,28 @@ export function SkillSettings({
setMessage(null);
setError(null);
try {
- await clearUserSkillOverrides(selectedUserId);
+ await Promise.all([
+ clearUserSkillOverrides(selectedUserId),
+ hasWebCapabilityCatalog ? clearUserCapabilityOverrides(selectedUserId) : Promise.resolve(),
+ ]);
await loadUserSkills(selectedUserId);
- setMessage('已恢复为角色默认技能');
+ 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 (
@@ -166,18 +286,36 @@ export function SkillSettings({
勾选后技能会安装到用户 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}
普通用户默认技能
>
)}
- {!userOnly && 单用户技能覆盖
}
+ {!userOnly && 单用户覆盖
}
{!userOnly && (