Skip to main content

matrixcode_core/command/handlers/
load.rs

1//! /load 命令实现
2//!
3//! 加载指定会话,包括完整消息历史和项目记忆。
4
5use super::super::backend_context::BackendContext;
6use super::super::command_trait::Command;
7use crate::{AgentEvent, ContentBlock, EventData, EventType, HistoryMessage, MessageContent, Role};
8use std::path::PathBuf;
9
10/// Load 命令
11///
12/// 用法:
13/// - /load <session_id> - 加载指定会话(包括消息历史和记忆)
14pub struct Load;
15
16impl Command for Load {
17    fn name(&self) -> &'static str {
18        "load"
19    }
20
21    fn help(&self) -> Option<&'static str> {
22        Some("加载指定会话(包括消息历史和记忆)。用法: /load <session_id>")
23    }
24
25    fn execute<'a>(
26        &'a self,
27        ctx: &'a mut BackendContext<'_>,
28    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = bool> + Send + 'a>> {
29        Box::pin(async move {
30            let session_id = ctx.message.strip_prefix("/load ").unwrap_or("");
31
32            if let Some(mgr) = ctx.session_mgr {
33                if mgr.resume(session_id).is_ok() {
34                    // Load full message history for display and agent
35                    let full_messages = mgr
36                        .full_messages()
37                        .map(|msgs| msgs.to_vec())
38                        .unwrap_or_default();
39
40                    if !full_messages.is_empty() {
41                        // Set full messages to agent
42                        ctx.agent.set_messages(full_messages.clone());
43
44                        // Load memory for this session's project path
45                        let project_path: Option<PathBuf> = mgr
46                            .current_metadata()
47                            .and_then(|m| m.project_path.clone())
48                            .map(PathBuf::from);
49
50                        if let Some(ref path) = project_path {
51                            // Reload memory storage with session's project path
52                            if let Ok(new_storage) = crate::memory::MemoryStorage::new(Some(path)) {
53                                *ctx.memory_storage = Some(new_storage);
54                                log::info!("Loaded memory for project: {}", path.display());
55                            }
56                        }
57
58                        // Convert messages to history format for TUI display
59                        // Each message can produce multiple history entries (text + thinking)
60                        let history: Vec<HistoryMessage> = full_messages
61                            .iter()
62                            .filter_map(|m| {
63                                let base_role = match m.role {
64                                    Role::User => "user",
65                                    Role::Assistant => "assistant",
66                                    _ => return None, // Skip system/tool messages
67                                };
68
69                                match &m.content {
70                                    MessageContent::Text(text) => {
71                                        Some(vec![HistoryMessage {
72                                            role: base_role.to_string(),
73                                            content: text.clone(),
74                                            is_thinking: false,
75                                        }])
76                                    }
77                                    MessageContent::Blocks(blocks) => {
78                                        // Extract text and thinking from blocks
79                                        // Thinking blocks become separate messages
80                                        let entries: Vec<HistoryMessage> = blocks
81                                            .iter()
82                                            .filter_map(|b| match b {
83                                                ContentBlock::Text { text } => Some(HistoryMessage {
84                                                    role: base_role.to_string(),
85                                                    content: text.clone(),
86                                                    is_thinking: false,
87                                                }),
88                                                ContentBlock::Thinking { thinking, .. } => {
89                                                    Some(HistoryMessage {
90                                                        role: base_role.to_string(),
91                                                        content: thinking.clone(),
92                                                        is_thinking: true,
93                                                    })
94                                                }
95                                                _ => None,
96                                            })
97                                            .collect();
98                                        if entries.is_empty() {
99                                            None
100                                        } else {
101                                            Some(entries)
102                                        }
103                                    }
104                                }
105                            })
106                            .flatten()
107                            .collect();
108
109                        // Send history to TUI for display
110                        if !history.is_empty() {
111                            let _ = ctx
112                                .event_tx
113                                .send(AgentEvent::with_data(
114                                    EventType::HistoryLoaded,
115                                    EventData::HistoryMessages { messages: history },
116                                ))
117                                .await;
118                        }
119
120                        // Build success message
121                        let msg = match project_path {
122                            Some(path) => format!(
123                                "✓ Session '{}' loaded: {} messages (project: {})",
124                                session_id,
125                                full_messages.len(),
126                                path.display()
127                            ),
128                            None => format!(
129                                "✓ Session '{}' loaded: {} messages",
130                                session_id,
131                                full_messages.len()
132                            ),
133                        };
134
135                        let _ = ctx.event_tx.send(AgentEvent::progress(msg, None)).await;
136                    } else {
137                        let _ = ctx
138                            .event_tx
139                            .send(AgentEvent::progress(
140                                format!("⚠️ Session '{}' has no messages", session_id),
141                                None,
142                            ))
143                            .await;
144                    }
145                } else {
146                    let _ = ctx
147                        .event_tx
148                        .send(AgentEvent::progress(
149                            format!("❌ Session '{}' not found", session_id),
150                            None,
151                        ))
152                        .await;
153                }
154            } else {
155                let _ = ctx
156                    .event_tx
157                    .send(AgentEvent::progress(
158                        "❌ Session manager not available",
159                        None,
160                    ))
161                    .await;
162            }
163
164            false // 不转发给 agent
165        })
166    }
167}