refactor(task): 子智能体相关字段重命名及导航逻辑调整

- 将 runtime 中的 sub_session_id 字段重命名为 task_id,作为持久化的 scope_key
- 调整持久化 todo_write 结果使用 task_id 代替 session_id 作为 scope_key
- 重命名前端消息中的 subagentTaskId 为 navigateToTaskId,增强导航表达
- 修改 MessageBubble 组件中子智能体任务导航相关的字段名和条件判断
- 优化 useChat 中 task tool_call 消息的 navigateToTaskId 设置逻辑,确保正确导航孙智能体任务
- 移除无用的 getTopicId 辅助方法,简化消息处理逻辑
- 在协议类型定义中新增 navigateToTaskId 字段,明确导航用途与关系
This commit is contained in:
oudecheng 2026-06-18 17:09:42 +08:00
parent e585ec71b1
commit 9ea5849f22
4 changed files with 20 additions and 44 deletions

View File

@ -110,7 +110,8 @@ struct SubAgentEmitter {
chat_id: String, chat_id: String,
metadata: HashMap<String, String>, metadata: HashMap<String, String>,
store: Arc<SessionStore>, store: Arc<SessionStore>,
sub_session_id: String, /// 子/孙智能体自身的 task_id用于持久化时作为 scope_key
task_id: String,
stream_message_id: std::sync::Mutex<Option<String>>, stream_message_id: std::sync::Mutex<Option<String>>,
} }
@ -159,7 +160,7 @@ impl EmittedMessageHandler for SubAgentEmitter {
} }
} }
// 拦截 todo_write 结果:持久化到 SQLite子代理用 session_id 作为 scope_key // 拦截 todo_write 结果:持久化到 SQLite子代理用 task_id 作为 scope_key与 list_todos 保持一致
if message.tool_name.as_deref() == Some("todo_write") { if message.tool_name.as_deref() == Some("todo_write") {
self.persist_todo_write_result(&message); self.persist_todo_write_result(&message);
} }
@ -212,7 +213,7 @@ impl SubAgentEmitter {
return; return;
}; };
let scope_key = &self.sub_session_id; let scope_key = &self.task_id;
let now = std::time::SystemTime::now() let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH) .duration_since(std::time::UNIX_EPOCH)
@ -366,7 +367,7 @@ impl DefaultSubAgentRuntime {
chat_id: session.parent_chat_id.clone(), chat_id: session.parent_chat_id.clone(),
metadata, metadata,
store: self.store.clone(), store: self.store.clone(),
sub_session_id: session.session_id.clone(), task_id: session.id.clone(),
stream_message_id: std::sync::Mutex::new(None), stream_message_id: std::sync::Mutex::new(None),
}, },
self.conversation_repository.clone(), self.conversation_repository.clone(),

View File

@ -480,12 +480,12 @@ export function MessageBubble({ message, onNavigateToSubAgent, showThinking = tr
</button> </button>
</div> </div>
)} )}
{isTaskTool && message.subagentTaskId && !taskResult && ( {isTaskTool && message.navigateToTaskId && !taskResult && (
<div className="px-3 pb-1"> <div className="px-3 pb-1">
<button <button
onClick={(e) => { onClick={(e) => {
e.stopPropagation() e.stopPropagation()
onNavigateToSubAgent?.(message.subagentTaskId!, taskDescription || '子智能体任务', subagentType) onNavigateToSubAgent?.(message.navigateToTaskId!, taskDescription || '子智能体任务', subagentType)
}} }}
className="text-xs text-[var(--accent-cyan)] hover:text-[var(--accent-cyan)]/80 hover:underline transition-colors flex items-center gap-1" className="text-xs text-[var(--accent-cyan)] hover:text-[var(--accent-cyan)]/80 hover:underline transition-colors flex items-center gap-1"
> >
@ -494,7 +494,7 @@ export function MessageBubble({ message, onNavigateToSubAgent, showThinking = tr
</button> </button>
</div> </div>
)} )}
{isTaskTool && !taskResult && !message.subagentTaskId && ( {isTaskTool && !taskResult && !message.navigateToTaskId && (
<div className="px-3 pb-2 text-xs text-[var(--text-muted)]"> <div className="px-3 pb-2 text-xs text-[var(--text-muted)]">
... ...
</div> </div>
@ -571,11 +571,11 @@ export function MessageBubble({ message, onNavigateToSubAgent, showThinking = tr
</pre> </pre>
</div> </div>
)} )}
{isTaskTool && message.subagentTaskId && ( {isTaskTool && message.navigateToTaskId && (
<button <button
onClick={(e) => { onClick={(e) => {
e.stopPropagation() e.stopPropagation()
onNavigateToSubAgent?.(message.subagentTaskId!, taskDescription || '子智能体任务', subagentType) onNavigateToSubAgent?.(message.navigateToTaskId!, taskDescription || '子智能体任务', subagentType)
}} }}
className="text-xs text-[var(--accent-cyan)] hover:text-[var(--accent-cyan)]/80 hover:underline transition-colors flex items-center gap-1" className="text-xs text-[var(--accent-cyan)] hover:text-[var(--accent-cyan)]/80 hover:underline transition-colors flex items-center gap-1"
> >

View File

@ -206,15 +206,6 @@ export function useChat(): UseChatReturn {
return undefined return undefined
} }
// Extract topic_id from a message if present
const getTopicId = (message: WsOutbound): string | undefined => {
if (message.type === 'tool_call' || message.type === 'tool_result'
|| message.type === 'tool_pending' || message.type === 'assistant_response') {
return (message as ToolCall | ToolResult | ToolPending | AssistantResponse).topic_id
}
return undefined
}
// Convert a server message to ChatMessage (extracted from handleServerMessage logic) // Convert a server message to ChatMessage (extracted from handleServerMessage logic)
const serverMessageToChatMessage = (message: WsOutbound): ChatMessage | null => { const serverMessageToChatMessage = (message: WsOutbound): ChatMessage | null => {
switch (message.type) { switch (message.type) {
@ -395,12 +386,8 @@ export function useChat(): UseChatReturn {
return return
} }
// Backfill grandchild task_id on task tool_call in sub-agent view. // When the sub-agent spawns a grandchild, set navigateToTaskId
// When the sub-agent spawns a grandchild via the task tool, the // on the task tool_call so "查看实时进度" navigates correctly.
// protocol's subagent_task_id field carries the parent task_id for
// routing, not the new child task_id. We must update it to the
// child's task_id from the task_started event so that "查看实时进度"
// navigates to the correct (grandchild) session.
if (message.type === 'task_started') { if (message.type === 'task_started') {
const msg = message as TaskStarted const msg = message as TaskStarted
if (msg.parent_task_id === currentSubAgentView.taskId) { if (msg.parent_task_id === currentSubAgentView.taskId) {
@ -410,8 +397,8 @@ export function useChat(): UseChatReturn {
const updatedMessages = [...top.messages] const updatedMessages = [...top.messages]
for (let i = updatedMessages.length - 1; i >= 0; i--) { for (let i = updatedMessages.length - 1; i >= 0; i--) {
const m = updatedMessages[i] const m = updatedMessages[i]
if (m.type === 'tool_call' && m.toolName === 'task' && m.subagentTaskId === currentSubAgentView.taskId) { if (m.type === 'tool_call' && m.toolName === 'task' && !m.navigateToTaskId) {
updatedMessages[i] = { ...m, subagentTaskId: msg.task_id } updatedMessages[i] = { ...m, navigateToTaskId: msg.task_id }
break break
} }
} }
@ -443,23 +430,8 @@ export function useChat(): UseChatReturn {
} }
// In main view, skip sub-agent messages (they belong to sub-agent view). // In main view, skip sub-agent messages (they belong to sub-agent view).
// But use the task_id to associate with the running task tool card.
const msgSubagentTaskId = getSubagentTaskId(message) const msgSubagentTaskId = getSubagentTaskId(message)
if (msgSubagentTaskId) { if (msgSubagentTaskId) {
// 只 backfill 当前话题的 task tool_call避免跨话题串扰
const msgTopicId = getTopicId(message)
if (msgTopicId && msgTopicId !== selectedTopicRef.current) return
setMessages((prev) => {
for (let i = prev.length - 1; i >= 0; i--) {
if (prev[i].type === 'tool_call' && prev[i].toolName === 'task' && !prev[i].subagentTaskId) {
const updated = [...prev]
updated[i] = { ...updated[i], subagentTaskId: msgSubagentTaskId }
return updated
}
}
return prev
})
return return
} }
@ -478,12 +450,12 @@ export function useChat(): UseChatReturn {
// 孙智能体的 TaskStarted 不应 backfill 到主视图 // 孙智能体的 TaskStarted 不应 backfill 到主视图
if (msg.parent_task_id) break if (msg.parent_task_id) break
// 立即更新对应的 task tool_call,让用户可以点击查看实时进度 // 设置 navigateToTaskId,让用户可以点击查看实时进度
setMessages((prev) => { setMessages((prev) => {
for (let i = prev.length - 1; i >= 0; i--) { for (let i = prev.length - 1; i >= 0; i--) {
if (prev[i].type === 'tool_call' && prev[i].toolName === 'task' && !prev[i].subagentTaskId) { if (prev[i].type === 'tool_call' && prev[i].toolName === 'task' && !prev[i].navigateToTaskId) {
const updated = [...prev] const updated = [...prev]
updated[i] = { ...updated[i], subagentTaskId: msg.task_id } updated[i] = { ...updated[i], navigateToTaskId: msg.task_id }
return updated return updated
} }
} }

View File

@ -453,7 +453,10 @@ export interface ChatMessage {
status?: 'calling' | 'result' | 'pending' status?: 'calling' | 'result' | 'pending'
resultContent?: string resultContent?: string
callContent?: string callContent?: string
/** 路由字段:标识消息属于哪个子智能体会话(与后端 subagent_task_id 一致) */
subagentTaskId?: string subagentTaskId?: string
/** 导航字段:仅 task 工具卡片使用,由 task_started 事件设置,指向新创建的子/孙智能体 task_id */
navigateToTaskId?: string
durationMs?: number durationMs?: number
reasoningContent?: string reasoningContent?: string
} }