pub mod dispatcher; pub mod message; pub use dispatcher::OutboundDispatcher; pub use message::{ ChannelContext, ChatMessage, CompletionStatus, ContentBlock, ControlMessage, InboundMessage, MediaItem, MediaRef, MessageSource, OutboundMessage, ProviderReasoningState, SourceKind, }; use std::sync::Arc; use tokio::sync::{Mutex, mpsc}; // ============================================================================ // 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>, // Control channel for session management operations control_tx: mpsc::Sender, control_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); let (control_tx, control_rx) = mpsc::channel(capacity); Arc::new(Self { inbound_tx, outbound_tx, inbound_rx: Mutex::new(inbound_rx), outbound_rx: Mutex::new(outbound_rx), control_tx, control_rx: Mutex::new(control_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) -> Option { let msg = self.inbound_rx.lock().await.recv().await?; #[cfg(debug_assertions)] tracing::debug!(channel = %msg.channel, sender = %msg.sender_id, chat = %msg.chat_id, "Bus: consuming inbound message"); Some(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) } /// Publish an outbound message and wait for the dispatcher to report the /// actual channel delivery result. pub async fn deliver_outbound(&self, mut msg: OutboundMessage) -> Result<(), BusError> { let (delivery_tx, mut delivery_rx) = tokio::sync::watch::channel(None); msg.delivery = Some(delivery_tx); self.publish_outbound(msg).await?; tokio::time::timeout(std::time::Duration::from_secs(120), async { loop { delivery_rx.changed().await.map_err(|_| BusError::Closed)?; if let Some(result) = delivery_rx.borrow().clone() { return result.map_err(BusError::DeliveryFailed); } } }) .await .map_err(|_| BusError::DeliveryTimedOut)? } /// Consume an outbound message (Dispatcher -> Bus) pub async fn consume_outbound(&self) -> Option { self.outbound_rx.lock().await.recv().await } /// Publish a control message (Channel -> Bus for session management) pub async fn publish_control(&self, msg: ControlMessage) -> Result<(), BusError> { tracing::debug!(op = ?msg.op, "Bus: publishing control message"); self.control_tx .send(msg) .await .map_err(|_| BusError::Closed) } /// Consume a control message (ControlProcessor -> Bus) pub async fn consume_control(&self) -> Option { self.control_rx.lock().await.recv().await } } // ============================================================================ // BusError // ============================================================================ #[derive(Debug)] pub enum BusError { Closed, DeliveryFailed(String), DeliveryTimedOut, } 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"), BusError::DeliveryFailed(error) => write!(f, "Outbound delivery failed: {error}"), BusError::DeliveryTimedOut => write!(f, "Outbound delivery confirmation timed out"), } } } impl std::error::Error for BusError {}