use std::sync::Arc;
use tokio::sync::mpsc;
use crate::env::SecretResolver;
use crate::error::AdapterError;
use crate::model::{AgentEvent, AgentSpec, Capabilities, LaneSpec, SessionRequest, TurnOutcome};
use crate::observe::SharedObserver;
use crate::proto::acp::{AcpLane, AcpSession, PermissionDecision, SessionDelegate};
use crate::proto::cli::CliLane;
use crate::proto::rpc::{RpcLane, RpcSession, RpcSessionDelegate};
#[derive(Clone, Debug, Default)]
pub struct TurnRequest {
pub text: String,
pub model: Option<String>,
pub effort: Option<String>,
}
#[async_trait::async_trait]
pub trait PermissionHandler: Send + Sync {
async fn decide(&self, prompt: crate::proto::acp::PermissionPrompt) -> PermissionDecision;
}
#[async_trait::async_trait]
pub trait BeforeTurn: Send + Sync {
async fn run(&self) -> Result<(), AdapterError>;
}
pub struct AgentSession {
lane: LaneSession,
before_turn: Option<Arc<dyn BeforeTurn>>,
}
pub enum LaneSession {
Acp(AcpSession),
Rpc(Box<RpcSession>),
Cli(CliSession),
}
pub struct CliSession {
lane: Arc<CliLane>,
request: SessionRequest,
events: mpsc::UnboundedSender<AgentEvent>,
}
impl CliSession {
pub fn session_id(&self) -> &str {
""
}
}
impl AgentSession {
pub fn lane(&self) -> &LaneSession {
&self.lane
}
pub fn session_id(&self) -> &str {
match &self.lane {
LaneSession::Acp(session) => session.session_id(),
LaneSession::Rpc(session) => session.session_id().unwrap_or(""),
LaneSession::Cli(session) => session.session_id(),
}
}
pub async fn prompt(&self, request: TurnRequest) -> Result<TurnOutcome, AdapterError> {
if let Some(hook) = &self.before_turn {
hook.run().await?;
}
match &self.lane {
LaneSession::Acp(session) => session.prompt(request.text).await,
LaneSession::Rpc(session) => {
session
.prompt(
&request.text,
request.model.as_deref(),
request.effort.as_deref(),
)
.await
}
LaneSession::Cli(session) => {
session
.lane
.run_turn(
&session.request,
&request.text,
request.model.as_deref(),
request.effort.as_deref(),
session.events.clone(),
)
.await
}
}
}
pub async fn cancel(&self) -> Result<(), AdapterError> {
match &self.lane {
LaneSession::Acp(session) => session.cancel(),
LaneSession::Rpc(session) => session.cancel(),
LaneSession::Cli(_) => Err(AdapterError::permanent(
"agent_cancel_unsupported",
"session/cancel",
)),
}
}
pub async fn close(self) -> Result<(), AdapterError> {
match self.lane {
LaneSession::Acp(session) => session.close().await,
LaneSession::Rpc(session) => session.close().await,
LaneSession::Cli(_) => Ok(()),
}
}
}
pub struct Adapter {
spec: AgentSpec,
secrets: Arc<dyn SecretResolver>,
observer: SharedObserver,
permission: Option<Arc<dyn PermissionHandler>>,
before_turn: Option<Arc<dyn BeforeTurn>>,
}
impl Adapter {
pub fn new(spec: AgentSpec) -> Self {
Self {
spec,
secrets: Arc::new(crate::env::NoSecrets),
observer: crate::observe::none(),
permission: None,
before_turn: None,
}
}
pub fn secrets(mut self, secrets: Arc<dyn SecretResolver>) -> Self {
self.secrets = secrets;
self
}
pub fn observer(mut self, observer: SharedObserver) -> Self {
self.observer = observer;
self
}
pub fn permission(mut self, handler: Arc<dyn PermissionHandler>) -> Self {
self.permission = Some(handler);
self
}
pub fn before_turn(mut self, hook: Arc<dyn BeforeTurn>) -> Self {
self.before_turn = Some(hook);
self
}
pub fn id(&self) -> &str {
&self.spec.id
}
pub fn spec(&self) -> &AgentSpec {
&self.spec
}
pub fn capabilities(&self) -> Capabilities {
match &self.spec.lane {
LaneSpec::Acp { .. } => Capabilities {
streaming: true,
cancellation: true,
session_resume: true,
permission_prompts: true,
detail: None,
},
LaneSpec::Rpc(spec) => spec.capabilities(),
LaneSpec::Cli(_) => Capabilities {
streaming: true,
cancellation: false,
session_resume: false,
permission_prompts: false,
detail: None,
},
}
}
pub async fn open_session(
&self,
request: SessionRequest,
events: mpsc::UnboundedSender<AgentEvent>,
) -> Result<AgentSession, AdapterError> {
match &self.spec.lane {
LaneSpec::Acp {
mcp_servers,
prefer_load,
} => {
struct Bridge {
events: mpsc::UnboundedSender<AgentEvent>,
permission: Option<Arc<dyn PermissionHandler>>,
}
#[async_trait::async_trait]
impl SessionDelegate for Bridge {
async fn on_event(&self, event: AgentEvent) {
let _ = self.events.send(event);
}
async fn on_permission(
&self,
prompt: crate::proto::acp::PermissionPrompt,
) -> PermissionDecision {
match self.permission.as_ref() {
Some(handler) => handler.decide(prompt).await,
None => PermissionDecision::Cancelled,
}
}
}
let lane = AcpLane {
command: self.spec.command.clone(),
mcp_servers: mcp_servers.clone(),
prefer_load: *prefer_load,
client_name: self
.spec
.client_name
.clone()
.unwrap_or_else(|| "a4aa".to_owned()),
secrets: self.secrets.clone(),
observer: self.observer.clone(),
};
let session = lane
.open(
request,
Arc::new(Bridge {
events,
permission: self.permission.clone(),
}),
)
.await?;
Ok(AgentSession {
lane: LaneSession::Acp(session),
before_turn: self.before_turn.clone(),
})
}
LaneSpec::Rpc(spec) => {
struct Bridge {
events: mpsc::UnboundedSender<AgentEvent>,
}
#[async_trait::async_trait]
impl RpcSessionDelegate for Bridge {
async fn on_event(&self, event: AgentEvent) {
let _ = self.events.send(event);
}
}
let lane = RpcLane {
command: self.spec.command.clone(),
spec: (**spec).clone(),
secrets: self.secrets.clone(),
observer: self.observer.clone(),
};
let session = lane.open(request, Arc::new(Bridge { events })).await?;
Ok(AgentSession {
lane: LaneSession::Rpc(Box::new(session)),
before_turn: self.before_turn.clone(),
})
}
LaneSpec::Cli(lane_spec) => {
let lane = CliLane {
command: self.spec.command.clone(),
lane: lane_spec.clone(),
secrets: self.secrets.clone(),
observer: self.observer.clone(),
max_output_bytes: self.spec.max_output_bytes,
};
Ok(AgentSession {
lane: LaneSession::Cli(CliSession {
lane: Arc::new(lane),
request,
events,
}),
before_turn: self.before_turn.clone(),
})
}
}
}
}