feat(engine): 添加MCP连接管理和工具集成功能
- 集成MCP连接管理器,支持MCP服务器连接 - 添加多种内置工具:ClarifyTool、CronTool、DelegateTool、ExecuteCodeTool、 PatchFileTool、ProcessTool、SendMessageTool、SpawnTool、TerminalTool、 TodoTool、WebFetchTool、WebSearchTool、WriteFileTool等 - 实现工具注册和装配功能 - 添加技能选择上下文参数 - 支持思考模式控制参数thinking_enabled feat(coordinator): 重构任务执行计划器参数命名 - 将learning_candidate_enabled重命名为allow_candidate_generation - 更新TeamGraphScheduler中的参数传递 - 修改LocalAgentRunner中的相关参数处理 - 更新README文档中的相应描述 refactor(context): 标准化工具调用参数格式 - 添加_json导入用于参数序列化 - 实现_provider_tool_calls方法标准化OpenAI兼容的工具调用载荷 - 修复工具调用中参数非字符串类型的序列化问题 refactor(session): 优化消息历史记录过滤逻辑 - 修改get_messages_as_conversation为基于运行状态过滤消息 - 排除未完成、失败或错误结束的运行记录 - 改进对话历史的可见性控制机制 fix(store): 修复FTS索引重建逻辑 - 添加异常处理防止FTS索引创建失败 - 实现_rebuild_fts_index方法重新构建全文搜索索引 - 优化索引触发器和表的维护流程
This commit is contained in:
@ -2,9 +2,8 @@
|
||||
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { MessageSquare, Activity, Clock, Puzzle, Blocks, FolderOpen, Store, LogIn, UserPlus, Bot, ServerCog, Mail, LogOut, ChevronDown } from 'lucide-react';
|
||||
import { Bell, Bot, ChevronDown, ListTodo, LogOut, Mail, MessageSquare, PackageOpen, Puzzle, Settings, Store, Wrench } from 'lucide-react';
|
||||
import { logout } from '@/lib/api';
|
||||
import { LanguageSwitcher } from '@/components/LanguageSwitcher';
|
||||
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
|
||||
@ -16,17 +15,7 @@ import { useAppI18n } from '@/lib/i18n/provider';
|
||||
import { useChatStore } from '@/lib/store';
|
||||
|
||||
type NavItem = {
|
||||
key:
|
||||
| 'chat'
|
||||
| 'status'
|
||||
| 'office'
|
||||
| 'skills'
|
||||
| 'plugins'
|
||||
| 'agents'
|
||||
| 'mcp'
|
||||
| 'outlook'
|
||||
| 'marketplace'
|
||||
| 'files';
|
||||
key: 'chat' | 'tasks' | 'notifications' | 'skills' | 'tools' | 'agents' | 'outlook' | 'marketplace' | 'plugins' | 'settings';
|
||||
href: string;
|
||||
icon: React.ComponentType<{ className?: string }>;
|
||||
matchPrefixes?: string[];
|
||||
@ -34,22 +23,22 @@ type NavItem = {
|
||||
|
||||
const NAV_ITEMS: NavItem[] = [
|
||||
{ key: 'chat', href: '/', icon: MessageSquare },
|
||||
{ key: 'status', href: '/status', icon: Activity },
|
||||
{ key: 'office', href: '/office', icon: Clock, matchPrefixes: ['/office', '/cron'] },
|
||||
{ key: 'tasks', href: '/tasks', icon: ListTodo, matchPrefixes: ['/tasks', '/office', '/cron'] },
|
||||
{ key: 'notifications', href: '/notifications', icon: Bell, matchPrefixes: ['/notifications'] },
|
||||
{ key: 'skills', href: '/skills', icon: Puzzle },
|
||||
{ key: 'plugins', href: '/plugins', icon: Blocks },
|
||||
{ key: 'agents', href: '/agents', icon: Bot },
|
||||
{ key: 'mcp', href: '/mcp', icon: ServerCog },
|
||||
{ key: 'outlook', href: '/outlook', icon: Mail },
|
||||
{ key: 'marketplace', href: '/marketplace', icon: Store },
|
||||
{ key: 'files', href: '/files', icon: FolderOpen },
|
||||
{ key: 'tools', href: '/mcp', icon: Wrench, matchPrefixes: ['/mcp'] },
|
||||
{ key: 'agents', href: '/agents', icon: Bot, matchPrefixes: ['/agents'] },
|
||||
{ key: 'outlook', href: '/outlook', icon: Mail, matchPrefixes: ['/outlook'] },
|
||||
{ key: 'marketplace', href: '/marketplace', icon: Store, matchPrefixes: ['/marketplace'] },
|
||||
{ key: 'plugins', href: '/plugins', icon: PackageOpen, matchPrefixes: ['/plugins'] },
|
||||
{
|
||||
key: 'settings',
|
||||
href: '/settings',
|
||||
icon: Settings,
|
||||
matchPrefixes: ['/settings', '/status', '/logs'],
|
||||
},
|
||||
];
|
||||
|
||||
const AUTH_ITEMS = [
|
||||
{ key: 'login', href: '/login', icon: LogIn },
|
||||
{ key: 'register', href: '/register', icon: UserPlus },
|
||||
] as const;
|
||||
|
||||
function ConnectionDot() {
|
||||
const { locale } = useAppI18n();
|
||||
const wsStatus = useChatStore((s) => s.wsStatus);
|
||||
@ -61,10 +50,10 @@ function ConnectionDot() {
|
||||
const isOffline = wsStatus === 'disconnected' || (wsStatus === 'connected' && nanobotReady === false);
|
||||
|
||||
const color = isOnline
|
||||
? 'bg-green-500'
|
||||
? 'bg-[#869683]'
|
||||
: isConnecting
|
||||
? 'bg-yellow-500'
|
||||
: 'bg-red-500';
|
||||
? 'bg-[#8B7E77]'
|
||||
: 'bg-[#5F5550]';
|
||||
|
||||
const label = appConnectionStatusLabel(wsStatus, nanobotReady, locale);
|
||||
|
||||
@ -86,23 +75,17 @@ const Header = () => {
|
||||
|
||||
const navLabel = React.useCallback((key: NavItem['key']) => {
|
||||
if (key === 'chat') return pickAppText(locale, '对话', 'Chat');
|
||||
if (key === 'status') return pickAppText(locale, '状态', 'Status');
|
||||
if (key === 'office') return pickAppText(locale, '任务管理', 'Tasks');
|
||||
if (key === 'tasks') return 'Task';
|
||||
if (key === 'notifications') return pickAppText(locale, '通知', 'Notifications');
|
||||
if (key === 'skills') return pickAppText(locale, '技能', 'Skills');
|
||||
if (key === 'plugins') return pickAppText(locale, '插件', 'Plugins');
|
||||
if (key === 'tools') return pickAppText(locale, '工具', 'Tools');
|
||||
if (key === 'agents') return pickAppText(locale, '智能体', 'Agents');
|
||||
if (key === 'mcp') return 'MCP';
|
||||
if (key === 'outlook') return 'Outlook';
|
||||
if (key === 'marketplace') return pickAppText(locale, '市场', 'Marketplace');
|
||||
return pickAppText(locale, '文件', 'Files');
|
||||
if (key === 'plugins') return pickAppText(locale, '插件', 'Plugins');
|
||||
return pickAppText(locale, '配置', 'Settings');
|
||||
}, [locale]);
|
||||
|
||||
const authLabel = React.useCallback((key: 'login' | 'register') => (
|
||||
key === 'login'
|
||||
? pickAppText(locale, '登录', 'Sign In')
|
||||
: pickAppText(locale, '注册', 'Sign Up')
|
||||
), [locale]);
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logout();
|
||||
setUser(null);
|
||||
@ -113,24 +96,16 @@ const Header = () => {
|
||||
const userInitial = (user?.username || user?.email || '?').trim().charAt(0).toUpperCase();
|
||||
|
||||
return (
|
||||
<header className="fixed top-0 left-0 right-0 bg-background border-b border-border z-50">
|
||||
<div className="max-w-[1720px] mx-auto px-5 sm:px-6 lg:px-8 xl:px-10">
|
||||
<div className="flex items-center h-16 gap-6">
|
||||
<Link href="/" className="flex shrink-0 items-center gap-3 pr-2">
|
||||
<Image
|
||||
src="/boardware-logo.jpg"
|
||||
alt="Boardware logo"
|
||||
width={40}
|
||||
height={32}
|
||||
className="h-8 w-10 shrink-0 rounded-sm bg-white object-contain p-0.5"
|
||||
/>
|
||||
<span className="whitespace-nowrap text-[1.05rem] font-semibold leading-none tracking-tight sm:text-[1.15rem]">
|
||||
Boardware Agent Sandbox
|
||||
<header className="fixed left-0 right-0 top-0 z-50 border-b border-[#E6E1DE] bg-[#F7F6F5]/95 backdrop-blur">
|
||||
<div className="mx-auto max-w-[1720px] px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid h-16 grid-cols-[minmax(120px,1fr)_auto_minmax(120px,1fr)] items-center gap-4">
|
||||
<Link href="/" className="flex shrink-0 items-center">
|
||||
<span className="font-serif text-[28px] font-semibold leading-none text-[#0B0B0B]">
|
||||
Beaver
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<div className="flex min-w-0 flex-1 items-center justify-end gap-3">
|
||||
<nav className="flex min-w-0 flex-1 items-center gap-1 overflow-x-auto whitespace-nowrap [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
|
||||
<nav className="flex items-center gap-1 rounded-full border border-[#E6E1DE] bg-white px-1.5 py-1 shadow-[0_1px_2px_rgba(0,0,0,0.04)]">
|
||||
{NAV_ITEMS.map((item) => {
|
||||
const isActive =
|
||||
item.href === '/'
|
||||
@ -141,10 +116,10 @@ const Header = () => {
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={`flex shrink-0 items-center gap-1.5 rounded-md px-3 py-2 text-sm font-medium transition-colors ${
|
||||
className={`flex shrink-0 items-center gap-1.5 rounded-full px-4 py-2 text-sm font-medium transition-colors ${
|
||||
isActive
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'text-muted-foreground hover:bg-accent hover:text-accent-foreground'
|
||||
: 'text-[#4F4642] hover:bg-[#F7F5F4] hover:text-[#0B0B0B]'
|
||||
}`}
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
@ -154,26 +129,30 @@ const Header = () => {
|
||||
})}
|
||||
</nav>
|
||||
|
||||
<div className="flex shrink-0 items-center gap-2 border-l border-border pl-4">
|
||||
<div className="flex min-w-0 items-center justify-end gap-3">
|
||||
<div className="hidden shrink-0 sm:block">
|
||||
<ConnectionDot />
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<LanguageSwitcher />
|
||||
{user ? (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 rounded-full border border-border/70 bg-background px-2 py-1.5 text-sm font-medium text-foreground transition-colors hover:bg-accent hover:text-accent-foreground"
|
||||
className="flex items-center gap-2 rounded-full border border-[#E6E1DE] bg-white px-2 py-1.5 text-sm font-medium text-[#1D1715] transition-colors hover:bg-[#F7F5F4]"
|
||||
>
|
||||
<Avatar className="h-8 w-8 border border-border/60">
|
||||
<Avatar className="h-8 w-8 border border-[#E6E1DE]">
|
||||
<AvatarFallback className="bg-primary text-xs font-semibold text-primary-foreground">
|
||||
{userInitial}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<span className="hidden max-w-28 truncate sm:block">{user.username}</span>
|
||||
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
||||
</Avatar>
|
||||
<span className="hidden max-w-28 truncate sm:block">{user.username}</span>
|
||||
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="end" className="w-80 rounded-3xl border-border/70 p-0 shadow-2xl">
|
||||
<div className="overflow-hidden rounded-3xl bg-gradient-to-b from-slate-50 via-slate-50 to-white">
|
||||
<div className="overflow-hidden rounded-3xl bg-[linear-gradient(180deg,#F7F5F4,#FFFFFF)]">
|
||||
<div className="border-b border-border/60 px-6 py-5">
|
||||
<p className="truncate text-center text-sm font-medium text-muted-foreground">
|
||||
{user.email}
|
||||
@ -210,30 +189,7 @@ const Header = () => {
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
) : !isAuthLoading ? (
|
||||
AUTH_ITEMS.map((item) => {
|
||||
const isActive = pathname.startsWith(item.href);
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={`flex items-center gap-1.5 rounded-md px-3 py-2 text-sm font-medium transition-colors ${
|
||||
isActive
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'text-muted-foreground hover:bg-accent hover:text-accent-foreground'
|
||||
}`}
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
{authLabel(item.key)}
|
||||
</Link>
|
||||
);
|
||||
})
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 border-l border-border pl-4">
|
||||
<ConnectionDot />
|
||||
) : !isAuthLoading ? null : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user