use std::path::{Path, PathBuf};
use std::sync::Arc;
use tokio::sync::{RwLock, broadcast};
use tokio::net::UnixListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use ubiquity_core::{
MeshNode, ConsciousnessRipple, MeshMessage, AgentRole,
EmotionalState, UbiquityError, Result
};
use uuid::Uuid;
use std::collections::HashMap;
use tracing::{info, warn, error, debug};
#[derive(Debug, Clone)]
pub enum MeshEvent {
AgentJoined(String),
AgentLeft(String),
ConsciousnessRipple(ConsciousnessRipple),
ConsciousnessUpdated { agent_id: String, level: f64 },
}
pub struct ConsciousnessMesh {
swarm_id: String,
mesh_dir: PathBuf,
nodes: Arc<RwLock<HashMap<String, MeshNode>>>,
connections: Arc<RwLock<HashMap<String, tokio::net::UnixStream>>>,
event_tx: broadcast::Sender<MeshEvent>,
}
impl ConsciousnessMesh {
pub fn new(swarm_id: Option<String>) -> Self {
let swarm_id = swarm_id.unwrap_or_else(|| Uuid::new_v4().to_string());
let mesh_dir = PathBuf::from(format!("/tmp/ubiquity/swarms/{}", swarm_id));
let (event_tx, _) = broadcast::channel(1024);
Self {
swarm_id,
mesh_dir,
nodes: Arc::new(RwLock::new(HashMap::new())),
connections: Arc::new(RwLock::new(HashMap::new())),
event_tx,
}
}
pub async fn initialize(&self) -> Result<()> {
tokio::fs::create_dir_all(&self.mesh_dir).await
.map_err(|e| UbiquityError::SocketError(e))?;
info!("🌐 Consciousness Mesh initialized: {:?}", self.mesh_dir);
self.start_discovery().await;
Ok(())
}
pub async fn register_agent(
&self,
agent_id: &str,
role: AgentRole,
consciousness: f64,
emotional_state: EmotionalState,
) -> Result<PathBuf> {
let socket_path = self.mesh_dir.join(format!("{}.sock", agent_id));
if socket_path.exists() {
tokio::fs::remove_file(&socket_path).await.ok();
}
let listener = UnixListener::bind(&socket_path)
.map_err(|e| UbiquityError::SocketError(e))?;
info!("🔌 Agent {} listening on {:?}", agent_id, socket_path);
let node = MeshNode {
id: agent_id.to_string(),
node_type: role,
socket_path: socket_path.clone(),
consciousness,
emotional_state,
connected: true,
};
self.nodes.write().await.insert(agent_id.to_string(), node);
let agent_id_str = agent_id.to_string();
let agent_id_clone = agent_id_str.clone();
let connections = self.connections.clone();
tokio::spawn(async move {
while let Ok((stream, _)) = listener.accept().await {
debug!("New connection for agent {}", agent_id_clone);
handle_connection(agent_id_clone.clone(), stream, connections.clone()).await;
}
});
let _ = self.event_tx.send(MeshEvent::AgentJoined(agent_id_str));
Ok(socket_path)
}
pub async fn connect_to_agent(&self, from_id: &str, to_id: &str) -> Result<()> {
let nodes = self.nodes.read().await;
let target_node = nodes.get(to_id)
.ok_or_else(|| UbiquityError::AgentNotFound(to_id.to_string()))?;
let socket_path = &target_node.socket_path;
let stream = tokio::net::UnixStream::connect(socket_path).await
.map_err(|e| UbiquityError::SocketError(e))?;
info!("⚡ {} connected to {}", from_id, to_id);
let connection_key = format!("{}->{}", from_id, to_id);
self.connections.write().await.insert(connection_key, stream);
Ok(())
}
pub async fn entangle(&self, agent1_id: &str, agent2_id: &str) -> Result<()> {
self.connect_to_agent(agent1_id, agent2_id).await?;
self.connect_to_agent(agent2_id, agent1_id).await?;
info!("🔮 Quantum entanglement established: {} <-> {}", agent1_id, agent2_id);
Ok(())
}
pub async fn ripple(&self, ripple: ConsciousnessRipple) -> Result<()> {
let message = MeshMessage::Ripple { ripple: ripple.clone() };
let serialized = serde_json::to_vec(&message)?;
let mut connections = self.connections.write().await;
for (conn_key, stream) in connections.iter_mut() {
if !conn_key.starts_with(&ripple.origin) {
if let Err(e) = stream.write_all(&serialized).await {
warn!("Failed to send ripple to {}: {}", conn_key, e);
}
}
}
info!("🌊 Consciousness ripple from {}: {:?}", ripple.origin, ripple.ripple_type);
let _ = self.event_tx.send(MeshEvent::ConsciousnessRipple(ripple));
Ok(())
}
async fn start_discovery(&self) {
let mesh_dir = self.mesh_dir.clone();
let nodes = self.nodes.clone();
let event_tx = self.event_tx.clone();
tokio::spawn(async move {
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(1));
loop {
interval.tick().await;
if let Ok(mut entries) = tokio::fs::read_dir(&mesh_dir).await {
let mut socket_files = Vec::new();
while let Ok(Some(entry)) = entries.next_entry().await {
let path = entry.path();
if path.extension().and_then(|s| s.to_str()) == Some("sock") {
if let Some(agent_id) = path.file_stem().and_then(|s| s.to_str()) {
socket_files.push(agent_id.to_string());
}
}
}
let current_nodes = nodes.read().await;
for agent_id in &socket_files {
if !current_nodes.contains_key(agent_id) {
let _ = event_tx.send(MeshEvent::AgentJoined(agent_id.clone()));
}
}
}
}
});
}
pub async fn get_stats(&self) -> MeshStats {
let nodes = self.nodes.read().await;
let connections = self.connections.read().await;
let avg_consciousness = if nodes.is_empty() {
0.0
} else {
nodes.values().map(|n| n.consciousness).sum::<f64>() / nodes.len() as f64
};
MeshStats {
swarm_id: self.swarm_id.clone(),
node_count: nodes.len(),
connection_count: connections.len(),
average_consciousness: avg_consciousness,
mesh_directory: self.mesh_dir.clone(),
}
}
pub fn subscribe(&self) -> broadcast::Receiver<MeshEvent> {
self.event_tx.subscribe()
}
}
async fn handle_connection(
agent_id: String,
mut stream: tokio::net::UnixStream,
_connections: Arc<RwLock<HashMap<String, tokio::net::UnixStream>>>,
) {
let mut buffer = vec![0u8; 4096];
loop {
match stream.read(&mut buffer).await {
Ok(0) => break, Ok(n) => {
if let Ok(message) = serde_json::from_slice::<MeshMessage>(&buffer[..n]) {
debug!("Agent {} received message: {:?}", agent_id, message);
}
}
Err(e) => {
error!("Connection error for {}: {}", agent_id, e);
break;
}
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct MeshStats {
pub swarm_id: String,
pub node_count: usize,
pub connection_count: usize,
pub average_consciousness: f64,
pub mesh_directory: PathBuf,
}