1use crate::agent::Message;
4use crate::{PawanError, Result};
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7
8#[derive(Debug, Serialize, Deserialize)]
10pub struct Session {
11 pub id: String,
13 pub model: String,
15 pub created_at: String,
17 pub updated_at: String,
19 pub messages: Vec<Message>,
21 #[serde(default)]
23 pub total_tokens: u64,
24 #[serde(default)]
26 pub iteration_count: u32,
27}
28
29impl Session {
30 pub fn new(model: &str) -> Self {
32 let id = uuid::Uuid::new_v4().to_string()[..8].to_string();
33 let now = chrono::Utc::now().to_rfc3339();
34 Self {
35 id,
36 model: model.to_string(),
37 created_at: now.clone(),
38 updated_at: now,
39 messages: Vec::new(),
40 total_tokens: 0,
41 iteration_count: 0,
42 }
43 }
44
45 pub fn sessions_dir() -> Result<PathBuf> {
47 let home = std::env::var("HOME").unwrap_or_else(|_| "/root".to_string());
48 let dir = PathBuf::from(home).join(".pawan").join("sessions");
49 if !dir.exists() {
50 std::fs::create_dir_all(&dir)
51 .map_err(|e| PawanError::Config(format!("Failed to create sessions dir: {}", e)))?;
52 }
53 Ok(dir)
54 }
55
56 pub fn save(&mut self) -> Result<PathBuf> {
58 self.updated_at = chrono::Utc::now().to_rfc3339();
59 let dir = Self::sessions_dir()?;
60 let path = dir.join(format!("{}.json", self.id));
61 let json = serde_json::to_string_pretty(self)
62 .map_err(|e| PawanError::Config(format!("Failed to serialize session: {}", e)))?;
63 std::fs::write(&path, json)
64 .map_err(|e| PawanError::Config(format!("Failed to write session: {}", e)))?;
65 Ok(path)
66 }
67
68 pub fn load(id: &str) -> Result<Self> {
70 let dir = Self::sessions_dir()?;
71 let path = dir.join(format!("{}.json", id));
72 if !path.exists() {
73 return Err(PawanError::NotFound(format!("Session not found: {}", id)));
74 }
75 let content = std::fs::read_to_string(&path)
76 .map_err(|e| PawanError::Config(format!("Failed to read session: {}", e)))?;
77 serde_json::from_str(&content)
78 .map_err(|e| PawanError::Config(format!("Failed to parse session: {}", e)))
79 }
80
81 pub fn list() -> Result<Vec<SessionSummary>> {
83 let dir = Self::sessions_dir()?;
84 let mut sessions = Vec::new();
85
86 if let Ok(entries) = std::fs::read_dir(&dir) {
87 for entry in entries.flatten() {
88 let path = entry.path();
89 if path.extension().is_some_and(|ext| ext == "json") {
90 if let Ok(content) = std::fs::read_to_string(&path) {
91 if let Ok(session) = serde_json::from_str::<Session>(&content) {
92 sessions.push(SessionSummary {
93 id: session.id,
94 model: session.model,
95 created_at: session.created_at,
96 updated_at: session.updated_at,
97 message_count: session.messages.len(),
98 });
99 }
100 }
101 }
102 }
103 }
104
105 sessions.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
106 Ok(sessions)
107 }
108}
109
110#[derive(Debug, Serialize, Deserialize)]
112pub struct SessionSummary {
113 pub id: String,
114 pub model: String,
115 pub created_at: String,
116 pub updated_at: String,
117 pub message_count: usize,
118}