1use crate::{MCPToolResult, get_memory_system, string_to_anyhow};
4use anyhow::Result;
5use post_cortex_core::core::timeout_utils::with_storage_timeout;
6use post_cortex_core::session::active_session::ActiveSession;
7use post_cortex_memory::ConversationMemorySystem;
8use post_cortex_storage::rocksdb_storage::SessionCheckpoint;
9use std::sync::Arc;
10use tracing::{debug, error, instrument};
11use uuid::Uuid;
12
13pub async fn create_session_checkpoint_with_system(
15 session_id: Uuid,
16 system: &ConversationMemorySystem,
17) -> Result<MCPToolResult> {
18 let session_arc = system
19 .get_session(session_id)
20 .await
21 .map_err(|e| anyhow::anyhow!("Failed to load session: {}", e))?;
22 let session = session_arc.load();
23
24 let checkpoint = create_comprehensive_checkpoint(&session).await?;
25
26 system
27 .storage_actor
28 .save_checkpoint(&checkpoint)
29 .await
30 .map_err(string_to_anyhow)?;
31
32 Ok(MCPToolResult::success(
33 "Checkpoint created successfully".to_string(),
34 Some(serde_json::json!({ "checkpoint_id": checkpoint.id.to_string() })),
35 ))
36}
37
38pub async fn load_session_checkpoint_with_system(
40 checkpoint_id: String,
41 session_id: Uuid,
42 system: &ConversationMemorySystem,
43) -> Result<MCPToolResult> {
44 eprintln!("Loading checkpoint - step 1: Parsing checkpoint ID");
45 let checkpoint_id = Uuid::parse_str(&checkpoint_id)?;
46
47 eprintln!("Loading checkpoint - step 2: Loading checkpoint from storage");
48 let checkpoint = system
49 .storage_actor
50 .load_checkpoint(checkpoint_id)
51 .await
52 .map_err(string_to_anyhow)?;
53
54 eprintln!("Loading checkpoint - step 3: Checkpoint loaded successfully");
55
56 eprintln!("Loading checkpoint - step 4: Creating session from checkpoint");
57 let mut session = ActiveSession::new(session_id, None, None);
58
59 eprintln!("Loading checkpoint - step 5: Restoring current state");
60 session.current_state = Arc::new(checkpoint.structured_context);
61
62 eprintln!("Loading checkpoint - step 6: Restoring incremental updates");
63 session.set_incremental_updates(checkpoint.recent_updates);
64
65 eprintln!("Loading checkpoint - step 10: Restoring code references");
66 session.code_references = Arc::new(checkpoint.code_references);
67
68 eprintln!("Loading checkpoint - step 11: Restoring change history");
69 session.set_change_history(checkpoint.change_history);
70
71 eprintln!("Loading checkpoint - step 12: Entity graph restored");
72
73 eprintln!("Loading checkpoint - step 13: Adding session to session manager");
74 system.session_manager.install(session_id, session);
75
76 eprintln!("Loading checkpoint - step 14: Updated session manager");
77
78 Ok(MCPToolResult::success(
79 "Session loaded from checkpoint successfully".to_string(),
80 None,
81 ))
82}
83
84pub async fn create_session_checkpoint(session_id: Uuid) -> Result<MCPToolResult> {
86 let result = with_storage_timeout(async {
87 let system = get_memory_system().await?;
88 let session_arc = system
89 .get_session(session_id)
90 .await
91 .map_err(|e| anyhow::anyhow!("Failed to load session: {}", e))?;
92 let session = session_arc.load();
93
94 let checkpoint = create_comprehensive_checkpoint(&session).await?;
95
96 system
97 .storage_actor
98 .save_checkpoint(&checkpoint)
99 .await
100 .map_err(string_to_anyhow)?;
101
102 Ok(MCPToolResult::success(
103 "Checkpoint created successfully".to_string(),
104 Some(serde_json::json!({ "checkpoint_id": checkpoint.id.to_string() })),
105 ))
106 })
107 .await;
108
109 match result {
110 Ok(success_result) => success_result,
111 Err(timeout_error) => {
112 error!(
113 "TIMEOUT: create_session_checkpoint - session: {}, error: {}",
114 session_id, timeout_error
115 );
116 Ok(MCPToolResult::error(format!(
117 "Checkpoint creation timed out: {}",
118 timeout_error
119 )))
120 }
121 }
122}
123
124pub async fn load_session_checkpoint(
126 checkpoint_id: String,
127 session_id: Uuid,
128) -> Result<MCPToolResult> {
129 let result = with_storage_timeout(async {
130 let system = get_memory_system().await?;
131 let checkpoint_id = Uuid::parse_str(&checkpoint_id)?;
132
133 let checkpoint = system
134 .storage_actor
135 .load_checkpoint(checkpoint_id)
136 .await
137 .map_err(string_to_anyhow)?;
138
139 let mut session = ActiveSession::new(session_id, None, None);
140 session.current_state = Arc::new(checkpoint.structured_context);
141 session.set_incremental_updates(checkpoint.recent_updates);
142 session.code_references = Arc::new(checkpoint.code_references);
143 session.set_change_history(checkpoint.change_history);
144
145 system.session_manager.install(session_id, session);
146
147 Ok(MCPToolResult::success(
148 "Session loaded from checkpoint successfully".to_string(),
149 None,
150 ))
151 })
152 .await;
153
154 match result {
155 Ok(success_result) => success_result,
156 Err(timeout_error) => {
157 error!(
158 "TIMEOUT: load_session_checkpoint - session: {}, error: {}",
159 session_id, timeout_error
160 );
161 Ok(MCPToolResult::error(format!(
162 "Checkpoint loading timed out: {}",
163 timeout_error
164 )))
165 }
166 }
167}
168
169pub async fn mark_important(session_id: Uuid, update_id: String) -> Result<MCPToolResult> {
171 let update_id = Uuid::parse_str(&update_id)?;
172 let system = get_memory_system().await?;
173 let session_arc = system
174 .get_session(session_id)
175 .await
176 .map_err(|e| anyhow::anyhow!("Failed to load session: {}", e))?;
177
178 let mut found = false;
179 session_arc.rcu(|current| {
180 let mut updated = (**current).clone();
181 let updates = Arc::make_mut(&mut updated.incremental_updates);
182 for update in updates.iter_mut() {
183 if update.id == update_id {
184 Arc::make_mut(update).user_marked_important = true;
185 found = true;
186 break;
187 }
188 }
189 Arc::new(updated)
190 });
191
192 if found {
193 Ok(MCPToolResult::success(
194 "Update marked as important".to_string(),
195 None,
196 ))
197 } else {
198 Ok(MCPToolResult::error("Update not found".to_string()))
199 }
200}
201
202pub async fn list_sessions_with_storage(
204 storage: &post_cortex_storage::rocksdb_storage::RealRocksDBStorage,
205) -> Result<MCPToolResult> {
206 match storage.list_session_summaries().await {
208 Ok(summaries) => {
209 let sessions_info: Vec<_> = summaries
210 .iter()
211 .map(|summary| summary_json(summary, None))
212 .collect();
213 Ok(MCPToolResult::success(
214 format!("Found {} sessions", sessions_info.len()),
215 Some(serde_json::json!({
216 "sessions": sessions_info
217 })),
218 ))
219 }
220 Err(e) => Ok(MCPToolResult::error(format!(
221 "Failed to load sessions: {e}"
222 ))),
223 }
224}
225
226fn summary_json(
228 summary: &post_cortex_storage::traits::SessionSummary,
229 entity_count: Option<usize>,
230) -> serde_json::Value {
231 serde_json::json!({
232 "id": summary.id.to_string(),
233 "name": summary.name,
234 "description": summary.description,
235 "created_at": summary.created_at.to_rfc3339(),
236 "last_updated": summary.last_updated.to_rfc3339(),
237 "update_count": summary.update_count,
238 "entity_count": entity_count
239 })
240}
241
242pub async fn list_sessions() -> Result<MCPToolResult> {
244 debug!("MCP-TOOLS: list_sessions() called");
245 let result = with_storage_timeout(async {
246 debug!("MCP-TOOLS: Getting memory system for list_sessions");
247 let system = get_memory_system().await?;
248 debug!("MCP-TOOLS: Got memory system, listing sessions");
249 let summaries = system
253 .list_session_summaries()
254 .await
255 .map_err(string_to_anyhow)?;
256 let sessions_info: Vec<_> = summaries
257 .iter()
258 .map(|summary| {
259 let entity_count = system
260 .session_manager
261 .sessions
262 .get(&summary.id)
263 .map(|s| s.load().entity_graph.entities.len());
264 summary_json(summary, entity_count)
265 })
266 .collect();
267
268 Ok(MCPToolResult::success(
269 format!("Found {} sessions", sessions_info.len()),
270 Some(serde_json::json!({
271 "sessions": sessions_info
272 })),
273 ))
274 })
275 .await;
276
277 match result {
278 Ok(success_result) => success_result,
279 Err(timeout_error) => {
280 error!("TIMEOUT: list_sessions - error: {timeout_error}");
281 Ok(MCPToolResult::error(format!(
282 "Session listing timed out: {timeout_error}"
283 )))
284 }
285 }
286}
287
288pub async fn load_session_with_system(
290 session_id: Uuid,
291 system: &ConversationMemorySystem,
292) -> Result<MCPToolResult> {
293 match system.get_session(session_id).await {
294 Ok(session_arc) => {
295 let session = session_arc.load();
296 Ok(MCPToolResult::success(
297 "Session loaded successfully".to_string(),
298 Some(serde_json::json!({
299 "session": {
300 "id": session.id().to_string(),
301 "created_at": session.created_at().to_rfc3339(),
302 "last_updated": session.last_updated.to_rfc3339(),
303 "update_count": session.incremental_updates.len(),
304 "entity_count": session.entity_graph.entities.len(),
305 "hot_context_size": session.hot_context.len(),
306 "warm_context_size": session.warm_context.len(),
307 "cold_context_size": session.cold_context.len(),
308 "code_references": session.code_references.keys().collect::<Vec<_>>(),
309 "change_history_count": session.change_history.len()
310 }
311 })),
312 ))
313 }
314 Err(e) => Ok(MCPToolResult::error(
315 format!("Failed to load session: {e}",),
316 )),
317 }
318}
319
320pub async fn load_session(session_id: Uuid) -> Result<MCPToolResult> {
322 let result = with_storage_timeout(async {
323 let system = get_memory_system().await?;
324 load_session_with_system(session_id, &system).await
325 })
326 .await;
327
328 match result {
329 Ok(success_result) => success_result,
330 Err(timeout_error) => {
331 error!(
332 "TIMEOUT: load_session - session: {}, error: {}",
333 session_id, timeout_error
334 );
335 Ok(MCPToolResult::error(format!(
336 "Session loading timed out: {}",
337 timeout_error
338 )))
339 }
340 }
341}
342
343pub async fn search_sessions(query: String) -> Result<MCPToolResult> {
345 let result = with_storage_timeout(async {
346 let system = get_memory_system().await?;
347 let sessions: Vec<_> = system
348 .find_session_summaries(&query)
349 .await
350 .map_err(string_to_anyhow)?
351 .into_iter()
352 .map(|s| {
353 serde_json::json!({
354 "id": s.id.to_string(),
355 "name": s.name,
356 "description": s.description
357 })
358 })
359 .collect();
360
361 Ok(MCPToolResult::success(
362 format!("Found {} sessions matching '{}'", sessions.len(), query),
363 Some(serde_json::json!({
364 "sessions": sessions
365 })),
366 ))
367 })
368 .await;
369
370 match result {
371 Ok(success_result) => success_result,
372 Err(timeout_error) => {
373 error!(
374 "TIMEOUT: search_sessions - query: {}, error: {}",
375 query, timeout_error
376 );
377 Ok(MCPToolResult::error(format!(
378 "Session search timed out: {}",
379 timeout_error
380 )))
381 }
382 }
383}
384
385#[instrument(skip(session_id), fields(session_id = %session_id))]
387pub async fn update_session_metadata(
388 session_id: Uuid,
389 name: Option<String>,
390 description: Option<String>,
391) -> Result<MCPToolResult> {
392 let result = with_storage_timeout(async {
393 let system = get_memory_system().await?;
394 system
395 .update_session_metadata(session_id, name, description)
396 .await
397 .map_err(string_to_anyhow)?;
398
399 let session_arc = system
400 .get_session(session_id)
401 .await
402 .map_err(|e| anyhow::anyhow!("Failed to load session: {}", e))?;
403 let session = session_arc.load();
404 let (final_name, final_description) = session.get_metadata();
405
406 Ok(MCPToolResult::success(
407 "Session metadata updated successfully".to_string(),
408 Some(serde_json::json!({
409 "session_id": session_id.to_string(),
410 "name": final_name,
411 "description": final_description
412 })),
413 ))
414 })
415 .await;
416
417 match result {
418 Ok(success_result) => success_result,
419 Err(timeout_error) => {
420 error!(
421 "TIMEOUT: update_session_metadata - session_id: {}, error: {}",
422 session_id, timeout_error
423 );
424 Ok(MCPToolResult::error(format!(
425 "TIMEOUT: Failed to update session metadata: {}",
426 timeout_error
427 )))
428 }
429 }
430}
431
432async fn create_comprehensive_checkpoint(session: &ActiveSession) -> Result<SessionCheckpoint> {
434 Ok(SessionCheckpoint {
435 id: Uuid::new_v4(),
436 session_id: session.id(),
437 created_at: chrono::Utc::now(),
438 structured_context: (*session.current_state).clone(),
439 recent_updates: session.incremental_updates_owned(),
440 code_references: (*session.code_references).clone(),
441 change_history: session.change_history_owned(),
442 total_updates: session.incremental_updates.len(),
443 context_quality_score: 1.0,
444 compression_ratio: 1.0,
445 })
446}