pub mod dispatcher; pub mod message; pub use dispatcher::OutboundDispatcher; pub use message::{ChatMessage, ContentBlock, InboundMessage, MediaItem, OutboundMessage}; use std::sync::Arc; use tokio::sync::{mpsc, Mutex}; // ============================================================================ // MessageBus - Async message queue for Channel <-> Agent communication // ============================================================================ pub struct MessageBus { inbound_tx: mpsc::Sender, outbound_tx: mpsc::Sender, inbound_rx: Mutex>, outbound_rx: Mutex>, } impl MessageBus { /// Create a new MessageBus with the given channel capacity pub fn new(capacity: usize) -> Arc { let (inbound_tx, inbound_rx) = mpsc::channel(capacity); let (outbound_tx, outbound_rx) = mpsc::channel(capacity); Arc::new(Self { inbound_tx, outbound_tx, inbound_rx: Mutex::new(inbound_rx), outbound_rx: Mutex::new(outbound_rx), }) } /// Publish an inbound message (Channel -> Bus) pub async fn publish_inbound(&self, msg: InboundMessage) -> Result<(), BusError> { #[cfg(debug_assertions)] tracing::debug!(channel = %msg.channel, sender = %msg.sender_id, chat = %msg.chat_id, content_len = %msg.content.len(), media_count = %msg.media.len(), "Bus: publishing inbound message"); self.inbound_tx .send(msg) .await .map_err(|_| BusError::Closed) } /// Consume an inbound message (Agent -> Bus) pub async fn consume_inbound(&self) -> InboundMessage { let msg = self.inbound_rx .lock() .await .recv() .await .expect("bus inbound closed"); #[cfg(debug_assertions)] tracing::debug!(channel = %msg.channel, sender = %msg.sender_id, chat = %msg.chat_id, "Bus: consuming inbound message"); msg } /// Publish an outbound message (Agent -> Bus) pub async fn publish_outbound(&self, msg: OutboundMessage) -> Result<(), BusError> { #[cfg(debug_assertions)] tracing::debug!(channel = %msg.channel, chat_id = %msg.chat_id, content_len = %msg.content.len(), "Bus: publishing outbound message"); self.outbound_tx .send(msg) .await .map_err(|_| BusError::Closed) } /// Consume an outbound message (Dispatcher -> Bus) pub async fn consume_outbound(&self) -> OutboundMessage { self.outbound_rx .lock() .await .recv() .await .expect("bus outbound closed") } } // ============================================================================ // BusError // ============================================================================ #[derive(Debug)] pub enum BusError { Closed, } impl std::fmt::Display for BusError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { BusError::Closed => write!(f, "Bus channel closed"), } } } impl std::error::Error for BusError {}