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:
parent
e585ec71b1
commit
9ea5849f22
@ -110,7 +110,8 @@ struct SubAgentEmitter {
|
||||
chat_id: String,
|
||||
metadata: HashMap<String, String>,
|
||||
store: Arc<SessionStore>,
|
||||
sub_session_id: String,
|
||||
/// 子/孙智能体自身的 task_id,用于持久化时作为 scope_key
|
||||
task_id: 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") {
|
||||
self.persist_todo_write_result(&message);
|
||||
}
|
||||
@ -212,7 +213,7 @@ impl SubAgentEmitter {
|
||||
return;
|
||||
};
|
||||
|
||||
let scope_key = &self.sub_session_id;
|
||||
let scope_key = &self.task_id;
|
||||
|
||||
let now = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
@ -366,7 +367,7 @@ impl DefaultSubAgentRuntime {
|
||||
chat_id: session.parent_chat_id.clone(),
|
||||
metadata,
|
||||
store: self.store.clone(),
|
||||
sub_session_id: session.session_id.clone(),
|
||||
task_id: session.id.clone(),
|
||||
stream_message_id: std::sync::Mutex::new(None),
|
||||
},
|
||||
self.conversation_repository.clone(),
|
||||
|
||||
@ -480,12 +480,12 @@ export function MessageBubble({ message, onNavigateToSubAgent, showThinking = tr
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{isTaskTool && message.subagentTaskId && !taskResult && (
|
||||
{isTaskTool && message.navigateToTaskId && !taskResult && (
|
||||
<div className="px-3 pb-1">
|
||||
<button
|
||||
onClick={(e) => {
|
||||
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"
|
||||
>
|
||||
@ -494,7 +494,7 @@ export function MessageBubble({ message, onNavigateToSubAgent, showThinking = tr
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{isTaskTool && !taskResult && !message.subagentTaskId && (
|
||||
{isTaskTool && !taskResult && !message.navigateToTaskId && (
|
||||
<div className="px-3 pb-2 text-xs text-[var(--text-muted)]">
|
||||
子智能体正在执行...
|
||||
</div>
|
||||
@ -571,11 +571,11 @@ export function MessageBubble({ message, onNavigateToSubAgent, showThinking = tr
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
{isTaskTool && message.subagentTaskId && (
|
||||
{isTaskTool && message.navigateToTaskId && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
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"
|
||||
>
|
||||
|
||||
@ -206,15 +206,6 @@ export function useChat(): UseChatReturn {
|
||||
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)
|
||||
const serverMessageToChatMessage = (message: WsOutbound): ChatMessage | null => {
|
||||
switch (message.type) {
|
||||
@ -395,12 +386,8 @@ export function useChat(): UseChatReturn {
|
||||
return
|
||||
}
|
||||
|
||||
// Backfill grandchild task_id on task tool_call in sub-agent view.
|
||||
// When the sub-agent spawns a grandchild via the task tool, the
|
||||
// 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.
|
||||
// When the sub-agent spawns a grandchild, set navigateToTaskId
|
||||
// on the task tool_call so "查看实时进度" navigates correctly.
|
||||
if (message.type === 'task_started') {
|
||||
const msg = message as TaskStarted
|
||||
if (msg.parent_task_id === currentSubAgentView.taskId) {
|
||||
@ -410,8 +397,8 @@ export function useChat(): UseChatReturn {
|
||||
const updatedMessages = [...top.messages]
|
||||
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
||||
const m = updatedMessages[i]
|
||||
if (m.type === 'tool_call' && m.toolName === 'task' && m.subagentTaskId === currentSubAgentView.taskId) {
|
||||
updatedMessages[i] = { ...m, subagentTaskId: msg.task_id }
|
||||
if (m.type === 'tool_call' && m.toolName === 'task' && !m.navigateToTaskId) {
|
||||
updatedMessages[i] = { ...m, navigateToTaskId: msg.task_id }
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -443,23 +430,8 @@ export function useChat(): UseChatReturn {
|
||||
}
|
||||
|
||||
// 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)
|
||||
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
|
||||
}
|
||||
|
||||
@ -478,12 +450,12 @@ export function useChat(): UseChatReturn {
|
||||
// 孙智能体的 TaskStarted 不应 backfill 到主视图
|
||||
if (msg.parent_task_id) break
|
||||
|
||||
// 立即更新对应的 task tool_call,让用户可以点击查看实时进度
|
||||
// 设置 navigateToTaskId,让用户可以点击查看实时进度
|
||||
setMessages((prev) => {
|
||||
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]
|
||||
updated[i] = { ...updated[i], subagentTaskId: msg.task_id }
|
||||
updated[i] = { ...updated[i], navigateToTaskId: msg.task_id }
|
||||
return updated
|
||||
}
|
||||
}
|
||||
|
||||
@ -453,7 +453,10 @@ export interface ChatMessage {
|
||||
status?: 'calling' | 'result' | 'pending'
|
||||
resultContent?: string
|
||||
callContent?: string
|
||||
/** 路由字段:标识消息属于哪个子智能体会话(与后端 subagent_task_id 一致) */
|
||||
subagentTaskId?: string
|
||||
/** 导航字段:仅 task 工具卡片使用,由 task_started 事件设置,指向新创建的子/孙智能体 task_id */
|
||||
navigateToTaskId?: string
|
||||
durationMs?: number
|
||||
reasoningContent?: string
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user