bob_core/channel.rs
1//! # Channel Abstraction
2//!
3//! Multi-endpoint channel interface for the Bob Agent Framework.
4//!
5//! A **channel** is the boundary between the outside world (user) and the
6//! agent loop. Its only responsibility is receiving user input and delivering
7//! agent output — no agent logic lives here.
8//!
9//! ## Architecture
10//!
11//! ```text
12//! ┌─────────┐ ┌────────────┐ ┌───────────┐
13//! │ User │ ───→ │ Channel │ ───→ │ AgentLoop │
14//! │ │ ←─── │ (trait) │ ←─── │ │
15//! └─────────┘ └────────────┘ └───────────┘
16//! ```
17//!
18//! Concrete implementations live in adapter or binary crates:
19//!
20//! | Channel | Scenario |
21//! |-------------|---------------------------------------|
22//! | CLI | One-shot `run --prompt "..."` |
23//! | REPL | Interactive terminal |
24//! | Telegram Bot | Long-polling with ACL |
25//! | Discord Bot | Gateway-based with channel filtering |
26
27use serde::{Deserialize, Serialize};
28
29/// Incoming message from a channel to the agent.
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct ChannelMessage {
32 /// Raw user text (may be a slash command or natural language).
33 pub text: String,
34 /// Session identifier for conversation continuity.
35 pub session_id: String,
36 /// Optional sender identifier (username, user-id, etc.).
37 pub sender: Option<String>,
38}
39
40/// Outgoing agent response delivered back through the channel.
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct ChannelOutput {
43 /// Response text.
44 pub text: String,
45 /// Whether this output represents an error condition.
46 pub is_error: bool,
47}
48
49/// Errors that can occur during channel I/O.
50#[derive(Debug, thiserror::Error)]
51pub enum ChannelError {
52 /// Sending a message to the user failed.
53 #[error("send failed: {0}")]
54 SendFailed(String),
55 /// The requested feature is not supported by this channel.
56 #[error("not supported: {0}")]
57 NotSupported(String),
58 /// The channel has been closed (user disconnected, etc.).
59 #[error("channel closed")]
60 Closed,
61}
62
63/// Abstract channel for multi-endpoint agent interaction.
64///
65/// Each channel implementation handles the transport-specific details
66/// of receiving user input and delivering agent output. The agent loop
67/// is decoupled from any specific transport.
68///
69/// ## Implementing a Channel
70///
71/// ```rust,ignore
72/// use bob_core::channel::{Channel, ChannelMessage, ChannelOutput, ChannelError};
73///
74/// struct MyTelegramChannel { /* ... */ }
75///
76/// #[async_trait::async_trait]
77/// impl Channel for MyTelegramChannel {
78/// async fn recv(&mut self) -> Option<ChannelMessage> {
79/// // Poll Telegram API for new messages...
80/// }
81///
82/// async fn send(&self, output: ChannelOutput) -> Result<(), ChannelError> {
83/// // Send via Telegram Bot API...
84/// Ok(())
85/// }
86/// }
87/// ```
88#[async_trait::async_trait]
89pub trait Channel: Send {
90 /// Receive the next user input message.
91 ///
92 /// Returns `None` when the channel is closed (e.g. user disconnected,
93 /// stdin EOF, or graceful shutdown).
94 async fn recv(&mut self) -> Option<ChannelMessage>;
95
96 /// Send an agent response back to the user.
97 async fn send(&self, output: ChannelOutput) -> Result<(), ChannelError>;
98}