repl_core/
runtime_bridge.rs1use std::sync::{Arc, Mutex};
2use symbi_runtime::communication::policy_gate::CommunicationPolicyGate;
3use symbi_runtime::communication::{
4 CommunicationBus, CommunicationConfig, DefaultCommunicationBus,
5};
6use symbi_runtime::context::manager::{ContextManagerConfig, StandardContextManager};
7use symbi_runtime::integrations::policy_engine::engine::{
8 OpaPolicyEngine, PolicyDecision, PolicyEngine,
9};
10use symbi_runtime::lifecycle::{DefaultLifecycleController, LifecycleConfig, LifecycleController};
11use symbi_runtime::reasoning::agent_registry::AgentRegistry;
12use symbi_runtime::reasoning::inference::InferenceProvider;
13use symbi_runtime::types::agent::AgentConfig;
14use symbi_runtime::types::security::Capability;
15use symbi_runtime::types::AgentId;
16
17pub struct RuntimeBridge {
19 lifecycle_controller: Arc<Mutex<Option<Arc<DefaultLifecycleController>>>>,
20 context_manager: Arc<Mutex<Option<Arc<StandardContextManager>>>>,
21 policy_engine: Arc<Mutex<OpaPolicyEngine>>,
22 inference_provider: Arc<Mutex<Option<Arc<dyn InferenceProvider>>>>,
24 agent_registry: Arc<AgentRegistry>,
26 comm_bus: Arc<Mutex<Option<Arc<dyn CommunicationBus + Send + Sync>>>>,
28 comm_policy: Arc<Mutex<Arc<CommunicationPolicyGate>>>,
30}
31
32impl Default for RuntimeBridge {
33 fn default() -> Self {
34 Self::new()
35 }
36}
37
38impl RuntimeBridge {
39 pub fn new() -> Self {
48 Self::with_policy(Arc::new(CommunicationPolicyGate::new(Vec::new())))
49 }
50
51 pub fn new_permissive_for_dev() -> Self {
57 Self::with_policy(Arc::new(CommunicationPolicyGate::permissive()))
58 }
59
60 pub fn with_policy(comm_policy_gate: Arc<CommunicationPolicyGate>) -> Self {
62 let lifecycle_controller = Arc::new(Mutex::new(None));
63 let context_manager = Arc::new(Mutex::new(None));
64 let policy_engine = Arc::new(Mutex::new(OpaPolicyEngine::new()));
65 let inference_provider = Arc::new(Mutex::new(None));
66 let agent_registry = Arc::new(AgentRegistry::new());
67 let comm_bus = Arc::new(Mutex::new(None));
68 let comm_policy = Arc::new(Mutex::new(comm_policy_gate));
69
70 Self {
71 lifecycle_controller,
72 context_manager,
73 policy_engine,
74 inference_provider,
75 agent_registry,
76 comm_bus,
77 comm_policy,
78 }
79 }
80
81 pub fn set_inference_provider(&self, provider: Arc<dyn InferenceProvider>) {
83 *self.inference_provider.lock().unwrap() = Some(provider);
84 }
85
86 pub fn agent_registry(&self) -> Arc<AgentRegistry> {
88 Arc::clone(&self.agent_registry)
89 }
90
91 pub fn comm_bus(&self) -> Option<Arc<dyn CommunicationBus + Send + Sync>> {
93 self.comm_bus.lock().unwrap().clone()
94 }
95
96 pub fn set_comm_policy(&self, policy: Arc<CommunicationPolicyGate>) {
98 *self.comm_policy.lock().unwrap() = policy;
99 }
100
101 pub fn reasoning_context(&self) -> crate::dsl::reasoning_builtins::ReasoningBuiltinContext {
107 let provider = self.inference_provider.lock().unwrap().clone();
108 let comm_bus = self.comm_bus.lock().unwrap().clone();
109 let comm_policy = Some(self.comm_policy.lock().unwrap().clone());
110 crate::dsl::reasoning_builtins::ReasoningBuiltinContext {
111 provider,
112 agent_registry: Some(Arc::clone(&self.agent_registry)),
113 sender_agent_id: None,
114 comm_bus,
115 comm_policy,
116 reasoning_policy_gate: None,
121 }
122 }
123
124 pub async fn initialize(&self) -> Result<(), String> {
131 let lifecycle_config = LifecycleConfig::default();
133 let lifecycle_controller = Arc::new(
134 DefaultLifecycleController::new(lifecycle_config)
135 .await
136 .map_err(|e| format!("Failed to create lifecycle controller: {}", e))?,
137 );
138
139 let context_config = ContextManagerConfig::default();
141 let context_manager = Arc::new(
142 StandardContextManager::new(context_config, "runtime_bridge")
143 .await
144 .map_err(|e| format!("Failed to create context manager: {}", e))?,
145 );
146
147 context_manager
149 .initialize()
150 .await
151 .map_err(|e| format!("Failed to initialize context manager: {}", e))?;
152
153 let bus_config = CommunicationConfig::default();
155 let bus = Arc::new(
156 DefaultCommunicationBus::new(bus_config)
157 .await
158 .map_err(|e| format!("Failed to create communication bus: {}", e))?,
159 ) as Arc<dyn CommunicationBus + Send + Sync>;
160
161 *self.lifecycle_controller.lock().unwrap() = Some(lifecycle_controller);
163 *self.context_manager.lock().unwrap() = Some(context_manager);
164 *self.comm_bus.lock().unwrap() = Some(bus);
165
166 Ok(())
167 }
168
169 pub async fn initialize_agent(&self, config: AgentConfig) -> Result<AgentId, String> {
170 let controller = {
171 let controller_guard = self.lifecycle_controller.lock().unwrap();
172 controller_guard.clone()
173 };
174
175 if let Some(controller) = controller {
176 controller
177 .initialize_agent(config)
178 .await
179 .map_err(|e| e.to_string())
180 } else {
181 Err("Lifecycle controller not initialized".to_string())
182 }
183 }
184
185 pub async fn check_capability(
187 &self,
188 agent_id: &str,
189 capability: &Capability,
190 ) -> Result<PolicyDecision, String> {
191 let engine = {
193 let engine_guard = self.policy_engine.lock().unwrap();
194 engine_guard.clone()
195 };
196 engine
197 .check_capability(agent_id, capability)
198 .await
199 .map_err(|e| e.to_string())
200 }
201
202 pub async fn register_event_handler(
204 &self,
205 agent_id: &str,
206 event_name: &str,
207 _event_type: &str,
208 ) -> Result<(), String> {
209 tracing::info!(
210 "Registered event handler '{}' for agent {}",
211 event_name,
212 agent_id
213 );
214 Ok(())
215 }
216
217 pub async fn emit_event(
219 &self,
220 agent_id: &str,
221 event_name: &str,
222 _data: &serde_json::Value,
223 ) -> Result<(), String> {
224 tracing::info!("Agent {} emitted event: {}", agent_id, event_name);
225 Ok(())
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232
233 #[tokio::test]
234 async fn test_reasoning_context_before_init_has_no_bus() {
235 let bridge = RuntimeBridge::new();
236 let ctx = bridge.reasoning_context();
237 assert!(ctx.comm_bus.is_none());
240 assert!(ctx.comm_policy.is_some());
241 }
242
243 #[tokio::test]
244 async fn test_new_default_policy_denies() {
245 use symbi_runtime::types::MessageType;
246 let bridge = RuntimeBridge::new();
247 let ctx = bridge.reasoning_context();
248 let policy = ctx.comm_policy.expect("policy present");
249 let recipient = AgentId::new();
250 let request = symbi_runtime::communication::policy_gate::CommunicationRequest {
251 sender: AgentId::new(),
252 recipient,
253 message_type: MessageType::Direct(recipient),
254 topic: None,
255 };
256 assert!(
257 policy.evaluate(&request).is_err(),
258 "default policy must be deny-by-default"
259 );
260 }
261
262 #[tokio::test]
263 async fn test_permissive_for_dev_allows() {
264 use symbi_runtime::types::MessageType;
265 let bridge = RuntimeBridge::new_permissive_for_dev();
266 let ctx = bridge.reasoning_context();
267 let policy = ctx.comm_policy.expect("policy present");
268 let recipient = AgentId::new();
269 let request = symbi_runtime::communication::policy_gate::CommunicationRequest {
270 sender: AgentId::new(),
271 recipient,
272 message_type: MessageType::Direct(recipient),
273 topic: None,
274 };
275 assert!(policy.evaluate(&request).is_ok());
276 }
277
278 #[tokio::test]
279 async fn test_reasoning_context_after_init_has_bus() {
280 let bridge = RuntimeBridge::new_permissive_for_dev();
281 bridge
282 .initialize()
283 .await
284 .expect("initialize should succeed");
285 let ctx = bridge.reasoning_context();
286 assert!(
287 ctx.comm_bus.is_some(),
288 "Communication bus should be populated after initialize()"
289 );
290 assert!(ctx.comm_policy.is_some(), "Policy gate is always present");
291 }
292
293 #[tokio::test]
294 async fn test_comm_bus_accessor() {
295 let bridge = RuntimeBridge::new_permissive_for_dev();
296 assert!(bridge.comm_bus().is_none());
297 bridge
298 .initialize()
299 .await
300 .expect("initialize should succeed");
301 assert!(bridge.comm_bus().is_some());
302 }
303
304 #[tokio::test]
305 async fn test_set_comm_policy_replaces_default() {
306 let bridge = RuntimeBridge::new();
307 let new_policy = Arc::new(CommunicationPolicyGate::permissive());
308 bridge.set_comm_policy(Arc::clone(&new_policy));
309 let ctx = bridge.reasoning_context();
310 let retrieved = ctx.comm_policy.expect("policy should be set");
311 assert!(Arc::ptr_eq(&retrieved, &new_policy));
312 }
313}