Skip to main content

oxios_kernel/kernel_handle/
mod.rs

1//! Kernel facade — 10 domain Facades composing the System Call API.
2
3pub mod a2a_api;
4pub mod agent_api;
5pub mod browser_api;
6pub mod exec_api;
7pub mod extension_api;
8pub mod infra_api;
9pub mod mcp_api;
10pub mod persona_api;
11pub mod security_api;
12pub mod space_api;
13pub mod state_api;
14
15pub use a2a_api::A2aApi;
16pub use agent_api::AgentApi;
17pub use browser_api::BrowserApi;
18pub use exec_api::ExecApi;
19pub use extension_api::ExtensionApi;
20pub use infra_api::InfraApi;
21pub use mcp_api::McpApi;
22pub use persona_api::PersonaApi;
23pub use security_api::SecurityApi;
24pub use space_api::SpaceApi;
25pub use state_api::StateApi;
26
27use crate::a2a::A2AProtocol;
28use crate::access_manager::AccessManager;
29use crate::audit_trail::AuditTrail;
30use crate::auth::AuthManager;
31use crate::budget::BudgetManager;
32use crate::config::OxiosConfig;
33use crate::cron::CronScheduler;
34use crate::event_bus::EventBus;
35use crate::git_layer::CommitInfo;
36use crate::git_layer::GitLayer;
37use crate::host_tools::HostToolValidator;
38use crate::mcp::McpBridge;
39use crate::memory::MemoryManager;
40use crate::persona_manager::PersonaManager;
41use crate::program::ProgramManager;
42use crate::resource_monitor::ResourceMonitor;
43use crate::scheduler::AgentScheduler;
44use crate::skill::SkillStore;
45use crate::space::SpaceManager;
46use crate::state_store::StateStore;
47use crate::supervisor::Supervisor;
48use serde::Serialize;
49use std::sync::Arc;
50use std::time::Instant;
51
52/// Oxios kernel System Call API — composed of 10 domain Facades.
53///
54/// Each Facade groups related system calls:
55/// - [`StateApi`]    — data persistence, sessions
56/// - [`AgentApi`]    — agent lifecycle, budgets, memory
57/// - [`SecurityApi`] — auth, audit trail, RBAC, approvals
58/// - [`PersonaApi`]  — multi-persona management
59/// - [`ExtensionApi`] — programs, skills, host tools
60/// - [`McpApi`]      — MCP server bridge
61/// - [`SpaceApi`]    — Space management, knowledge flow
62/// - [`ExecApi`]     — execution config, access management
63/// - [`BrowserApi`]  — browser backend
64/// - [`A2aApi`]      — agent-to-agent communication
65pub struct KernelHandle {
66    /// State management: save/load/sessions.
67    pub state: StateApi,
68    /// Agent management: lifecycle/budgets/memory.
69    pub agents: AgentApi,
70    /// Security: auth/audit/RBAC/approvals.
71    pub security: SecurityApi,
72    /// Persona management.
73    pub persona: PersonaApi,
74    /// Extensions: programs/skills/host tools.
75    pub extensions: ExtensionApi,
76    /// MCP server bridge.
77    pub mcp: McpApi,
78    /// Infrastructure: Git/scheduler/cron/resources/events/system.
79    pub infra: InfraApi,
80    /// Space management: context partitioning, knowledge flow.
81    pub spaces: SpaceApi,
82    /// Execution: config + access management.
83    pub exec: ExecApi,
84    /// Browser backend (zero-sized when `browser` feature is disabled).
85    pub browser: BrowserApi,
86    /// Agent-to-agent communication.
87    pub a2a: A2aApi,
88}
89
90impl KernelHandle {
91    /// Create a new KernelHandle from 11 domain Facades.
92    ///
93    /// Each Facade is assembled independently in `kernel.rs` and passed here.
94    /// This enables testing individual Facades without the full kernel.
95    #[allow(clippy::too_many_arguments)]
96    pub fn new(
97        state: StateApi,
98        agents: AgentApi,
99        security: SecurityApi,
100        persona: PersonaApi,
101        extensions: ExtensionApi,
102        mcp: McpApi,
103        infra: InfraApi,
104        spaces: SpaceApi,
105        exec: ExecApi,
106        browser: BrowserApi,
107        a2a: A2aApi,
108    ) -> Self {
109        Self {
110            state,
111            agents,
112            security,
113            persona,
114            extensions,
115            mcp,
116            infra,
117            spaces,
118            exec,
119            browser,
120            a2a,
121        }
122    }
123
124    /// Build a KernelHandle from raw subsystem parameters.
125    ///
126    /// Prefer [`KernelHandle::new()`] which takes pre-built Facades.
127    #[deprecated(note = "Use KernelHandle::new() with pre-built Facades instead")]
128    #[allow(clippy::too_many_arguments)]
129    pub fn from_subsystems(
130        state_store: Arc<StateStore>,
131        event_bus: EventBus,
132        supervisor: Arc<dyn Supervisor>,
133        scheduler: Arc<AgentScheduler>,
134        memory_manager: Arc<MemoryManager>,
135        git_layer: Arc<GitLayer>,
136        audit_trail: Arc<AuditTrail>,
137        budget_manager: Arc<BudgetManager>,
138        resource_monitor: Arc<ResourceMonitor>,
139        cron_scheduler: Arc<CronScheduler>,
140        program_manager: Arc<ProgramManager>,
141        skill_store: Arc<SkillStore>,
142        persona_manager: Arc<PersonaManager>,
143        mcp_bridge: Arc<McpBridge>,
144        auth_manager: Arc<parking_lot::Mutex<AuthManager>>,
145        access_manager: Arc<parking_lot::Mutex<AccessManager>>,
146        host_tool_validator: Arc<HostToolValidator>,
147        config: OxiosConfig,
148        start_time: Instant,
149        space_manager: Arc<SpaceManager>,
150    ) -> Self {
151        Self {
152            security: SecurityApi::new(
153                auth_manager.clone(),
154                audit_trail,
155                access_manager.clone(),
156                state_store.clone(),
157            ),
158            state: StateApi::new(state_store),
159            agents: AgentApi::new(supervisor, budget_manager, memory_manager),
160            persona: PersonaApi::new(persona_manager),
161            extensions: ExtensionApi::new(program_manager, skill_store, host_tool_validator),
162            mcp: McpApi::new(mcp_bridge),
163            infra: InfraApi::new(
164                git_layer,
165                scheduler,
166                cron_scheduler,
167                resource_monitor,
168                event_bus.clone(),
169                config.clone(),
170                start_time,
171            ),
172            spaces: SpaceApi::new(space_manager, event_bus),
173            exec: ExecApi::new(Arc::new(config.exec.clone()), access_manager),
174            browser: BrowserApi::default(),
175            a2a: A2aApi::new(Arc::new(A2AProtocol::new(crate::EventBus::new(0)))),
176        }
177    }
178
179    // ═══════════════════════════════════════════════════════════════════════
180    // Convenience methods (cross-Facades orchestration)
181    // ═══════════════════════════════════════════════════════════════════════
182
183    /// Save data and commit to git (State + Infra).
184    pub async fn save_and_commit<T: Serialize>(
185        &self,
186        category: &str,
187        name: &str,
188        data: &T,
189    ) -> anyhow::Result<()> {
190        self.state.save(category, name, data).await?;
191        let git = self.infra.git();
192        if git.is_enabled() {
193            let rel_path = format!("{}/{}.json", category, name);
194            let _ = git.commit_file(&rel_path, &format!("save {}/{}", category, name));
195        }
196        Ok(())
197    }
198
199    /// Save markdown and commit to git (State + Infra).
200    pub async fn save_markdown_and_commit(
201        &self,
202        category: &str,
203        name: &str,
204        content: &str,
205    ) -> anyhow::Result<()> {
206        self.state.save_markdown(category, name, content).await?;
207        let git = self.infra.git();
208        if git.is_enabled() {
209            let rel_path = format!("{}/{}.md", category, name);
210            let _ = git.commit_file(&rel_path, &format!("save {}/{}", category, name));
211        }
212        Ok(())
213    }
214
215    /// Delete a file and commit the removal to git (State + Infra).
216    pub async fn delete_and_commit(&self, category: &str, name: &str) -> anyhow::Result<bool> {
217        let deleted = self.state.delete(category, name).await?;
218        if deleted {
219            let git = self.infra.git();
220            if git.is_enabled() {
221                let rel_path = format!("{}/{}.json", category, name);
222                let _ = git.remove_file(&rel_path, &format!("delete {}/{}", category, name));
223            }
224        }
225        Ok(deleted)
226    }
227
228    /// Commit all current changes to git.
229    pub fn commit_all(&self, message: &str) -> anyhow::Result<Option<CommitInfo>> {
230        self.state.commit_all(self.infra.git(), message)
231    }
232
233    /// Flush audit trail and commit to git (Security + Infra).
234    pub fn flush_audit(&self) -> anyhow::Result<()> {
235        self.security.flush(self.infra.git())
236    }
237
238    /// Schedule a cron job by expression (convenience wrapper).
239    pub async fn schedule(
240        &self,
241        cron_expr: &str,
242        task: &str,
243        persona: Option<&str>,
244    ) -> anyhow::Result<String> {
245        let _persona = persona.unwrap_or("default");
246        let job = crate::cron::CronJob::new(
247            format!("job_{}", uuid::Uuid::new_v4()),
248            cron_expr.to_string(),
249            task.to_string(),
250        );
251        let job_id = self.infra.add_cron(job).await?;
252        Ok(job_id.to_string())
253    }
254
255    /// Unschedule a cron job by string ID (convenience wrapper).
256    pub async fn unschedule(&self, job_id: &str) -> anyhow::Result<bool> {
257        let uuid =
258            uuid::Uuid::parse_str(job_id).map_err(|e| anyhow::anyhow!("invalid job id: {e}"))?;
259        match self.infra.remove_cron(uuid).await {
260            Ok(()) => Ok(true),
261            Err(_) => Ok(false),
262        }
263    }
264
265    /// List cron jobs (convenience wrapper).
266    pub fn list_schedules(&self) -> Vec<crate::cron::CronJob> {
267        self.infra.list_crons()
268    }
269
270    /// Load JSON from state store.
271    pub async fn load_json<T: serde::de::DeserializeOwned>(
272        &self,
273        category: &str,
274        name: &str,
275    ) -> anyhow::Result<Option<T>> {
276        self.state.load(category, name).await
277    }
278
279    /// Get kernel start time.
280    pub fn start_time(&self) -> std::time::Instant {
281        self.infra.start_time
282    }
283}