pub trait Channel: Send {
Show 24 methods
// Required methods
fn recv(
&mut self,
) -> impl Future<Output = Result<Option<ChannelMessage>, ChannelError>> + Send;
fn send(
&mut self,
text: &str,
) -> impl Future<Output = Result<(), ChannelError>> + Send;
fn send_chunk(
&mut self,
chunk: &str,
) -> impl Future<Output = Result<(), ChannelError>> + Send;
fn flush_chunks(
&mut self,
) -> impl Future<Output = Result<(), ChannelError>> + Send;
// Provided methods
fn try_recv(&mut self) -> Option<ChannelMessage> { ... }
fn supports_exit(&self) -> bool { ... }
fn requires_input_sanitization(&self) -> bool { ... }
fn send_typing(
&mut self,
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
fn send_status(
&mut self,
_text: &str,
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
fn send_transcript_backfill(
&mut self,
entries: &[TranscriptEntry],
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
fn send_resume_banner(
&mut self,
text: &str,
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
fn send_status_best_effort(
&mut self,
text: &str,
) -> impl Future<Output = ()> + Send { ... }
fn send_thinking_chunk(
&mut self,
_chunk: &str,
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
fn send_queue_count(
&mut self,
_count: usize,
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
fn send_context_estimate(
&mut self,
_tokens: usize,
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
fn send_usage(
&mut self,
_input_tokens: u64,
_output_tokens: u64,
_context_window: u64,
_cache_read_tokens: u64,
_cache_write_tokens: u64,
_cost_cents: f64,
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
fn send_diff(
&mut self,
_diff: DiffData,
_tool_call_id: &str,
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
fn send_tool_start(
&mut self,
_event: ToolStartEvent,
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
fn send_tool_output(
&mut self,
event: ToolOutputEvent,
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
fn confirm(
&mut self,
_prompt: &str,
) -> impl Future<Output = Result<bool, ChannelError>> + Send { ... }
fn elicit(
&mut self,
_request: ElicitationRequest,
) -> impl Future<Output = Result<ElicitationResponse, ChannelError>> + Send { ... }
fn send_stop_hint(
&mut self,
_hint: StopHint,
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
fn notify_foreground_subagent_started(
&mut self,
_id: &str,
_name: &str,
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
fn notify_foreground_subagent_completed(
&mut self,
_id: &str,
_name: &str,
_success: bool,
) -> impl Future<Output = Result<(), ChannelError>> + Send { ... }
}Expand description
Bidirectional communication channel for the agent.
§TODO (A3 — deferred: split monolithic Channel into focused sub-traits)
Channel currently has 16+ methods with 12 default no-op bodies. This makes it easy to
accidentally ignore capabilities (e.g., streaming, elicitation) on a new channel
implementation without a compile error. The planned split:
MessageChannel—send/recv(required for all channels)StreamingChannel—send_streaming_chunk/finish_stream(opt-in)ElicitationChannel—request_elicitation(opt-in)StatusChannel—set_status/clear_status(opt-in)
Blocked by: workspace-wide breaking change affecting CLI, Telegram, TUI, gateway, JSON, Discord, Slack, loopback channels, and all integration tests. Must be migrated channel by channel across ≥5 PRs. Requires its own SDD spec. See critic review §S4.
Required Methods§
Sourcefn recv(
&mut self,
) -> impl Future<Output = Result<Option<ChannelMessage>, ChannelError>> + Send
fn recv( &mut self, ) -> impl Future<Output = Result<Option<ChannelMessage>, ChannelError>> + Send
Receive the next message. Returns None on EOF or shutdown.
§Errors
Returns an error if the underlying I/O fails.
Sourcefn send_chunk(
&mut self,
chunk: &str,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_chunk( &mut self, chunk: &str, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Sourcefn flush_chunks(
&mut self,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn flush_chunks( &mut self, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Provided Methods§
Sourcefn try_recv(&mut self) -> Option<ChannelMessage>
fn try_recv(&mut self) -> Option<ChannelMessage>
Non-blocking receive. Returns None if no message is immediately available.
Sourcefn supports_exit(&self) -> bool
fn supports_exit(&self) -> bool
Whether /exit and /quit commands should terminate the agent loop.
Returns false for persistent server-side channels (e.g. Telegram) where
breaking the loop would not meaningfully exit from the user’s perspective.
Sourcefn requires_input_sanitization(&self) -> bool
fn requires_input_sanitization(&self) -> bool
Whether messages from this channel are raw external input that must be sanitized
(ContentTrustLevel::ExternalUntrusted) before the residual, non-command text reaches
the LLM context.
Returns true for direct bot-adapter channels (Telegram, Discord, Slack) whose input
comes from arbitrary remote users. Returns false (default) for local/operator-trusted
channels (CLI, TUI) and for LoopbackChannel — gateway webhooks and A2A messages are
already sanitized by their respective forwarders before being injected as a
ChannelMessage, so sanitizing again here would double-wrap them.
Sanitization is applied downstream of all command dispatch (Agent::run’s registries and
dispatch_slash_command), not at recv/try_recv, so recognized commands still dispatch
on raw text — only the text that actually reaches the LLM is wrapped.
Also reused as an “is this channel display-owning” proxy by the resume-banner call sites
in Agent::load_history and Agent::load_and_resume_conversation (spec-068 §13.2,
#6420): false gates the banner in; true excludes chat channels from it. JsonCli
does not override this (correctly local/operator-trusted for sanitization purposes), so
it instead overrides Channel::send_resume_banner directly to stay excluded from the
banner without being excluded from sanitization semantics — the two concerns are related
but not identical; check both when adding a new banner-adjacent call site.
Sourcefn send_typing(
&mut self,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_typing( &mut self, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Sourcefn send_status(
&mut self,
_text: &str,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_status( &mut self, _text: &str, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Send a status label (shown as spinner text in TUI). No-op by default.
§Errors
Returns an error if the underlying I/O fails.
Sourcefn send_transcript_backfill(
&mut self,
entries: &[TranscriptEntry],
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_transcript_backfill( &mut self, entries: &[TranscriptEntry], ) -> impl Future<Output = Result<(), ChannelError>> + Send
Send a bounded transcript slice for /history backfill (spec-068 §13.6-§13.7).
Default: renders entries into one flat string via
zeph_commands::TranscriptFormatter::render_flat and forwards through
Channel::send — correct for every channel with no structured display buffer of
its own (CLI, Telegram, Discord, Slack). TuiChannel overrides this to backfill
per-entry into its own display buffer instead of flattening, keeping the backfill
path split from input_history/up-arrow recall (INV-SP-6, §13.7, AC-20).
§Errors
Returns an error if the underlying I/O fails.
Send a resume banner (spec-068 §13.5) from a live mid-session conversation swap
(/conv resume, /conv fork — see Agent::load_and_resume_conversation), not just the
process-startup path.
Default: forwards through Channel::send like any other message — correct for CLI
(prints the line) and any channel with no persistent-banner concept. TuiChannel
overrides this to emit AgentEvent::ResumeBanner into its persistent header instead of
a scrolling chat line.
§Errors
Returns an error if the underlying I/O fails.
Sourcefn send_status_best_effort(
&mut self,
text: &str,
) -> impl Future<Output = ()> + Send
fn send_status_best_effort( &mut self, text: &str, ) -> impl Future<Output = ()> + Send
Best-effort variant of send_status for the many call sites
where a status update is a UX nicety, not a value the turn depends on.
Bounds the send to STATUS_SEND_TIMEOUT and logs the outcome (tracing::debug! on
success, tracing::warn! on error or timeout) instead of returning a Result. Callers
that used to write let _ = channel.send_status(...).await; should call this instead:
failures become visible in logs, and a slow/rate-limited channel (see #6094) can no
longer stall the agent turn loop.
Sourcefn send_thinking_chunk(
&mut self,
_chunk: &str,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_thinking_chunk( &mut self, _chunk: &str, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Send a thinking/reasoning token chunk. No-op by default.
§Errors
Returns an error if the underlying I/O fails.
Sourcefn send_queue_count(
&mut self,
_count: usize,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_queue_count( &mut self, _count: usize, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Notify channel of queued message count. No-op by default.
§Errors
Returns an error if the underlying I/O fails.
Sourcefn send_context_estimate(
&mut self,
_tokens: usize,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_context_estimate( &mut self, _tokens: usize, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Send the projected context token count to the channel after context assembly.
The value is an approximation; non-TUI channels may ignore it. No-op by default.
§Errors
Returns an error if the underlying I/O fails.
Sourcefn send_usage(
&mut self,
_input_tokens: u64,
_output_tokens: u64,
_context_window: u64,
_cache_read_tokens: u64,
_cache_write_tokens: u64,
_cost_cents: f64,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_usage( &mut self, _input_tokens: u64, _output_tokens: u64, _context_window: u64, _cache_read_tokens: u64, _cache_write_tokens: u64, _cost_cents: f64, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Send token usage after an LLM call. No-op by default.
cost_cents is the cumulative session cost in USD cents as tracked by the
internal cost tracker (already-cumulative value — do not sum across calls).
§Errors
Returns an error if the underlying I/O fails.
Sourcefn send_diff(
&mut self,
_diff: DiffData,
_tool_call_id: &str,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_diff( &mut self, _diff: DiffData, _tool_call_id: &str, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Send diff data for a tool result. No-op by default (TUI overrides).
tool_call_id identifies which tool call produced the diff so it can
be attached to the correct ChatMessage.
§Errors
Returns an error if the underlying I/O fails.
Sourcefn send_tool_start(
&mut self,
_event: ToolStartEvent,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_tool_start( &mut self, _event: ToolStartEvent, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Announce that a tool call is starting.
Emitted before execution begins so the transport layer can send an
InProgress status to the peer before the result arrives.
No-op by default.
§Errors
Returns an error if the underlying I/O fails.
Sourcefn send_tool_output(
&mut self,
event: ToolOutputEvent,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_tool_output( &mut self, event: ToolOutputEvent, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Send a complete tool output with optional diff and filter stats atomically.
display is the formatted tool output. The default implementation forwards to
Channel::send. Structured channels (e.g. LoopbackChannel) override this to
emit a typed event so consumers can access tool_name and display as separate fields.
§Errors
Returns an error if the underlying I/O fails.
Sourcefn confirm(
&mut self,
_prompt: &str,
) -> impl Future<Output = Result<bool, ChannelError>> + Send
fn confirm( &mut self, _prompt: &str, ) -> impl Future<Output = Result<bool, ChannelError>> + Send
Request user confirmation for a destructive action. Returns true if confirmed.
Default: auto-confirm (for headless/test scenarios).
§Errors
Returns an error if the underlying I/O fails.
Sourcefn elicit(
&mut self,
_request: ElicitationRequest,
) -> impl Future<Output = Result<ElicitationResponse, ChannelError>> + Send
fn elicit( &mut self, _request: ElicitationRequest, ) -> impl Future<Output = Result<ElicitationResponse, ChannelError>> + Send
Request structured input from the user for an MCP elicitation.
Always displays request.server_name to prevent phishing by malicious servers.
Default: auto-decline (for headless/daemon/non-interactive scenarios).
§Errors
Returns an error if the underlying I/O fails.
Sourcefn send_stop_hint(
&mut self,
_hint: StopHint,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_stop_hint( &mut self, _hint: StopHint, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Signal the non-default stop reason to the consumer before flushing.
Called by the agent loop immediately before flush_chunks() when a
truncation or turn-limit condition is detected. No-op by default.
§Errors
Returns an error if the underlying I/O fails.
Sourcefn notify_foreground_subagent_started(
&mut self,
_id: &str,
_name: &str,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn notify_foreground_subagent_started( &mut self, _id: &str, _name: &str, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Notify channel that a foreground subagent has started. No-op by default.
Called after the subagent is spawned and before polling begins. Channels that support subagent views (e.g. TUI) should switch to the subagent transcript view on receipt.
§Errors
Returns an error if the underlying I/O fails.
Sourcefn notify_foreground_subagent_completed(
&mut self,
_id: &str,
_name: &str,
_success: bool,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn notify_foreground_subagent_completed( &mut self, _id: &str, _name: &str, _success: bool, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Notify channel that a foreground subagent has completed. No-op by default.
Called after poll_subagent_until_done returns. Channels that support
subagent views should switch back to the main view and show a status
notification.
§Errors
Returns an error if the underlying I/O fails.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".