pub struct CliChannel { /* private fields */ }Expand description
CLI channel that reads from stdin and writes to stdout.
Input is read in a background task (spawned lazily on the first Channel::recv
call), which makes recv() cancel-safe: dropping the future (e.g. inside a
tokio::select! branch) never discards buffered input — messages stay in the
internal mpsc channel and are returned on the next recv() call.
The channel automatically detects whether stdin is a TTY:
- TTY mode — uses
line_editor::read_linewith crossterm raw-mode for readline-style editing (cursor movement, history navigation,Ctrl-C/Ctrl-D). - Piped mode — spawns a dedicated OS thread that reads lines from a
BufReaderand shuttles them through a tokio channel, avoiding repeated stdin locks.
§Examples
use zeph_channels::CliChannel;
use zeph_core::channel::Channel;
let mut ch = CliChannel::new();
// Send a formatted reply to stdout.
ch.send("Hello from Zeph!").await.unwrap();Implementations§
Source§impl CliChannel
impl CliChannel
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a new CLI channel without persistent history.
This is safe to call outside of a Tokio runtime; the background stdin
reader task is not spawned until the first Channel::recv call.
Sourcepub fn with_history(
entries: Vec<String>,
persist_fn: impl Fn(&str) + Send + 'static,
) -> Self
pub fn with_history( entries: Vec<String>, persist_fn: impl Fn(&str) + Send + 'static, ) -> Self
Create a CLI channel with persistent input history.
entries is a pre-loaded history list (e.g. loaded from SQLite on
startup). persist_fn is called for each newly submitted entry so the
caller can persist it (e.g. via SqliteStore::save_input_entry).
Duplicate consecutive entries are silently ignored; empty lines are never added to the history.
§Examples
use zeph_channels::CliChannel;
let previous: Vec<String> = vec!["ls -la".into(), "cargo build".into()];
let ch = CliChannel::with_history(previous, |entry| {
// Persist `entry` to your storage layer.
eprintln!("saving: {entry}");
});Trait Implementations§
Source§impl Channel for CliChannel
impl Channel for CliChannel
Source§async fn recv(&mut self) -> Result<Option<ChannelMessage>, ChannelError>
async fn recv(&mut self) -> Result<Option<ChannelMessage>, ChannelError>
Receive the next user message.
This method is cancel-safe: dropping the future does not discard any
buffered input. The background stdin reader task buffers messages in an
mpsc channel; they remain available on the next recv() call.
Source§async fn send(&mut self, text: &str) -> Result<(), ChannelError>
async fn send(&mut self, text: &str) -> Result<(), ChannelError>
Write a complete agent reply to stdout.
The message is prefixed with "Zeph: " and followed by a newline.
Use send_chunk / flush_chunks for streaming output instead.
§Errors
Always returns Ok(()) — stdout writes do not produce recoverable
errors in this adapter.
Source§async fn send_chunk(&mut self, chunk: &str) -> Result<(), ChannelError>
async fn send_chunk(&mut self, chunk: &str) -> Result<(), ChannelError>
Write a streaming chunk to stdout and accumulate it internally.
Chunks are printed without a trailing newline so that the response
streams character-by-character. Call flush_chunks when the stream
is complete to emit the final newline and clear the internal buffer.
§Errors
Returns Err if the stdout flush fails.
Source§async fn flush_chunks(&mut self) -> Result<(), ChannelError>
async fn flush_chunks(&mut self) -> Result<(), ChannelError>
Finalise a streamed response by printing a trailing newline.
Clears the internal accumulation buffer so the channel is ready for the next response.
§Errors
Always returns Ok(()).
Source§async fn confirm(&mut self, prompt: &str) -> Result<bool, ChannelError>
async fn confirm(&mut self, prompt: &str) -> Result<bool, ChannelError>
Prompt the user for a yes/no confirmation on stdin.
In non-interactive (piped) mode the method auto-declines and returns
Ok(false) without blocking. In TTY mode it reads one line and returns
true only when the user types y or Y.
§Errors
Returns Err if spawning the blocking task fails or if the underlying
readline call returns an I/O error.
Source§async fn elicit(
&mut self,
request: ElicitationRequest,
) -> Result<ElicitationResponse, ChannelError>
async fn elicit( &mut self, request: ElicitationRequest, ) -> Result<ElicitationResponse, ChannelError>
Collect structured input from the user on behalf of an MCP server.
Prompts the user for each field in request.fields sequentially. In
non-interactive (piped) mode the method logs a warning and auto-declines
without blocking.
Field values are coerced to the declared ElicitationFieldType. If a
value cannot be coerced the method returns
ElicitationResponse::Declined immediately. Ctrl-C or Ctrl-D
returns ElicitationResponse::Cancelled.
§Errors
Returns Err if spawning the blocking task fails or if the underlying
readline call returns an I/O error.
Source§fn try_recv(&mut self) -> Option<ChannelMessage>
fn try_recv(&mut self) -> Option<ChannelMessage>
None if no message is immediately available.Source§fn supports_exit(&self) -> bool
fn supports_exit(&self) -> bool
Source§fn send_typing(
&mut self,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_typing( &mut self, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Source§fn 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
Source§fn 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
Source§fn 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
Source§fn send_usage(
&mut self,
_input_tokens: u64,
_output_tokens: u64,
_context_window: u64,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_usage( &mut self, _input_tokens: u64, _output_tokens: u64, _context_window: u64, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Source§fn send_diff(
&mut self,
_diff: DiffData,
) -> impl Future<Output = Result<(), ChannelError>> + Send
fn send_diff( &mut self, _diff: DiffData, ) -> impl Future<Output = Result<(), ChannelError>> + Send
Source§fn 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
Source§fn 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
Source§fn 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
Source§impl Debug for CliChannel
impl Debug for CliChannel
Auto Trait Implementations§
impl Freeze for CliChannel
impl !RefUnwindSafe for CliChannel
impl Send for CliChannel
impl !Sync for CliChannel
impl Unpin for CliChannel
impl UnsafeUnpin for CliChannel
impl !UnwindSafe for CliChannel
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Erasable for T
impl<T> Erasable for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request