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