1pub mod a2a_api;
4pub mod agent_api;
5pub mod calendar_api;
6pub mod email_api;
7pub mod engine_api;
8pub mod exec_api;
9pub mod extension_api;
10pub mod infra_api;
11pub mod knowledge_lens;
12pub mod marketplace_api;
13pub mod mcp_api;
14pub mod memory_api;
15pub mod persona_api;
16pub mod project_api;
17pub mod security_api;
18pub mod state_api;
19
20pub use a2a_api::A2aApi;
21pub use agent_api::AgentApi;
22pub use calendar_api::CalendarApi;
23pub use email_api::EmailApi;
24pub use engine_api::{
25 EngineApi, EngineConfigResponse, FallbackEvent, InputModality, ModelInfo, ProviderCategory,
26 ProviderInfo, RoutingConfigSnapshot, RoutingStats, RoutingStatsSnapshot, RoutingUpdate,
27 ValidateKeyResult,
28};
29pub use exec_api::ExecApi;
30pub use exec_api::SharedExecConfig;
31pub use extension_api::ExtensionApi;
32pub use infra_api::InfraApi;
33pub use knowledge_lens::{
34 CopilotResponse, KnowledgeContext, KnowledgeLens, KnowledgeNote, MemoryNote,
35};
36pub use marketplace_api::MarketplaceApi;
37pub use mcp_api::McpApi;
38pub use memory_api::MemoryApi;
39pub use persona_api::PersonaApi;
40pub use project_api::{ProjectApi, ProjectInfo};
41pub use security_api::SecurityApi;
42pub use state_api::StateApi;
43
44use crate::a2a::A2AProtocol;
45use crate::access_manager::AccessManager;
46use crate::auth::AuthManager;
47use crate::budget::BudgetManager;
48use crate::config::OxiosConfig;
49use crate::cron::CronScheduler;
50use crate::event_bus::EventBus;
51use crate::git_layer::CommitInfo;
52use crate::git_layer::GitLayer;
53use crate::mcp::McpBridge;
54use crate::memory::MemoryManager;
55use crate::persona::PersonaManager;
56use crate::resource_monitor::ResourceMonitor;
57use crate::scheduler::AgentScheduler;
58use crate::skill::clawhub::{ClawHubClient, ClawHubInstaller};
59use crate::skill::skills_sh::{SkillsShClient, SkillsShInstaller};
60use crate::skill::SkillManager;
61use crate::state_store::StateStore;
62use crate::supervisor::Supervisor;
63use oxi_sdk::observability::AuditTrail;
64use serde::Serialize;
65use std::sync::Arc;
66use std::time::Instant;
67
68pub struct KernelHandle {
83 pub state: StateApi,
85 pub agents: AgentApi,
87 pub security: SecurityApi,
89 pub persona: PersonaApi,
91 pub extensions: ExtensionApi,
93 pub mcp: McpApi,
95 pub infra: InfraApi,
97 pub projects: Option<ProjectApi>,
99 pub exec: ExecApi,
101 pub a2a: A2aApi,
103 pub engine: EngineApi,
105 pub knowledge: Arc<oxios_markdown::KnowledgeBase>,
107 pub knowledge_lens: Arc<KnowledgeLens>,
109 pub marketplace_api: MarketplaceApi,
111 pub calendar: Option<CalendarApi>,
113 pub email: Option<EmailApi>,
115}
116
117impl KernelHandle {
118 #[allow(clippy::too_many_arguments)]
123 pub fn new(
124 state: StateApi,
125 agents: AgentApi,
126 security: SecurityApi,
127 persona: PersonaApi,
128 extensions: ExtensionApi,
129 mcp: McpApi,
130 infra: InfraApi,
131 projects: Option<ProjectApi>,
132 exec: ExecApi,
133 a2a: A2aApi,
134 engine: EngineApi,
135 knowledge: Arc<oxios_markdown::KnowledgeBase>,
136 knowledge_lens: Arc<KnowledgeLens>,
137 marketplace_api: MarketplaceApi,
138 calendar: Option<CalendarApi>,
139 email: Option<EmailApi>,
140 ) -> Self {
141 Self {
142 state,
143 agents,
144 security,
145 persona,
146 extensions,
147 mcp,
148 infra,
149 projects,
150 exec,
151 a2a,
152 engine,
153 knowledge,
154 knowledge_lens,
155 marketplace_api,
156 calendar,
157 email,
158 }
159 }
160
161 #[deprecated(note = "Use KernelHandle::new() with pre-built Facades instead")]
165 #[allow(clippy::too_many_arguments)]
166 pub fn from_subsystems(
167 state_store: Arc<StateStore>,
168 event_bus: EventBus,
169 supervisor: Arc<dyn Supervisor>,
170 scheduler: Arc<AgentScheduler>,
171 memory_manager: Arc<MemoryManager>,
172 git_layer: Arc<GitLayer>,
173 audit_trail: Arc<AuditTrail>,
174 budget_manager: Arc<BudgetManager>,
175 resource_monitor: Arc<ResourceMonitor>,
176 cron_scheduler: Arc<CronScheduler>,
177 skill_manager: Arc<SkillManager>,
178 persona_manager: Arc<PersonaManager>,
179 mcp_bridge: Arc<McpBridge>,
180 auth_manager: Arc<parking_lot::Mutex<AuthManager>>,
181 access_manager: Arc<parking_lot::Mutex<AccessManager>>,
182 config: OxiosConfig,
183 start_time: Instant,
184 ) -> Self {
185 let knowledge_dir = state_store.base_path.join("knowledge");
186 let knowledge = Arc::new(
187 oxios_markdown::KnowledgeBase::new(knowledge_dir)
188 .expect("Failed to create KnowledgeBase"),
189 );
190 let knowledge_lens = Arc::new(
191 KnowledgeLens::new(knowledge.clone(), memory_manager.clone())
192 .expect("Failed to create KnowledgeLens"),
193 );
194 Self {
195 security: SecurityApi::new(
196 auth_manager.clone(),
197 audit_trail,
198 access_manager.clone(),
199 state_store.clone(),
200 ),
201 state: StateApi::new(state_store.clone()),
202 agents: AgentApi::new(
203 supervisor,
204 budget_manager,
205 memory_manager,
206 Some(event_bus.clone()),
207 ),
208 persona: PersonaApi::new(persona_manager),
209 extensions: ExtensionApi::new(skill_manager),
210 mcp: McpApi::new(mcp_bridge),
211 infra: InfraApi::new(
212 git_layer,
213 scheduler,
214 cron_scheduler,
215 resource_monitor,
216 event_bus.clone(),
217 config.clone(),
218 start_time,
219 ),
220 projects: None,
221 exec: ExecApi::new(
222 Arc::new(parking_lot::RwLock::new(config.exec.clone())),
223 access_manager,
224 ),
225 #[allow(clippy::default_trait_access)]
226 a2a: A2aApi::new(Arc::new(A2AProtocol::new(crate::EventBus::new(0)))),
227 engine: EngineApi::new(
228 Arc::new(parking_lot::RwLock::new(config.clone())),
229 std::path::PathBuf::from("~/.oxios/config.toml"),
230 Arc::new(RoutingStats::new()),
231 Arc::new(crate::engine::EngineHandle::new(Arc::new(
232 crate::engine::OxiosEngine::new("anthropic/claude-sonnet-4-20250514"),
233 ))),
234 ),
235 knowledge,
236 knowledge_lens,
237 marketplace_api: MarketplaceApi::new(
238 Arc::new(ClawHubInstaller::new(
239 state_store.base_path.join("skills"),
240 state_store.base_path.clone(),
241 config.marketplace.base_url.clone(),
242 )),
243 Arc::new(
244 ClawHubClient::new(config.marketplace.base_url.clone())
245 .expect("valid ClawHub client"),
246 ),
247 Arc::new(SkillsShInstaller::new(
248 state_store.base_path.join("skills"),
249 None,
250 None,
251 )),
252 Arc::new(SkillsShClient::new(None, None).expect("valid Skills.sh client")),
253 ),
254 calendar: None, email: None, }
257 }
258
259 pub async fn save_and_commit<T: Serialize>(
265 &self,
266 category: &str,
267 name: &str,
268 data: &T,
269 ) -> anyhow::Result<()> {
270 self.state.save(category, name, data).await?;
271 let git = self.infra.git();
272 if git.is_enabled() {
273 let rel_path = format!("{category}/{name}.json");
274 let _ = git.commit_file(&rel_path, &format!("save {category}/{name}"));
275 }
276 Ok(())
277 }
278
279 pub async fn save_markdown_and_commit(
281 &self,
282 category: &str,
283 name: &str,
284 content: &str,
285 ) -> anyhow::Result<()> {
286 self.state.save_markdown(category, name, content).await?;
287 let git = self.infra.git();
288 if git.is_enabled() {
289 let rel_path = format!("{category}/{name}.md");
290 let _ = git.commit_file(&rel_path, &format!("save {category}/{name}"));
291 }
292 Ok(())
293 }
294
295 pub async fn delete_and_commit(&self, category: &str, name: &str) -> anyhow::Result<bool> {
297 let deleted = self.state.delete(category, name).await?;
298 if deleted {
299 let git = self.infra.git();
300 if git.is_enabled() {
301 let rel_path = format!("{category}/{name}.json");
302 let _ = git.remove_file(&rel_path, &format!("delete {category}/{name}"));
303 }
304 }
305 Ok(deleted)
306 }
307
308 pub fn commit_all(&self, message: &str) -> anyhow::Result<Option<CommitInfo>> {
310 self.state.commit_all(self.infra.git(), message)
311 }
312
313 pub fn flush_audit(&self) -> anyhow::Result<()> {
315 self.security.flush(self.infra.git())
316 }
317
318 pub async fn schedule(
320 &self,
321 cron_expr: &str,
322 task: &str,
323 persona: Option<&str>,
324 ) -> anyhow::Result<String> {
325 let _persona = persona.unwrap_or("default");
326 let job = crate::cron::CronJob::new(
327 format!("job_{}", uuid::Uuid::new_v4()),
328 cron_expr.to_string(),
329 task.to_string(),
330 );
331 let job_id = self.infra.add_cron(job).await?;
332 Ok(job_id.to_string())
333 }
334
335 pub async fn unschedule(&self, job_id: &str) -> anyhow::Result<bool> {
337 let uuid =
338 uuid::Uuid::parse_str(job_id).map_err(|e| anyhow::anyhow!("invalid job id: {e}"))?;
339 match self.infra.remove_cron(uuid).await {
340 Ok(()) => Ok(true),
341 Err(_) => Ok(false),
342 }
343 }
344
345 pub fn list_schedules(&self) -> Vec<crate::cron::CronJob> {
347 self.infra.list_crons()
348 }
349
350 pub async fn load_json<T: serde::de::DeserializeOwned>(
352 &self,
353 category: &str,
354 name: &str,
355 ) -> anyhow::Result<Option<T>> {
356 self.state.load(category, name).await
357 }
358
359 pub fn start_time(&self) -> std::time::Instant {
361 self.infra.start_time
362 }
363
364 pub fn marketplace_api(&self) -> &MarketplaceApi {
366 &self.marketplace_api
367 }
368
369 pub fn memory(&self) -> MemoryApi {
377 let mm = self.agents.memory_manager().clone();
381 MemoryApi::new(mm)
382 }
383}