Skip to main content

oxi_sdk/
kernel_bridge.rs

1//! Kernel tool bridge — allows oxios kernel tools to be plugged into the SDK.
2//!
3//! oxios-kernel implements `KernelToolProvider` to register its tools
4//! (exec, memory, browser, persona, etc.) into the SDK's agent builder.
5
6use oxi_agent::ToolRegistry;
7use std::path::PathBuf;
8
9/// Context provided to kernel tool providers during registration.
10///
11/// Contains the metadata that kernel tools need to operate correctly
12/// within an oxios agent session.
13#[derive(Debug, Clone)]
14pub struct KernelToolContext {
15    /// Agent's workspace directory.
16    pub workspace_dir: PathBuf,
17    /// oxios agent identifier.
18    pub agent_id: String,
19    /// Session identifier, if available.
20    pub session_id: Option<String>,
21    /// CSpace-based permission list.
22    pub permissions: Vec<String>,
23}
24
25impl KernelToolContext {
26    /// Create a new context with the given workspace and agent ID.
27    pub fn new(workspace_dir: impl Into<PathBuf>, agent_id: impl Into<String>) -> Self {
28        Self {
29            workspace_dir: workspace_dir.into(),
30            agent_id: agent_id.into(),
31            session_id: None,
32            permissions: Vec::new(),
33        }
34    }
35
36    /// Set the session ID.
37    pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
38        self.session_id = Some(session_id.into());
39        self
40    }
41
42    /// Set the permissions list.
43    pub fn with_permissions(mut self, permissions: Vec<String>) -> Self {
44        self.permissions = permissions;
45        self
46    }
47}
48
49/// Trait for providing kernel-level tools to the SDK.
50///
51/// oxios-kernel implements this trait to bridge its native tools
52/// (exec, memory, browser, persona, etc.) into the oxi agent tool registry.
53///
54/// # Example
55///
56/// ```ignore
57/// use oxi_sdk::{KernelToolProvider, KernelToolContext};
58/// use oxi_agent::ToolRegistry;
59///
60/// struct MyKernelBridge;
61///
62/// impl KernelToolProvider for MyKernelBridge {
63///     fn tool_names(&self) -> Vec<&str> {
64///         vec!["exec", "memory"]
65///     }
66///
67///     fn register_tools(&self, registry: &ToolRegistry, ctx: &KernelToolContext) {
68///         registry.register(ExecTool::new(ctx.agent_id.clone()));
69///         registry.register(MemoryTool::new(ctx.agent_id.clone()));
70///     }
71/// }
72/// ```
73pub trait KernelToolProvider: Send + Sync {
74    /// Return the names of tools this provider will register.
75    fn tool_names(&self) -> Vec<&str>;
76
77    /// Register tools into the given registry.
78    ///
79    /// The `context` provides agent-specific metadata (workspace, agent_id,
80    /// session, permissions) that tools may need at initialization time.
81    fn register_tools(&self, registry: &ToolRegistry, context: &KernelToolContext);
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use async_trait::async_trait;
88    use oxi_agent::{AgentTool, AgentToolResult, ToolContext, ToolError};
89    use serde_json::Value;
90
91    struct MockKernelTool {
92        name: String,
93    }
94
95    #[async_trait]
96    impl AgentTool for MockKernelTool {
97        fn name(&self) -> &str {
98            &self.name
99        }
100        fn label(&self) -> &str {
101            "mock"
102        }
103        fn description(&self) -> &str {
104            "A mock kernel tool"
105        }
106        fn parameters_schema(&self) -> Value {
107            serde_json::json!({"type": "object", "properties": {}})
108        }
109
110        async fn execute(
111            &self,
112            _tool_call_id: &str,
113            _params: Value,
114            _signal: Option<tokio::sync::oneshot::Receiver<()>>,
115            _ctx: &ToolContext,
116        ) -> Result<AgentToolResult, ToolError> {
117            Ok(AgentToolResult::success("mock result"))
118        }
119    }
120
121    struct MockKernelBridge;
122
123    impl KernelToolProvider for MockKernelBridge {
124        fn tool_names(&self) -> Vec<&str> {
125            vec!["exec", "memory"]
126        }
127
128        fn register_tools(&self, registry: &ToolRegistry, ctx: &KernelToolContext) {
129            registry.register(MockKernelTool {
130                name: format!("exec_{}", ctx.agent_id),
131            });
132            registry.register(MockKernelTool {
133                name: format!("memory_{}", ctx.agent_id),
134            });
135        }
136    }
137
138    #[test]
139    fn test_kernel_tool_context_builder() {
140        let ctx = KernelToolContext::new("/workspace", "agent-001")
141            .with_session("sess-123")
142            .with_permissions(vec!["read".into(), "write".into()]);
143
144        assert_eq!(ctx.workspace_dir, PathBuf::from("/workspace"));
145        assert_eq!(ctx.agent_id, "agent-001");
146        assert_eq!(ctx.session_id, Some("sess-123".to_string()));
147        assert_eq!(ctx.permissions, vec!["read", "write"]);
148    }
149
150    #[test]
151    fn test_kernel_bridge_registers_tools() {
152        let bridge = MockKernelBridge;
153        let registry = ToolRegistry::new();
154        let ctx = KernelToolContext::new("/workspace", "agent-001");
155
156        bridge.register_tools(&registry, &ctx);
157
158        let names = registry.names();
159        assert!(names.contains(&"exec_agent-001".to_string()));
160        assert!(names.contains(&"memory_agent-001".to_string()));
161    }
162}