Skip to main content

ytil_agents/agent/session_deletion/
cursor.rs

1use std::path::Path;
2
3use rootcause::prelude::ResultExt;
4use rootcause::report;
5
6use super::DeletionPlan;
7use crate::agent::session::SessionKey;
8
9pub(super) fn build_deletion_plan(
10    root: &Path,
11    key: &SessionKey,
12    selected_path: Option<&Path>,
13) -> rootcause::Result<DeletionPlan> {
14    if let Some(selected_path) = selected_path {
15        return plan_for_selected_path(root, key, selected_path);
16    }
17
18    let session_paths = crate::agent::session_loader::find_session_paths(
19        root,
20        crate::agent::session_loader::cursor::is_session_file,
21        |_| false,
22    )?;
23    let mut matches = Vec::new();
24    for meta_path in session_paths {
25        let Some(session_dir) = meta_path.parent() else {
26            continue;
27        };
28        if session_dir.file_name().and_then(|name| name.to_str()) == Some(key.id()) {
29            matches.push(session_dir.to_path_buf());
30        }
31    }
32    matches.sort();
33    matches.dedup();
34    let [path] = matches.as_slice() else {
35        return Err(
36            report!("selected Cursor session was not found uniquely in the session store")
37                .attach(format!("session_id={}", key.id()))
38                .attach(format!("matches={}", matches.len())),
39        );
40    };
41    Ok(DeletionPlan::new(key.clone(), vec![path.clone()], 0, Vec::new()))
42}
43
44fn plan_for_selected_path(root: &Path, key: &SessionKey, selected_path: &Path) -> rootcause::Result<DeletionPlan> {
45    let root = root
46        .canonicalize()
47        .context("failed to resolve Cursor session store")
48        .attach_with(|| format!("path={}", root.display()))?;
49    let selected_path = selected_path
50        .canonicalize()
51        .context("failed to resolve selected Cursor session path")
52        .attach_with(|| format!("path={}", selected_path.display()))?;
53    if !selected_path.starts_with(&root) {
54        return Err(report!("selected Cursor session path is outside the session store")
55            .attach(format!("path={}", selected_path.display()))
56            .attach(format!("root={}", root.display())));
57    }
58    if selected_path.file_name().and_then(|name| name.to_str()) != Some(key.id()) {
59        return Err(report!("selected Cursor session path does not match the session id")
60            .attach(format!("path={}", selected_path.display()))
61            .attach(format!("session_id={}", key.id())));
62    }
63    if !selected_path.join("meta.json").is_file() {
64        return Err(report!("selected Cursor session is missing meta.json")
65            .attach(format!("path={}", selected_path.display()))
66            .attach(format!("session_id={}", key.id())));
67    }
68
69    Ok(DeletionPlan::new(key.clone(), vec![selected_path], 0, Vec::new()))
70}