agentic_workflow_mcp/session/
mod.rs1use std::path::{Path, PathBuf};
2use std::sync::Arc;
3use std::time::{Duration, Instant};
4
5use tokio::sync::Mutex;
6
7use agentic_workflow::engine::store::WorkflowStore;
8
9#[derive(Debug, Clone)]
11pub enum AutonomicProfile {
12 Desktop,
14 Server,
16 Terminal,
18}
19
20impl AutonomicProfile {
21 pub fn autosave_interval(&self) -> Duration {
22 match self {
23 Self::Desktop => Duration::from_secs(30),
24 Self::Server => Duration::from_secs(15),
25 Self::Terminal => Duration::from_secs(60),
26 }
27 }
28
29 pub fn maintenance_interval(&self) -> Duration {
30 match self {
31 Self::Desktop => Duration::from_secs(300),
32 Self::Server => Duration::from_secs(120),
33 Self::Terminal => Duration::from_secs(600),
34 }
35 }
36}
37
38pub struct SessionManager {
40 store: WorkflowStore,
41 session_id: String,
42 profile: AutonomicProfile,
43 started_at: Instant,
44 last_save: Instant,
45 mutation_count: u64,
46 data_path: PathBuf,
47}
48
49impl SessionManager {
50 pub fn open(path: impl AsRef<Path>) -> anyhow::Result<Self> {
52 let path = path.as_ref().to_path_buf();
53 let store = WorkflowStore::open(&path)
54 .map_err(|e| anyhow::anyhow!("Failed to open store: {}", e))?;
55
56 let session_id = uuid::Uuid::new_v4().to_string();
57 let now = Instant::now();
58
59 eprintln!("SessionManager: opened session {} at {}", session_id, path.display());
60
61 Ok(Self {
62 store,
63 session_id,
64 profile: AutonomicProfile::Desktop,
65 started_at: now,
66 last_save: now,
67 mutation_count: 0,
68 data_path: path,
69 })
70 }
71
72 pub fn open_memory() -> Self {
74 Self {
75 store: WorkflowStore::open_memory(),
76 session_id: uuid::Uuid::new_v4().to_string(),
77 profile: AutonomicProfile::Desktop,
78 started_at: Instant::now(),
79 last_save: Instant::now(),
80 mutation_count: 0,
81 data_path: PathBuf::new(),
82 }
83 }
84
85 pub fn store_mut(&mut self) -> &mut WorkflowStore {
87 self.mutation_count += 1;
88 &mut self.store
89 }
90
91 pub fn store(&self) -> &WorkflowStore {
93 &self.store
94 }
95
96 pub fn session_id(&self) -> &str {
98 &self.session_id
99 }
100
101 pub fn set_profile(&mut self, profile: AutonomicProfile) {
103 self.profile = profile;
104 }
105
106 pub fn mutation_count(&self) -> u64 {
108 self.mutation_count
109 }
110
111 pub fn uptime(&self) -> Duration {
113 self.started_at.elapsed()
114 }
115
116 pub fn maintenance_tick(&mut self) -> anyhow::Result<()> {
118 let since_save = self.last_save.elapsed();
119
120 if since_save >= self.profile.autosave_interval() && self.store.is_dirty() {
121 self.store.save()
122 .map_err(|e| anyhow::anyhow!("Autosave failed: {}", e))?;
123 self.last_save = Instant::now();
124 eprintln!(
125 "SessionManager: autosave ({} mutations, {}s since last save)",
126 self.mutation_count,
127 since_save.as_secs()
128 );
129 }
130
131 Ok(())
132 }
133
134 pub fn force_save(&mut self) -> anyhow::Result<()> {
136 self.store.save()
137 .map_err(|e| anyhow::anyhow!("Save failed: {}", e))?;
138 self.last_save = Instant::now();
139 Ok(())
140 }
141
142 pub fn stats(&self) -> serde_json::Value {
144 serde_json::json!({
145 "session_id": self.session_id,
146 "workflow_count": self.store.count(),
147 "mutation_count": self.mutation_count,
148 "uptime_secs": self.uptime().as_secs(),
149 "data_path": self.data_path.display().to_string(),
150 "profile": format!("{:?}", self.profile),
151 "is_dirty": self.store.is_dirty(),
152 })
153 }
154
155 pub fn shutdown(&mut self) -> anyhow::Result<()> {
157 if self.store.is_dirty() {
158 self.force_save()?;
159 }
160 eprintln!(
161 "SessionManager: shutdown session {} ({} mutations, {}s uptime)",
162 self.session_id, self.mutation_count, self.uptime().as_secs()
163 );
164 Ok(())
165 }
166}
167
168impl Drop for SessionManager {
169 fn drop(&mut self) {
170 if let Err(e) = self.shutdown() {
171 eprintln!("SessionManager: shutdown error on drop: {}", e);
172 }
173 }
174}
175
176pub fn create_shared_session(path: impl AsRef<Path>) -> anyhow::Result<Arc<Mutex<SessionManager>>> {
178 let manager = SessionManager::open(path)?;
179 Ok(Arc::new(Mutex::new(manager)))
180}
181
182pub async fn spawn_autosave(session: Arc<Mutex<SessionManager>>) {
184 tokio::spawn(async move {
185 let mut interval = tokio::time::interval(Duration::from_secs(10));
186 loop {
187 interval.tick().await;
188 let mut mgr = session.lock().await;
189 if let Err(e) = mgr.maintenance_tick() {
190 eprintln!("Autosave tick error: {}", e);
191 }
192 }
193 });
194}