1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//! MessageChannel: a message transport channel between an Agent and the outside world (a human or another Agent).
//!
//! The channel supports only two operations, defined by the [`MessageChannel`] trait:
//!
//! - [`MessageChannel::ask`] — send a question and wait for a reply (request-response), for scenarios that need the
//! counterparty's confirmation (e.g. asking the user for consent before a tool runs);
//! - [`MessageChannel::notify`] — a one-way notification, sent without waiting for a reply.
//!
//! Interrupt semantics: an interrupt happens when a channel consumer (such as a tool) calls `ask`, and the call
//! returning is the resume. The channel itself is unaware of interrupts and takes no part in the resume flow.
//!
//! # Choosing a channel: which one when
//!
//! Pick a channel along two dimensions: "does it need a reply" and "how many receivers are there".
//!
//! | Need | Choice |
//! |------|------|
//! | Interacting with a human: terminal questions, confirmation, approval | [`CliMessageChannel`] |
//! | One-on-one dialogue between two Agents, with request-response in both directions | [`MpscChannel`] |
//! | One-to-many broadcast notifications, no reply expected | [`BroadcastChannel`] |
//! | Observing changes of the latest state (status, heartbeat, progress) | [`WatchChannel`] |
//!
//! Selection notes:
//!
//! - **Need request-response** → [`CliMessageChannel`] (human)
//! or [`MpscChannel`] (Agent); the other two don't support `ask`
//! and return [`ChannelError::NotSupported`].
//! - **One-way notifications only** → [`BroadcastChannel`] or
//! [`WatchChannel`]; neither waits for the counterparty's confirmation.
//! The difference is semantic: broadcast is a message queue (bounded, drops old
//! messages when consumers are slow); watch holds the latest value (unbounded, keeps only the newest).
//! - **Concurrent two-way questioning** → replies in `MpscChannel` are bound to their requests one-to-one, so both
//! sides can ask each other concurrently without replies going astray; `CliMessageChannel` suits slow-paced
//! "human confirms one at a time" interactions — while one request is unanswered, later requests queue up.
//! - **Concurrent access** → all implementations are `Send + Sync`, so they can be placed in an [`Arc`](std::sync::Arc)
//! and shared across tasks; `ask` presents only one request at a time.
//!
//! Event observation (UI / environment subscription) does not go through this module, but through the
//! publish-subscribe of [`EventChannel`](crate::event_channel::EventChannel): conversation channels handle
//! "request-response / notifications", while event channels handle "observing the reasoning process".
/// Sends a question to the outside world and waits for a reply, or sends a one-way notification.
///
/// The receiver is not limited to humans: the same trait supports both
/// Agent-to-Agent dialogue (one-to-one `ask`) and broadcast notifications
/// (`notify`). **Serializing concurrent calls is the implementation's job**:
/// `ask` presents only one request at a time, and the next one gets its turn
/// only after the previous reply — e.g. a human can only approve one by one.
///
/// # Example
///
/// Implementing a custom channel: just answer "how to send a question" and "how to send a notification".
///
/// ```rust
/// use molo::{ChannelError, MessageChannel};
///
/// // A custom channel that echoes the question back as the reply and drops notifications.
/// struct EchoChannel;
///
/// #[async_trait::async_trait]
/// impl MessageChannel for EchoChannel {
/// async fn ask(&self, message: &str) -> Result<String, ChannelError> {
/// Ok(format!("echo: {message}"))
/// }
///
/// async fn notify(&self, message: &str) -> Result<(), ChannelError> {
/// Ok(())
/// }
/// }
///
/// # #[tokio::main]
/// # async fn main() -> Result<(), molo::ChannelError> {
/// let channel = EchoChannel;
/// assert_eq!(channel.ask("hello").await?, "echo: hello");
/// # Ok(())
/// # }
/// ```
/// The reason a message channel failed.
///
/// When each variant is triggered:
///
/// - [`ChannelError::Io`] — underlying read/write failure (e.g. a terminal IO error);
/// - [`ChannelError::Closed`] — the channel is closed: the peer has been dropped, or input ended
/// (e.g. Ctrl-D in a terminal); after it closes, every subsequent call on the channel returns the same error;
/// - [`ChannelError::NoReply`] — [`IncomingMessage::reply`] was called on a notification message
/// that does not expect a reply;
/// - [`ChannelError::NotSupported`] — the current implementation does not support this operation (broadcast / watch
/// don't support `ask`).
///
/// The enum is `#[non_exhaustive]` (reserved for extension): matches must include a wildcard arm.
/// A message in the queue: questions carry a reply channel, notifications don't.
/// A message received from the channel: the text content, plus a reply slot that only questions have.
///
/// Returned by each channel's receive method. Check [`IncomingMessage::wants_reply`] first —
/// for question messages, send the reply back with [`IncomingMessage::reply`]; notification messages have no reply slot.
///
/// See [`MpscChannel`] for a complete usage example.
pub use ;
pub use CliMessageChannel;
pub use MpscChannel;
pub use ;