feat: MetonaAI Desktop 初始项目
- Electron + React + TypeScript 架构 - 三栏布局: Sidebar | ChatPanel | DetailPanel - 9 个内置工具 (文件系统/网络/记忆/命令) - SQLite 持久化 (better-sqlite3) - MUI 暗色/亮色主题系统 - Agent Loop ReAct 状态机引擎 - DeepSeek / Agnes AI / Ollama Provider 适配器 - MCP 协议集成 - 系统托盘 + 全局快捷键 - Tailwind CSS v4 + Tailwind Merge - 修复: Sidebar 缺失 TextField 导入导致黑屏
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Task Orchestrator — 任务编排器
|
||||
*
|
||||
* 支持父子委派模式:主 Agent 委派子任务给 SubAgent。
|
||||
*
|
||||
* @see docs/生产级通用 AI Agent 智能体桌面应用:完整设计与构建指南.html — 第五章
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
export interface SubAgentResult {
|
||||
taskId: string;
|
||||
result: string;
|
||||
success: boolean;
|
||||
durationMs: number;
|
||||
}
|
||||
|
||||
interface SubAgentHandle {
|
||||
taskId: string;
|
||||
description: string;
|
||||
status: 'pending' | 'running' | 'completed' | 'error';
|
||||
result?: SubAgentResult;
|
||||
abort: () => void;
|
||||
onComplete: (callback: (result: SubAgentResult) => void) => void;
|
||||
onError: (callback: (error: Error) => void) => void;
|
||||
getStatus: () => { taskId: string; status: string; description: string };
|
||||
}
|
||||
|
||||
export class TaskOrchestrator extends EventEmitter {
|
||||
private activeSubAgents = new Map<string, SubAgentHandle>();
|
||||
|
||||
/**
|
||||
* 委派子任务
|
||||
*
|
||||
* 创建一个子任务句柄,通过事件驱动的方式执行。
|
||||
* 实际执行逻辑由上层 Agent Loop 决定。
|
||||
*/
|
||||
async delegate(params: {
|
||||
taskId?: string;
|
||||
description: string;
|
||||
parentSessionId: string;
|
||||
maxIterations?: number;
|
||||
tools?: string[];
|
||||
}): Promise<SubAgentResult> {
|
||||
const taskId = params.taskId ?? `sub_${nanoid(8)}`;
|
||||
const startMs = Date.now();
|
||||
|
||||
this.emit('taskDelegated', { taskId, description: params.description, parentSessionId: params.parentSessionId });
|
||||
|
||||
// 返回一个可被上层消费的 Promise
|
||||
return new Promise<SubAgentResult>((resolve) => {
|
||||
const handle: SubAgentHandle = {
|
||||
taskId,
|
||||
description: params.description,
|
||||
status: 'pending',
|
||||
abort: () => {
|
||||
handle.status = 'error';
|
||||
this.activeSubAgents.delete(taskId);
|
||||
resolve({ taskId, result: 'Aborted', success: false, durationMs: Date.now() - startMs });
|
||||
},
|
||||
onComplete: (callback) => {
|
||||
if (handle.result) callback(handle.result);
|
||||
},
|
||||
onError: (_callback) => {},
|
||||
getStatus: () => ({ taskId, status: handle.status, description: handle.description }),
|
||||
};
|
||||
|
||||
this.activeSubAgents.set(taskId, handle);
|
||||
|
||||
// 立即标记为运行中
|
||||
handle.status = 'running';
|
||||
this.emit('taskStarted', { taskId });
|
||||
|
||||
// 子任务完成时调用
|
||||
const complete = (result: string, success: boolean) => {
|
||||
handle.status = success ? 'completed' : 'error';
|
||||
handle.result = { taskId, result, success, durationMs: Date.now() - startMs };
|
||||
this.activeSubAgents.delete(taskId);
|
||||
this.emit('taskCompleted', handle.result);
|
||||
resolve(handle.result);
|
||||
};
|
||||
|
||||
// 暴露完成方法给调用者
|
||||
(handle as unknown as Record<string, unknown>).complete = complete;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成子任务
|
||||
*/
|
||||
completeTask(taskId: string, result: string, success: boolean): void {
|
||||
const handle = this.activeSubAgents.get(taskId);
|
||||
if (handle) {
|
||||
const complete = (handle as unknown as Record<string, unknown>).complete as ((result: string, success: boolean) => void) | undefined;
|
||||
complete?.(result, success);
|
||||
}
|
||||
}
|
||||
|
||||
getActiveAgentsStatus(): Array<{ taskId: string; status: string; description: string }> {
|
||||
return Array.from(this.activeSubAgents.values()).map((a) => a.getStatus());
|
||||
}
|
||||
|
||||
abortAll(): void {
|
||||
for (const agent of this.activeSubAgents.values()) {
|
||||
agent.abort();
|
||||
}
|
||||
this.activeSubAgents.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user