diff --git a/packages/server/src/agent/adapter.ts b/packages/server/src/agent/adapter.ts index a8e171e..7910d5d 100644 --- a/packages/server/src/agent/adapter.ts +++ b/packages/server/src/agent/adapter.ts @@ -529,6 +529,7 @@ export async function processMessage( text: result.text, hasToolCalls, messageCount: result.messages.length, + agentName: options?.agentMode || 'build', }, }); diff --git a/packages/ui/src/api/types.ts b/packages/ui/src/api/types.ts index a0eeb01..b2280c0 100644 --- a/packages/ui/src/api/types.ts +++ b/packages/ui/src/api/types.ts @@ -95,6 +95,8 @@ export interface Message { model?: string; stepCount?: number; totalTokens?: number; + /** 生成此消息的 Agent 名称 */ + agentName?: string; }; } diff --git a/packages/ui/src/components/ChatMessage.tsx b/packages/ui/src/components/ChatMessage.tsx index 8760025..8e66269 100644 --- a/packages/ui/src/components/ChatMessage.tsx +++ b/packages/ui/src/components/ChatMessage.tsx @@ -19,6 +19,7 @@ import { motion, AnimatePresence } from 'framer-motion'; import { useState, forwardRef } from 'react'; import { cn } from '../utils/cn'; import { fadeInUp, smoothTransition } from '../utils/animations'; +import { getAgentDisplayName } from '../utils/agent'; import { Markdown } from './Markdown'; import { FileMentionText } from './FileMentionTag'; import type { Message, ToolCallInfo, ToolCallStatus, ToolMessagePart } from '../api/types.js'; @@ -137,7 +138,7 @@ export const ChatMessage = forwardRef(
- {isUser ? 'You' : 'AI Assistant'} + {isUser ? 'You' : getAgentDisplayName(message.metadata?.agentName)}
-
AI Assistant
+
{getAgentDisplayName(agentName)}
-
AI Assistant
+
{getAgentDisplayName(agentName)}
{[0, 1, 2].map((i) => ( (null); @@ -138,6 +141,7 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate timestamp: new Date().toISOString(), parts: [] as MessagePart[], content: '', + metadata: { agentName: prev.currentAgent }, }; // 复制 parts 数组以进行修改 @@ -180,6 +184,7 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate timestamp: new Date().toISOString(), parts: [] as MessagePart[], content: '', + metadata: { agentName: prev.currentAgent }, }; // 添加工具调用 part @@ -192,11 +197,19 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate arguments: payload.arguments, }; + // 如果是 task 工具,切换到子 agent + const newAgent = + payload.toolName === 'task' && payload.arguments?.subagent_type + ? (payload.arguments.subagent_type as string) + : prev.currentAgent; + return { ...prev, + currentAgent: newAgent, streamingMessage: { ...streaming, parts: [...streaming.parts, toolPart], + metadata: { ...streaming.metadata, agentName: newAgent }, }, }; }); @@ -222,11 +235,20 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate return part; }); + // 查找完成的工具是否为 task,如果是则恢复主 agent + const completedTool = prev.streamingMessage.parts.find( + (part) => part.type === 'tool' && part.id === payload.id + ); + const isTaskTool = completedTool?.type === 'tool' && completedTool.toolName === 'task'; + const newAgent = isTaskTool ? prev.agentMode : prev.currentAgent; + return { ...prev, + currentAgent: newAgent, streamingMessage: { ...prev.streamingMessage, parts, + metadata: { ...prev.streamingMessage.metadata, agentName: newAgent }, }, }; }); @@ -238,6 +260,8 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate // 使用流式消息或创建新消息 const streaming = prev.streamingMessage; const content = message.payload?.content || streaming?.content || ''; + // 从服务器 payload 获取 agentName,或使用当前 agentMode + const agentName = message.payload?.agentName || prev.agentMode; const newMessage: Message = streaming ? { @@ -245,6 +269,7 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate id: message.payload?.id || streaming.id, timestamp: message.payload?.timestamp || streaming.timestamp, content, + metadata: { ...streaming.metadata, agentName }, } : { id: message.payload?.id || `assistant-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`, @@ -252,6 +277,7 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate timestamp: message.payload?.timestamp || new Date().toISOString(), parts: [{ type: 'text', id: `text-${Date.now()}`, text: content }], content, + metadata: { agentName }, }; return { @@ -259,6 +285,7 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate messages: [...prev.messages, newMessage], streamingMessage: null, isLoading: false, + currentAgent: prev.agentMode, // 恢复为主 agent }; }); break; @@ -448,6 +475,7 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate permissionRequest: null, agentMode: 'build', autoApprove: false, + currentAgent: 'build', }); reconnectAttemptsRef.current = 0; diff --git a/packages/ui/src/utils/agent.ts b/packages/ui/src/utils/agent.ts new file mode 100644 index 0000000..f537ae4 --- /dev/null +++ b/packages/ui/src/utils/agent.ts @@ -0,0 +1,26 @@ +/** + * Agent 工具函数 + */ + +/** + * Agent 显示名称映射 + */ +export const AGENT_DISPLAY_NAMES: Record = { + build: 'Build Agent', + plan: 'Plan Agent', + general: 'General Agent', + explore: 'Explore Agent', + 'code-reviewer': 'Code Reviewer', + vision: 'Vision Agent', + summary: 'Summary Agent', +}; + +/** + * 获取 Agent 显示名称 + * @param agentName Agent 名称(如 build、plan、explore 等) + * @returns 显示名称 + */ +export function getAgentDisplayName(agentName?: string): string { + if (!agentName) return 'AI Assistant'; + return AGENT_DISPLAY_NAMES[agentName] || agentName; +} diff --git a/packages/web/src/pages/Chat.tsx b/packages/web/src/pages/Chat.tsx index 7bd8346..3e1621d 100644 --- a/packages/web/src/pages/Chat.tsx +++ b/packages/web/src/pages/Chat.tsx @@ -62,6 +62,7 @@ export function ChatPage({ autoApprove, setAgentMode, setAutoApprove, + currentAgent, } = useChat({ sessionId, onError: (error) => { @@ -297,7 +298,7 @@ export function ChatPage({ )} - {isLoading && !streamingMessage && } + {isLoading && !streamingMessage && }