Skip to main content

oxios_kernel/tools/
kernel_bridge.rs

1//! KernelToolProvider bridge — plugs oxios kernel tools into oxi-sdk agent builder.
2//!
3//! Implements [`oxi_sdk::KernelToolProvider`] so that oxios kernel tools
4//! (exec, memory, browser, etc.) can be registered into the SDK's
5//! `AgentBuilder` via `.kernel_tools()`.
6
7use std::sync::Arc;
8
9use oxi_sdk::SearchCache;
10use oxi_sdk::ToolRegistry;
11use oxi_sdk::{
12    KernelToolContext as SdkKernelToolContext, KernelToolProvider as SdkKernelToolProvider,
13};
14
15use crate::tools::registration::register_always_on;
16use crate::KernelHandle;
17
18/// Bridges all oxios kernel tools into the oxi-sdk agent builder.
19pub struct OxiosKernelBridge {
20    kernel_handle: Arc<KernelHandle>,
21    search_cache: Arc<SearchCache>,
22}
23
24impl OxiosKernelBridge {
25    /// Create a new bridge with the given kernel handle.
26    pub fn new(kernel_handle: Arc<KernelHandle>) -> Self {
27        Self {
28            kernel_handle,
29            search_cache: Arc::new(SearchCache::new()),
30        }
31    }
32
33    /// Create a new bridge with a pre-built search cache.
34    pub fn with_cache(kernel_handle: Arc<KernelHandle>, search_cache: Arc<SearchCache>) -> Self {
35        Self {
36            kernel_handle,
37            search_cache,
38        }
39    }
40}
41
42impl SdkKernelToolProvider for OxiosKernelBridge {
43    fn tool_names(&self) -> Vec<&str> {
44        vec![
45            // Always-on file tools
46            "read",
47            "write",
48            "edit",
49            "grep",
50            "find",
51            "ls",
52            // Kernel domain
53            "exec",
54            "memory_read",
55            "memory_write",
56            "memory_search",
57            "project",
58            "agent",
59            "a2a_delegate",
60            "a2a_send",
61            "a2a_query",
62            "persona",
63            "cron",
64            "security",
65            "budget",
66            "resource",
67            "mcp",
68            "browser",
69            "knowledge",
70            // Marketplace (ClawHub)
71            "marketplace",
72            // Calendar
73            "calendar",
74            // Email
75            "send_email",
76        ]
77    }
78
79    fn register_tools(&self, registry: &ToolRegistry, context: &SdkKernelToolContext) {
80        // 1. Always-on file tools + web search
81        register_always_on(registry, Arc::clone(&self.search_cache));
82
83        // 2. Kernel domain tools via KernelHandle
84        crate::tools::builtin::register_all_kernel_tools(
85            registry,
86            &self.kernel_handle,
87            &context.agent_id,
88        );
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    /// Verify that `tool_names()` returns the expected number of tool names.
97    #[tokio::test]
98    async fn test_tool_names_length() {
99        let tmp = tempfile::tempdir().unwrap();
100        let base = tmp.path().to_path_buf();
101
102        // Build a minimal KernelHandle for testing
103        let state_store =
104            Arc::new(crate::state_store::StateStore::new(base.join("workspace")).unwrap());
105
106        let kernel = Arc::new(crate::KernelHandle::new(
107            crate::StateApi::new(state_store.clone()),
108            crate::AgentApi::new(
109                Arc::new(crate::supervisor::NoOpSupervisor),
110                Arc::new(crate::budget::BudgetManager::new()),
111                Arc::new(crate::memory::MemoryManager::new(state_store.clone())),
112                None,
113            ),
114            crate::SecurityApi::new(
115                Arc::new(parking_lot::Mutex::new(crate::auth::AuthManager::new())),
116                Arc::new(oxi_sdk::observability::AuditTrail::new(100)),
117                Arc::new(parking_lot::Mutex::new(
118                    crate::access_manager::AccessManager::new(),
119                )),
120                state_store.clone(),
121            ),
122            crate::PersonaApi::new(Arc::new(crate::persona::PersonaManager::new())),
123            crate::ExtensionApi::new(Arc::new(crate::skill::SkillManager::new(
124                base.join("skills"),
125                base.join("share/skills"),
126            ))),
127            crate::McpApi::new(Arc::new(crate::mcp::McpBridge::new())),
128            crate::InfraApi::new(
129                Arc::new(crate::git_layer::GitLayer::new(base.join("git"), false).unwrap()),
130                Arc::new(crate::scheduler::AgentScheduler::new(5, 60, 300)),
131                Arc::new(crate::cron::CronScheduler::new(state_store.clone(), 60)),
132                Arc::new(crate::resource_monitor::ResourceMonitor::new(60, 60)),
133                crate::event_bus::EventBus::new(256),
134                crate::OxiosConfig::default(),
135                std::time::Instant::now(),
136            ),
137            None,
138            crate::ExecApi::new(
139                Arc::new(parking_lot::RwLock::new(
140                    crate::config::ExecConfig::default(),
141                )),
142                Arc::new(parking_lot::Mutex::new(
143                    crate::access_manager::AccessManager::new(),
144                )),
145            ),
146            crate::A2aApi::new(Arc::new(crate::a2a::A2AProtocol::new(
147                crate::event_bus::EventBus::new(256),
148            ))),
149            crate::EngineApi::new(
150                Arc::new(parking_lot::RwLock::new(crate::OxiosConfig::default())),
151                base.join("config.toml"),
152                Arc::new(crate::RoutingStats::new()),
153                Arc::new(crate::engine::EngineHandle::new(Arc::new(
154                    crate::OxiosEngine::new("anthropic/claude-sonnet-4-20250514"),
155                ))),
156            ),
157            Arc::new(oxios_markdown::KnowledgeBase::new(base.join("knowledge")).unwrap()),
158            Arc::new(
159                crate::kernel_handle::KnowledgeLens::new(
160                    Arc::new(
161                        oxios_markdown::KnowledgeBase::new(base.join("knowledge_lens")).unwrap(),
162                    ),
163                    Arc::new(crate::memory::MemoryManager::new(state_store.clone())),
164                )
165                .unwrap(),
166            ),
167            crate::MarketplaceApi::new(
168                Arc::new(crate::skill::clawhub::ClawHubInstaller::new(
169                    base.join("skills"),
170                    base.join("workspace"),
171                    None,
172                )),
173                Arc::new(
174                    crate::skill::clawhub::ClawHubClient::new(None).expect("valid ClawHub client"),
175                ),
176                Arc::new(crate::skill::skills_sh::SkillsShInstaller::new(
177                    base.join("skills"),
178                    None,
179                    None,
180                )),
181                Arc::new(
182                    crate::skill::skills_sh::SkillsShClient::new(None, None)
183                        .expect("valid Skills.sh client"),
184                ),
185            ),
186            None, // calendar (not configured in test)
187            None, // email (not configured in test)
188        ));
189
190        let bridge = OxiosKernelBridge::new(kernel);
191
192        let names = bridge.tool_names();
193        // 6 always-on + 18 kernel domain = 24 ... plus knowledge = 25 ... plus calendar = 25 (optional, not configured)
194        // plus send_email = 26
195        assert_eq!(names.len(), 26, "expected 26 tools, got {:?}", names);
196    }
197}