From 50d0b92336df0e5712d3616ca659e051bf1e715b Mon Sep 17 00:00:00 2001 From: oudecheng <13802883547@139.com> Date: Fri, 12 Jun 2026 16:49:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=AF=A6=E7=BB=86?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E8=A7=86=E5=9B=BE=E5=92=8C=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E5=9B=BE=E6=A0=87=EF=BC=8C=E6=94=AF=E6=8C=81=E6=94=BE=E5=A4=A7?= =?UTF-8?q?=E6=9F=A5=E7=9C=8B=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/command/handlers/list_todos.rs | 6 +++++ src/gateway/execution.rs | 12 ++++------ src/gateway/ws.rs | 4 +++- web/src/components/Chat/MessageBubble.tsx | 23 +++++++++++++++++- web/src/components/Panel/ToolPanel.tsx | 29 ++++++++++++++++++++--- 5 files changed, 62 insertions(+), 12 deletions(-) diff --git a/src/command/handlers/list_todos.rs b/src/command/handlers/list_todos.rs index 621ec05..535b21d 100644 --- a/src/command/handlers/list_todos.rs +++ b/src/command/handlers/list_todos.rs @@ -54,6 +54,12 @@ impl CommandHandler for ListTodosCommandHandler { .list_todos(scope_key) .map_err(|e| CommandError::new("LIST_TODOS_ERROR", e.to_string()))?; + tracing::info!( + scope_key = %scope_key, + record_count = records.len(), + "list_todos handler: reading from store" + ); + let summaries: Vec = records .into_iter() .map(|r| TodoItemSummary { diff --git a/src/gateway/execution.rs b/src/gateway/execution.rs index c4e2bee..c2ad6c4 100644 --- a/src/gateway/execution.rs +++ b/src/gateway/execution.rs @@ -190,13 +190,11 @@ impl AgentExecutionService { // 只有当是最新回合时才触发历史压缩 let should_schedule_compaction = is_current_turn; - // 拦截 todo_write 结果:持久化 + 前端推送 - if is_current_turn { - session.intercept_todo_write_results( - &request.result.emitted_messages, - request.chat_id, - ); - } + // 拦截 todo_write 结果:持久化 + 前端推送(不受 is_current_turn 限制) + session.intercept_todo_write_results( + &request.result.emitted_messages, + request.chat_id, + ); Ok(FinalizedAgentResult { outbound_messages, diff --git a/src/gateway/ws.rs b/src/gateway/ws.rs index 3c1cf83..fa39c2a 100644 --- a/src/gateway/ws.rs +++ b/src/gateway/ws.rs @@ -525,7 +525,9 @@ async fn handle_inbound( // 处理 Todo 列表 if let Some(todos_json) = response.metadata.get("todos") { if let Ok(todos) = serde_json::from_str::>(todos_json) { - let _ = sender.send(WsOutbound::TodoList { todos, scope_key: String::new() }).await; + let scope_key = response.metadata.get("todos_scope_key").cloned().unwrap_or_default(); + tracing::info!(todo_count = todos.len(), %scope_key, "list_todos command response"); + let _ = sender.send(WsOutbound::TodoList { todos, scope_key }).await; } } diff --git a/web/src/components/Chat/MessageBubble.tsx b/web/src/components/Chat/MessageBubble.tsx index b28b918..643f75b 100644 --- a/web/src/components/Chat/MessageBubble.tsx +++ b/web/src/components/Chat/MessageBubble.tsx @@ -1,8 +1,9 @@ import { useState, useEffect } from 'react' -import { User, Bot, Wrench, CheckCircle, AlertCircle, Terminal, File, Image, FileText, Music, Video, Download, ChevronDown, ChevronRight, Copy, Check, Loader2, XCircle, Clock, Loader, X, Brain } from 'lucide-react' +import { User, Bot, Wrench, CheckCircle, AlertCircle, Terminal, File, Image, FileText, Music, Video, Download, ChevronDown, ChevronRight, Copy, Check, Loader2, XCircle, Clock, Loader, X, Brain, Maximize2 } from 'lucide-react' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import type { ChatMessage, Attachment, TaskToolResult } from '../../types/protocol' +import { ToolDetailModal } from './ToolDetailModal' // 状态图标组件 function StatusIcon({ status, size = 14 }: { status: 'calling' | 'result' | 'pending' | 'success' | 'failed' | 'timeout', size?: number }) { @@ -294,6 +295,7 @@ export function MessageBubble({ message, onNavigateToSubAgent, showThinking = tr const isTool = message.role === 'tool' const isMergedTool = message.type === 'merged_tool' const [toolExpanded, setToolExpanded] = useState(false) + const [showDetailModal, setShowDetailModal] = useState(false) const [lightboxImage, setLightboxImage] = useState<{ base64: string; mimeType: string; fileName?: string } | null>(null) const lightboxElement = lightboxImage ? ( @@ -429,6 +431,13 @@ export function MessageBubble({ message, onNavigateToSubAgent, showThinking = tr )} {hasResult && } + {toolExpanded ? ( @@ -585,6 +594,18 @@ export function MessageBubble({ message, onNavigateToSubAgent, showThinking = tr {lightboxElement} + {showDetailModal && ( + setShowDetailModal(false)} + /> + )} ) } diff --git a/web/src/components/Panel/ToolPanel.tsx b/web/src/components/Panel/ToolPanel.tsx index 347bddc..ab89ead 100644 --- a/web/src/components/Panel/ToolPanel.tsx +++ b/web/src/components/Panel/ToolPanel.tsx @@ -1,6 +1,7 @@ -import { ChevronDown, ChevronRight, Play, Check, AlertTriangle, Terminal } from 'lucide-react' +import { ChevronDown, ChevronRight, Play, Check, AlertTriangle, Terminal, Maximize2 } from 'lucide-react' import { useState, useMemo } from 'react' import type { ChatMessage } from '../../types/protocol' +import { ToolDetailModal } from '../Chat/ToolDetailModal' interface ToolPanelProps { messages: ChatMessage[] @@ -77,6 +78,7 @@ function mergeToolMessages(messages: ChatMessage[]): ToolCallItem[] { export function ToolPanel({ messages }: ToolPanelProps) { const [expandedTools, setExpandedTools] = useState>(new Set()) + const [detailModalTool, setDetailModalTool] = useState(null) const toolCalls = useMemo(() => mergeToolMessages(messages), [messages]) @@ -143,6 +145,7 @@ export function ToolPanel({ messages }: ToolPanelProps) { } return ( + <>
@@ -187,13 +190,20 @@ export function ToolPanel({ messages }: ToolPanelProps) { )}
- +
+ {isExpanded ? ( ) : ( )} - +
{/* 结果预览区 — 始终可见 */} @@ -234,6 +244,19 @@ export function ToolPanel({ messages }: ToolPanelProps) {
+ {detailModalTool && ( + setDetailModalTool(null)} + /> + )} + ) }