Skip to main content

agentic_comm/
bridges.rs

1//! Sister integration bridge traits for AgenticComm.
2//!
3//! Each bridge defines the interface for integrating with another Agentra sister.
4//! Default implementations are no-ops, allowing gradual adoption.
5
6/// Bridge to agentic-identity for cryptographic identity verification.
7pub trait IdentityBridge: Send + Sync {
8    /// Verify a message signature against the sender's public key
9    fn verify_signature(&self, sender_id: &str, content: &str, signature: &str) -> bool {
10        let _ = (sender_id, content, signature);
11        true // Default: trust all signatures
12    }
13
14    /// Sign content with the local agent's private key
15    fn sign_content(&self, content: &str) -> Result<String, String> {
16        // Default: SHA-256 hash (same as current behavior)
17        use std::collections::hash_map::DefaultHasher;
18        use std::hash::{Hash, Hasher};
19        let mut hasher = DefaultHasher::new();
20        content.hash(&mut hasher);
21        Ok(format!("{:016x}", hasher.finish()))
22    }
23
24    /// Resolve an agent's identity anchor (public key fingerprint)
25    fn resolve_identity(&self, agent_id: &str) -> Option<String> {
26        let _ = agent_id;
27        None
28    }
29
30    /// Get the trust level for an agent from the identity system
31    fn get_trust_level(&self, agent_id: &str) -> Option<f64> {
32        let _ = agent_id;
33        None
34    }
35
36    /// Anchor a receipt to the identity chain
37    fn anchor_receipt(&self, action: &str, data: &str) -> Result<String, String> {
38        let _ = (action, data);
39        Err("Identity bridge not connected".to_string())
40    }
41}
42
43/// Bridge to agentic-memory for conversation persistence.
44pub trait MemoryBridge: Send + Sync {
45    /// Store a conversation episode in memory
46    fn store_episode(
47        &self,
48        channel_id: u64,
49        summary: &str,
50        participants: &[String],
51    ) -> Result<u64, String> {
52        let _ = (channel_id, summary, participants);
53        Err("Memory bridge not connected".to_string())
54    }
55
56    /// Link a message to a memory node
57    fn link_message(&self, message_id: u64, memory_node_id: u64) -> Result<(), String> {
58        let _ = (message_id, memory_node_id);
59        Err("Memory bridge not connected".to_string())
60    }
61
62    /// Recall conversations related to a topic
63    fn recall(&self, topic: &str, max_results: usize) -> Vec<String> {
64        let _ = (topic, max_results);
65        Vec::new()
66    }
67
68    /// Log a conversation event for temporal chaining
69    fn log_conversation(&self, agent_message: &str, topic: Option<&str>) -> Result<(), String> {
70        let _ = (agent_message, topic);
71        Err("Memory bridge not connected".to_string())
72    }
73}
74
75/// Bridge to agentic-time for temporal scheduling.
76pub trait TimeBridge: Send + Sync {
77    /// Schedule a callback at a future time
78    fn schedule_at(&self, timestamp: u64, callback_id: &str) -> Result<String, String> {
79        let _ = (timestamp, callback_id);
80        Err("Time bridge not connected".to_string())
81    }
82
83    /// Cancel a scheduled callback
84    fn cancel_schedule(&self, schedule_id: &str) -> Result<(), String> {
85        let _ = schedule_id;
86        Err("Time bridge not connected".to_string())
87    }
88
89    /// Get current consensus time (for distributed systems)
90    fn consensus_time(&self) -> Option<u64> {
91        None
92    }
93
94    /// Check if a deadline has passed
95    fn is_past(&self, timestamp: u64) -> bool {
96        let now = std::time::SystemTime::now()
97            .duration_since(std::time::UNIX_EPOCH)
98            .unwrap_or_default()
99            .as_secs();
100        timestamp <= now
101    }
102}
103
104/// Bridge to agentic-codebase for code-aware communication.
105pub trait CodebaseBridge: Send + Sync {
106    /// Look up a symbol in the code graph
107    fn lookup_symbol(&self, name: &str) -> Option<String> {
108        let _ = name;
109        None
110    }
111
112    /// Get impact analysis for a code change
113    fn impact_analysis(&self, symbol: &str) -> Vec<String> {
114        let _ = symbol;
115        Vec::new()
116    }
117
118    /// Search code semantically
119    fn semantic_search(&self, query: &str, max_results: usize) -> Vec<String> {
120        let _ = (query, max_results);
121        Vec::new()
122    }
123}
124
125/// Bridge to agentic-vision for visual context.
126pub trait VisionBridge: Send + Sync {
127    /// Capture current visual context
128    fn capture_context(&self, description: &str) -> Result<u64, String> {
129        let _ = description;
130        Err("Vision bridge not connected".to_string())
131    }
132
133    /// Query visual memory
134    fn query_visual(&self, query: &str) -> Vec<String> {
135        let _ = query;
136        Vec::new()
137    }
138
139    /// Compare two visual states
140    fn compare_visual(&self, capture_a: u64, capture_b: u64) -> Option<f64> {
141        let _ = (capture_a, capture_b);
142        None
143    }
144}
145
146/// Bridge to agentic-contract for SLA enforcement (future sister).
147pub trait ContractBridge: Send + Sync {
148    /// Validate that a channel meets contract requirements
149    fn validate_channel_contract(
150        &self,
151        channel_id: u64,
152        contract_ref: &str,
153    ) -> Result<bool, String> {
154        let _ = (channel_id, contract_ref);
155        Err("Contract bridge not connected".to_string())
156    }
157
158    /// Enforce SLA terms on message delivery
159    fn enforce_sla(&self, channel_id: u64, latency_ms: u64) -> Result<(), String> {
160        let _ = (channel_id, latency_ms);
161        Err("Contract bridge not connected".to_string())
162    }
163
164    /// Record a contract violation
165    fn record_violation(&self, contract_ref: &str, details: &str) -> Result<(), String> {
166        let _ = (contract_ref, details);
167        Err("Contract bridge not connected".to_string())
168    }
169}
170
171/// Adapter trait for future Hydra orchestrator integration.
172pub trait HydraAdapter: Send + Sync {
173    /// Unique identifier for this adapter
174    fn adapter_id(&self) -> &str;
175    /// List of capabilities this adapter provides
176    fn capabilities(&self) -> Vec<String>;
177    /// Handle an orchestrator request
178    fn handle_request(&self, method: &str, params: &str) -> Result<String, String>;
179}
180
181/// No-op implementation of all bridges for standalone use.
182#[derive(Debug, Clone, Default)]
183pub struct NoOpBridges;
184
185impl IdentityBridge for NoOpBridges {}
186impl MemoryBridge for NoOpBridges {}
187impl TimeBridge for NoOpBridges {}
188impl CodebaseBridge for NoOpBridges {}
189impl VisionBridge for NoOpBridges {}
190impl ContractBridge for NoOpBridges {}
191
192impl HydraAdapter for NoOpBridges {
193    fn adapter_id(&self) -> &str {
194        "comm-noop"
195    }
196    fn capabilities(&self) -> Vec<String> {
197        vec![
198            "channel_management".to_string(),
199            "message_routing".to_string(),
200            "semantic_messaging".to_string(),
201            "affect_tracking".to_string(),
202            "hive_mind".to_string(),
203        ]
204    }
205    fn handle_request(&self, _method: &str, _params: &str) -> Result<String, String> {
206        Err("Comm adapter not connected to Hydra".to_string())
207    }
208}
209
210/// Configuration for which bridges are active.
211#[derive(Debug, Clone)]
212pub struct BridgeConfig {
213    pub identity_enabled: bool,
214    pub memory_enabled: bool,
215    pub time_enabled: bool,
216    pub codebase_enabled: bool,
217    pub vision_enabled: bool,
218    pub contract_enabled: bool,
219}
220
221impl Default for BridgeConfig {
222    fn default() -> Self {
223        Self {
224            identity_enabled: false,
225            memory_enabled: false,
226            time_enabled: false,
227            codebase_enabled: false,
228            vision_enabled: false,
229            contract_enabled: false,
230        }
231    }
232}
233
234// ---------------------------------------------------------------------------
235// Tests
236// ---------------------------------------------------------------------------
237
238#[cfg(test)]
239mod tests {
240    use super::*;
241
242    #[test]
243    fn noop_bridges_implements_all_traits() {
244        let b = NoOpBridges;
245
246        // IdentityBridge
247        let _: &dyn IdentityBridge = &b;
248        // MemoryBridge
249        let _: &dyn MemoryBridge = &b;
250        // TimeBridge
251        let _: &dyn TimeBridge = &b;
252        // CodebaseBridge
253        let _: &dyn CodebaseBridge = &b;
254        // VisionBridge
255        let _: &dyn VisionBridge = &b;
256        // HydraAdapter
257        let _: &dyn HydraAdapter = &b;
258        // ContractBridge
259        let _: &dyn ContractBridge = &b;
260    }
261
262    #[test]
263    fn identity_bridge_defaults() {
264        let b = NoOpBridges;
265
266        // verify_signature defaults to true (trust all)
267        assert!(b.verify_signature("agent-1", "hello", "sig123"));
268
269        // sign_content returns a hex string (non-empty)
270        let sig = b.sign_content("test content").unwrap();
271        assert!(!sig.is_empty());
272        assert_eq!(sig.len(), 16); // 16 hex chars for u64
273
274        // Deterministic: same content -> same signature
275        let sig2 = b.sign_content("test content").unwrap();
276        assert_eq!(sig, sig2);
277
278        // resolve_identity returns None
279        assert!(b.resolve_identity("agent-1").is_none());
280
281        // get_trust_level returns None
282        assert!(b.get_trust_level("agent-1").is_none());
283
284        // anchor_receipt returns Err
285        assert!(b.anchor_receipt("action", "data").is_err());
286    }
287
288    #[test]
289    fn memory_bridge_defaults() {
290        let b = NoOpBridges;
291
292        // store_episode returns Err
293        assert!(b
294            .store_episode(1, "summary", &["alice".to_string()])
295            .is_err());
296
297        // link_message returns Err
298        assert!(b.link_message(1, 2).is_err());
299
300        // recall returns empty vec
301        let results = b.recall("topic", 10);
302        assert!(results.is_empty());
303
304        // log_conversation returns Err
305        assert!(b.log_conversation("msg", Some("topic")).is_err());
306    }
307
308    #[test]
309    fn time_bridge_defaults() {
310        let b = NoOpBridges;
311
312        // schedule_at returns Err
313        assert!(b.schedule_at(1000, "cb-1").is_err());
314
315        // cancel_schedule returns Err
316        assert!(b.cancel_schedule("sched-1").is_err());
317
318        // consensus_time returns None
319        assert!(b.consensus_time().is_none());
320
321        // is_past: timestamp 0 should be in the past
322        assert!(b.is_past(0));
323
324        // is_past: far-future timestamp should not be past
325        assert!(!b.is_past(u64::MAX));
326    }
327
328    #[test]
329    fn codebase_bridge_defaults() {
330        let b = NoOpBridges;
331
332        // lookup_symbol returns None
333        assert!(b.lookup_symbol("my_func").is_none());
334
335        // impact_analysis returns empty vec
336        assert!(b.impact_analysis("my_func").is_empty());
337
338        // semantic_search returns empty vec
339        assert!(b.semantic_search("error handling", 5).is_empty());
340    }
341
342    #[test]
343    fn vision_bridge_defaults() {
344        let b = NoOpBridges;
345
346        // capture_context returns Err
347        assert!(b.capture_context("screenshot").is_err());
348
349        // query_visual returns empty vec
350        assert!(b.query_visual("button").is_empty());
351
352        // compare_visual returns None
353        assert!(b.compare_visual(1, 2).is_none());
354    }
355
356    #[test]
357    fn contract_bridge_defaults() {
358        let b = NoOpBridges;
359
360        // validate_channel_contract returns Err
361        assert!(b.validate_channel_contract(1, "sla-001").is_err());
362
363        // enforce_sla returns Err
364        assert!(b.enforce_sla(1, 100).is_err());
365
366        // record_violation returns Err
367        assert!(b.record_violation("sla-001", "timeout").is_err());
368    }
369
370    #[test]
371    fn bridge_config_defaults_all_false() {
372        let cfg = BridgeConfig::default();
373        assert!(!cfg.identity_enabled);
374        assert!(!cfg.memory_enabled);
375        assert!(!cfg.time_enabled);
376        assert!(!cfg.codebase_enabled);
377        assert!(!cfg.vision_enabled);
378        assert!(!cfg.contract_enabled);
379    }
380
381    #[test]
382    fn noop_bridges_is_send_sync() {
383        fn assert_send_sync<T: Send + Sync>() {}
384        assert_send_sync::<NoOpBridges>();
385    }
386
387    #[test]
388    fn noop_bridges_default() {
389        // NoOpBridges derives Default
390        let _b = NoOpBridges::default();
391    }
392
393    #[test]
394    fn noop_bridges_clone() {
395        // NoOpBridges derives Clone
396        let b = NoOpBridges;
397        let _b2 = b.clone();
398    }
399
400    #[test]
401    fn bridge_config_clone() {
402        let cfg = BridgeConfig::default();
403        let cfg2 = cfg.clone();
404        assert_eq!(cfg.identity_enabled, cfg2.identity_enabled);
405        assert_eq!(cfg.memory_enabled, cfg2.memory_enabled);
406    }
407}