feat(agent): 添加对持久化子智能体的支持并增强委派管理

添加了持久化子智能体的完整生命周期管理功能,包括创建、更新、删除和查询API接口。
新增了子智能体的JSON-RPC通信协议支持,实现了远程调用和任务管理功能。

同时增强了委派管理器的功能:
- 添加了对本地委派、插件委派和本地回退的开关控制
- 实现了持久化子智能体任务的自动检测和本地执行保护
- 增加了对不同委派类型的权限验证机制

修改了智能体注册表以支持插件智能体的条件性包含,并更新了工具注册逻辑以支持可选工具。

BREAKING CHANGE: 委派管理器的构造函数签名已更改,添加了新的控制参数。
```
This commit is contained in:
2026-03-27 10:15:35 +08:00
parent bad1e16ab4
commit 29dfd14aa6
133 changed files with 11656 additions and 220 deletions

View File

@ -3,8 +3,9 @@
import React from 'react';
import { Bot, Loader2, Paperclip, User } from 'lucide-react';
import type { ChatMessage } from '@/types';
import type { ChatMessage, ProcessArtifact, ProcessEvent, ProcessRun } from '@/types';
import { getAccessToken, getFileUrl } from '@/lib/api';
import { AgentTeamBlock } from '@/components/chat-workbench/AgentTeamBlock';
import { MarkdownContent } from '@/components/chat-workbench/MarkdownContent';
import { ScrollArea } from '@/components/ui/scroll-area';
@ -108,21 +109,120 @@ function MessageBubble({ message }: { message: ChatMessage }) {
);
}
type AgentTeamGroup = {
rootRun: ProcessRun;
memberRuns: ProcessRun[];
startedAt: string;
};
function parseTimelineTime(value?: string | null): number | null {
if (!value) return null;
const parsed = new Date(value).getTime();
return Number.isFinite(parsed) ? parsed : null;
}
function buildAgentTeamGroups(processRuns: ProcessRun[]): AgentTeamGroup[] {
const runMap = new Map(processRuns.map((run) => [run.run_id, run]));
const groups = new Map<string, AgentTeamGroup>();
for (const run of processRuns) {
if (run.actor_type !== 'agent') {
continue;
}
let root = run;
const seen = new Set<string>([run.run_id]);
let parentId = run.parent_run_id ?? null;
while (parentId) {
const parent = runMap.get(parentId);
if (!parent || seen.has(parent.run_id)) {
break;
}
root = parent;
seen.add(parent.run_id);
parentId = parent.parent_run_id ?? null;
}
const existing = groups.get(root.run_id);
if (existing) {
existing.memberRuns.push(run);
continue;
}
groups.set(root.run_id, {
rootRun: root,
memberRuns: [run],
startedAt: root.started_at || run.started_at,
});
}
return Array.from(groups.values())
.map((group) => ({
...group,
memberRuns: [...group.memberRuns].sort((a: ProcessRun, b: ProcessRun) => {
const at = parseTimelineTime(a.started_at) ?? 0;
const bt = parseTimelineTime(b.started_at) ?? 0;
return at - bt;
}),
}))
.sort((a, b) => {
const at = parseTimelineTime(a.startedAt) ?? 0;
const bt = parseTimelineTime(b.startedAt) ?? 0;
return at - bt;
});
}
export function MessageList({
messages,
isThinking,
messagesEndRef,
viewportRef,
processRuns,
processEvents,
processArtifacts,
selectedRunId,
onSelectRun,
onCancelRun,
}: {
messages: ChatMessage[];
isThinking: boolean;
messagesEndRef: React.RefObject<HTMLDivElement>;
viewportRef: React.RefObject<HTMLDivElement>;
processRuns: ProcessRun[];
processEvents: ProcessEvent[];
processArtifacts: ProcessArtifact[];
selectedRunId: string | null;
onSelectRun: (runId: string) => void;
onCancelRun: (runId: string) => void;
}) {
const teamGroups = React.useMemo(() => buildAgentTeamGroups(processRuns), [processRuns]);
const timelineItems = React.useMemo(() => {
const messageItems = messages.map((message, index) => ({
kind: 'message' as const,
key: `${message.role}:${message.timestamp || index}:${index}`,
sortTime: parseTimelineTime(message.timestamp) ?? Number.MAX_SAFE_INTEGER / 2 + index,
order: index,
message,
}));
const teamItems = teamGroups.map((group, index) => ({
kind: 'team' as const,
key: `team:${group.rootRun.run_id}`,
sortTime: parseTimelineTime(group.startedAt) ?? Number.MAX_SAFE_INTEGER / 2 + messages.length + index,
order: messages.length + index,
group,
}));
return [...messageItems, ...teamItems].sort((a, b) => {
if (a.sortTime !== b.sortTime) {
return a.sortTime - b.sortTime;
}
return a.order - b.order;
});
}, [messages, teamGroups]);
return (
<ScrollArea className="h-full px-4" viewportRef={viewportRef}>
<div className="max-w-4xl mx-auto py-4 space-y-4">
{messages.length === 0 && !isThinking && (
<div className="max-w-6xl mx-auto py-4 space-y-4">
{messages.length === 0 && teamGroups.length === 0 && !isThinking && (
<div className="flex flex-col items-center justify-center py-20 text-muted-foreground">
<Bot className="w-12 h-12 mb-4 opacity-50" />
<p className="text-lg font-medium">Boardware Agent Sandbox</p>
@ -130,9 +230,22 @@ export function MessageList({
</div>
)}
{messages.map((msg, i) => (
<MessageBubble key={`${msg.role}:${msg.timestamp || i}:${i}`} message={msg} />
))}
{timelineItems.map((item) =>
item.kind === 'message' ? (
<MessageBubble key={item.key} message={item.message} />
) : (
<AgentTeamBlock
key={item.key}
rootRun={item.group.rootRun}
memberRuns={item.group.memberRuns}
events={processEvents}
artifacts={processArtifacts}
selectedRunId={selectedRunId}
onSelectRun={onSelectRun}
onCancelRun={onCancelRun}
/>
)
)}
{isThinking && (
<div className="flex items-center gap-2 text-muted-foreground px-1">