Skip to main content

oxios_kernel/kernel_handle/
state_api.rs

1//! State API — data persistence, session management.
2
3use crate::state_store::{
4    PruneConfig, PruneThrottle, Session, SessionId, SessionSummary, StateStore,
5};
6use serde::{de::DeserializeOwned, Serialize};
7use std::sync::Arc;
8
9/// State management system calls.
10///
11/// All data persistence: file (JSON/Markdown) storage, session management.
12pub struct StateApi {
13    pub(crate) state_store: Arc<StateStore>,
14    /// Throttle for auto-prune to avoid excessive disk scans.
15    pub prune_throttle: PruneThrottle,
16}
17
18impl StateApi {
19    /// Create a new StateApi.
20    pub fn new(state_store: Arc<StateStore>) -> Self {
21        Self {
22            state_store,
23            prune_throttle: PruneThrottle::new(3600), // 1 hour cooldown
24        }
25    }
26    /// Save JSON data.
27    pub async fn save<T: Serialize>(
28        &self,
29        category: &str,
30        name: &str,
31        data: &T,
32    ) -> anyhow::Result<()> {
33        self.state_store.save_json(category, name, data).await
34    }
35
36    /// Save markdown content.
37    pub async fn save_markdown(
38        &self,
39        category: &str,
40        name: &str,
41        content: &str,
42    ) -> anyhow::Result<()> {
43        self.state_store
44            .save_markdown(category, name, content)
45            .await
46    }
47
48    /// Load JSON data.
49    pub async fn load<T: DeserializeOwned>(
50        &self,
51        category: &str,
52        name: &str,
53    ) -> anyhow::Result<Option<T>> {
54        self.state_store.load_json(category, name).await
55    }
56
57    /// Load markdown content.
58    pub async fn load_markdown(
59        &self,
60        category: &str,
61        name: &str,
62    ) -> anyhow::Result<Option<String>> {
63        self.state_store.load_markdown(category, name).await
64    }
65
66    /// Delete a file.
67    pub async fn delete(&self, category: &str, name: &str) -> anyhow::Result<bool> {
68        self.state_store.delete_file(category, name).await
69    }
70
71    /// List files in a category.
72    pub async fn list_category(&self, category: &str) -> anyhow::Result<Vec<String>> {
73        self.state_store.list_category(category).await
74    }
75
76    /// Commit all changes to git via the provided GitLayer.
77    pub fn commit_all(
78        &self,
79        git: &crate::git_layer::GitLayer,
80        message: &str,
81    ) -> anyhow::Result<Option<crate::git_layer::CommitInfo>> {
82        if !git.is_enabled() {
83            return Ok(None);
84        }
85        git.commit_file(".", message)
86            .ok()
87            .map_or(Ok(None), |info| Ok(Some(info)))
88    }
89
90    /// Save session.
91    pub async fn save_session(&self, session: &Session) -> anyhow::Result<()> {
92        self.state_store.save_session(session).await
93    }
94
95    /// Load session.
96    pub async fn load_session(&self, id: &SessionId) -> anyhow::Result<Option<Session>> {
97        self.state_store.load_session(id).await
98    }
99
100    /// List sessions.
101    pub async fn list_sessions(&self) -> anyhow::Result<Vec<SessionSummary>> {
102        self.state_store.list_sessions().await
103    }
104
105    /// Delete session.
106    pub async fn delete_session(&self, id: &SessionId) -> anyhow::Result<bool> {
107        self.state_store.delete_session(id).await
108    }
109
110    /// Get workspace base path.
111    pub fn workspace_path(&self) -> &std::path::Path {
112        &self.state_store.base_path
113    }
114
115    /// Access the underlying StateStore (for backup/restore).
116    pub fn store(&self) -> &Arc<StateStore> {
117        &self.state_store
118    }
119
120    /// Prune sessions based on configuration.
121    ///
122    /// Removes sessions that exceed TTL or exceed the maximum count.
123    pub async fn prune_sessions(&self, config: &PruneConfig) -> anyhow::Result<usize> {
124        self.state_store.prune_sessions(config).await
125    }
126
127    /// Check if auto-prune should run (respects cooldown throttle).
128    pub fn should_auto_prune(&self) -> bool {
129        self.prune_throttle.should_prune()
130    }
131}