cap_rs/driver.rs
1//! Driver backends — concrete implementations that drive a CLI agent.
2//!
3//! Each driver is gated behind a feature flag. The cross-driver API is the
4//! [`Driver`] trait; all drivers emit the same [`crate::core::AgentEvent`]
5//! stream regardless of wire format.
6
7#[cfg(feature = "stream-json")]
8pub mod stream_json;
9
10#[cfg(feature = "pty")]
11pub mod pty;
12
13// Future modules — gated on their respective features:
14// #[cfg(feature = "acp")] pub mod acp;
15// #[cfg(feature = "a2a")] pub mod a2a;
16// #[cfg(feature = "grpc")] pub mod grpc;
17// #[cfg(feature = "orchestrator")] pub mod orchestrator;
18
19// The Driver trait and DriverError are shared across all driver backends.
20// They're gated on `any(stream-json, pty, ...)` because their deps
21// (async-trait, thiserror) come in via those features.
22
23#[cfg(any(feature = "stream-json", feature = "pty"))]
24mod common {
25 use crate::core::{AgentEvent, ClientFrame};
26
27 /// A unified driver interface. Concrete drivers translate this to their
28 /// underlying wire format (PTY, stream-json, gRPC, ACP-stdio, A2A SSE).
29 #[async_trait::async_trait]
30 pub trait Driver: Send {
31 /// Send a frame to the agent. The frame is processed asynchronously;
32 /// resulting events arrive via [`Driver::next_event`].
33 async fn send(&mut self, frame: ClientFrame) -> Result<(), DriverError>;
34
35 /// Await the next event from the agent. Returns `None` when the
36 /// agent has exited cleanly.
37 async fn next_event(&mut self) -> Option<AgentEvent>;
38
39 /// Shut down the agent process and release resources.
40 async fn shutdown(&mut self) -> Result<(), DriverError>;
41 }
42
43 /// Driver-level errors.
44 #[derive(Debug, thiserror::Error)]
45 #[non_exhaustive]
46 pub enum DriverError {
47 #[error("agent binary not found on PATH: {0}")]
48 BinaryNotFound(String),
49
50 #[error("failed to spawn agent process: {0}")]
51 SpawnFailed(#[source] std::io::Error),
52
53 #[error("agent process exited unexpectedly")]
54 AgentExited,
55
56 #[error("io error while talking to agent: {0}")]
57 Io(#[from] std::io::Error),
58
59 #[error("failed to parse agent output: {0}")]
60 Parse(String),
61
62 #[error("agent reported error: {code} — {message}")]
63 AgentError { code: String, message: String },
64 }
65
66 #[cfg(feature = "stream-json")]
67 impl From<serde_json::Error> for DriverError {
68 fn from(e: serde_json::Error) -> Self {
69 DriverError::Parse(e.to_string())
70 }
71 }
72}
73
74#[cfg(any(feature = "stream-json", feature = "pty"))]
75pub use common::{Driver, DriverError};