Skip to main content

Channel

Trait Channel 

Source
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:

  • MessageChannelsend / recv (required for all channels)
  • StreamingChannelsend_streaming_chunk / finish_stream (opt-in)
  • ElicitationChannelrequest_elicitation (opt-in)
  • StatusChannelset_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§

Source

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.

Source

fn send( &mut self, text: &str, ) -> impl Future<Output = Result<(), ChannelError>> + Send

Send a text response.

§Errors

Returns an error if the underlying I/O fails.

Source

fn send_chunk( &mut self, chunk: &str, ) -> impl Future<Output = Result<(), ChannelError>> + Send

Send a partial chunk of streaming response.

§Errors

Returns an error if the underlying I/O fails.

Source

fn flush_chunks( &mut self, ) -> impl Future<Output = Result<(), ChannelError>> + Send

Flush any buffered chunks.

§Errors

Returns an error if the underlying I/O fails.

Provided Methods§

Source

fn try_recv(&mut self) -> Option<ChannelMessage>

Non-blocking receive. Returns None if no message is immediately available.

Source

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.

Source

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.

Source

fn send_typing( &mut self, ) -> impl Future<Output = Result<(), ChannelError>> + Send

Send a typing indicator. No-op by default.

§Errors

Returns an error if the underlying I/O fails.

Source

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.

Source

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.

Source

fn send_resume_banner( &mut self, text: &str, ) -> impl Future<Output = Result<(), ChannelError>> + Send

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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".

Implementors§