feat(ui): 添加子 Agent 进度显示功能
当 build agent 调用 guide/explore 等子 agent 时, 用户可以在 web 页面实时看到子 agent 的执行进度 实现方案: - Core: 使用 EventEmitter 模式发射子 agent 事件 - Server: 订阅事件并转发到 WebSocket - UI: 处理事件并渲染 SubagentProgress 组件 新增文件: - packages/core/src/agent/events.ts - packages/ui/src/components/SubagentProgress.tsx 修改文件: - core: executor.ts, manager.ts, types.ts, task.ts - server: adapter.ts, types.ts - ui: useChat.ts, types.ts - web: Chat.tsx
This commit is contained in:
@@ -168,6 +168,25 @@ interface AgentRegistryInterface {
|
||||
isInitialized(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 子 Agent 事件类型
|
||||
*/
|
||||
interface SubagentEvent {
|
||||
type: 'subagent:start' | 'subagent:end' | 'subagent:stream' | 'subagent:tool_start' | 'subagent:tool_end';
|
||||
sessionId: string;
|
||||
agentId: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Agent Event Emitter 接口
|
||||
*/
|
||||
interface AgentEventEmitterInterface {
|
||||
on(sessionId: string, listener: (event: SubagentEvent) => void): () => void;
|
||||
off(sessionId: string, listener: (event: SubagentEvent) => void): void;
|
||||
clear(sessionId: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Core 模块接口
|
||||
*/
|
||||
@@ -180,6 +199,7 @@ interface CoreModule {
|
||||
getPermissionManager: (projectRoot?: string) => PermissionManager;
|
||||
getProviderRegistry: () => ProviderRegistryInterface;
|
||||
agentRegistry: AgentRegistryInterface;
|
||||
agentEventEmitter?: AgentEventEmitterInterface;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -446,6 +466,22 @@ export async function processMessage(
|
||||
setSessionAutoApprove(sessionId, null);
|
||||
}
|
||||
|
||||
// 订阅子 Agent 事件(如果可用)
|
||||
let unsubscribeSubagentEvents: (() => void) | null = null;
|
||||
if (coreModule?.agentEventEmitter) {
|
||||
unsubscribeSubagentEvents = coreModule.agentEventEmitter.on(sessionId, (event: SubagentEvent) => {
|
||||
// 检查是否已取消
|
||||
if (abortController.signal.aborted) return;
|
||||
|
||||
// 转发子 Agent 事件到前端
|
||||
broadcastToSession(sessionId, {
|
||||
type: event.type,
|
||||
sessionId,
|
||||
payload: event,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用 Agent 的 chat 方法,使用流式回调和 AbortSignal
|
||||
const result = await agent.chat(content, {
|
||||
@@ -554,6 +590,10 @@ export async function processMessage(
|
||||
|
||||
emitLogEvent(sessionId, 'error', errorMessage);
|
||||
} finally {
|
||||
// 取消子 Agent 事件订阅
|
||||
if (unsubscribeSubagentEvents) {
|
||||
unsubscribeSubagentEvents();
|
||||
}
|
||||
// 清理 AbortController
|
||||
abortControllerCache.delete(sessionId);
|
||||
sessionManager.updateStatus(sessionId, 'idle' as SessionStatus);
|
||||
|
||||
@@ -123,7 +123,13 @@ export interface ServerMessage {
|
||||
| 'error'
|
||||
| 'session_updated'
|
||||
| 'permission_request'
|
||||
| 'mode_switched'; // 模式切换完成
|
||||
| 'mode_switched' // 模式切换完成
|
||||
// 子 Agent 事件
|
||||
| 'subagent:start' // 子 Agent 开始执行
|
||||
| 'subagent:end' // 子 Agent 执行结束
|
||||
| 'subagent:stream' // 子 Agent 流式输出
|
||||
| 'subagent:tool_start' // 子 Agent 工具调用开始
|
||||
| 'subagent:tool_end'; // 子 Agent 工具调用结束
|
||||
sessionId: string;
|
||||
payload?: unknown;
|
||||
}
|
||||
@@ -144,6 +150,67 @@ export interface ToolEndPayload {
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
// ============ 子 Agent 事件 Payload ============
|
||||
|
||||
/** 子 Agent 开始事件 Payload */
|
||||
export interface SubagentStartPayload {
|
||||
type: 'subagent:start';
|
||||
sessionId: string;
|
||||
agentId: string;
|
||||
agentName: string;
|
||||
description: string;
|
||||
parentToolCallId?: string;
|
||||
}
|
||||
|
||||
/** 子 Agent 结束事件 Payload */
|
||||
export interface SubagentEndPayload {
|
||||
type: 'subagent:end';
|
||||
sessionId: string;
|
||||
agentId: string;
|
||||
agentName: string;
|
||||
success: boolean;
|
||||
duration: number;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/** 子 Agent 流式输出事件 Payload */
|
||||
export interface SubagentStreamPayload {
|
||||
type: 'subagent:stream';
|
||||
sessionId: string;
|
||||
agentId: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
/** 子 Agent 工具调用开始事件 Payload */
|
||||
export interface SubagentToolStartPayload {
|
||||
type: 'subagent:tool_start';
|
||||
sessionId: string;
|
||||
agentId: string;
|
||||
toolCallId: string;
|
||||
toolName: string;
|
||||
args: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/** 子 Agent 工具调用结束事件 Payload */
|
||||
export interface SubagentToolEndPayload {
|
||||
type: 'subagent:tool_end';
|
||||
sessionId: string;
|
||||
agentId: string;
|
||||
toolCallId: string;
|
||||
status: 'completed' | 'error';
|
||||
result?: unknown;
|
||||
error?: string;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
/** 子 Agent 事件 Payload 联合类型 */
|
||||
export type SubagentEventPayload =
|
||||
| SubagentStartPayload
|
||||
| SubagentEndPayload
|
||||
| SubagentStreamPayload
|
||||
| SubagentToolStartPayload
|
||||
| SubagentToolEndPayload;
|
||||
|
||||
// ============ Permission 相关 ============
|
||||
|
||||
export type PermissionType = 'bash' | 'file' | 'git' | 'web';
|
||||
|
||||
Reference in New Issue
Block a user