use std::{future::Future, marker::PhantomData, path::Path, sync::Arc};
use futures::channel::mpsc;
use futures::future::{self, Either};
use crate::{
Agent, Client, ConnectionTo as RawConnectionTo, Dispatch, HandleDispatchFrom, Handled,
JsonRpcRequest, Responder, Role,
jsonrpc::{
DynamicHandlerCleanup, DynamicHandlerGuard,
run::{NullRun, RunWithConnectionTo, RunnerErrorScope},
},
role::{HasPeer, acp::ProxySessionMessages},
util::{MatchDispatch, MatchDispatchFrom},
v1::ConnectionTo,
v1::schema::{
ContentBlock, ContentChunk, LoadSessionRequest, LoadSessionResponse, NewSessionRequest,
NewSessionResponse, PromptRequest, PromptResponse, ResumeSessionRequest,
ResumeSessionResponse, SessionId, SessionNotification, SessionUpdate,
},
};
use crate::Raw;
#[cfg(feature = "unstable_mcp_over_acp")]
use crate::{jsonrpc::run::ChainRun, mcp_server::McpServer};
#[cfg(feature = "unstable_protocol_v2")]
pub(crate) mod v2;
type SessionCleanup = Vec<Arc<dyn DynamicHandlerCleanup>>;
fn session_cleanup<R: Role>(guards: &[DynamicHandlerGuard<R>]) -> SessionCleanup {
guards
.iter()
.filter_map(DynamicHandlerGuard::cleanup)
.collect()
}
fn close_session_registrations(cleanup: &SessionCleanup) {
for registration in cleanup {
registration.close();
}
}
async fn wait_session_cleanup(cleanup: SessionCleanup) {
future::join_all(cleanup.iter().map(|registration| registration.wait())).await;
}
fn session_runner_connection<R: Role>(
connection: RawConnectionTo<R>,
cleanup: &SessionCleanup,
) -> (RawConnectionTo<R>, RunnerErrorScope) {
let closing = cleanup.clone();
let scope = RunnerErrorScope::new(
move || close_session_registrations(&closing),
wait_session_cleanup(cleanup.clone()),
);
(connection.with_runner_error_scope(scope.clone()), scope)
}
async fn drive_session_cleanup(
run: impl Future<Output = Result<(), crate::Error>>,
cleanup: SessionCleanup,
) -> Result<(), crate::Error> {
match future::select(
Box::pin(wait_session_cleanup(cleanup.clone())),
Box::pin(run),
)
.await
{
Either::Left(((), _run)) => Ok(()),
Either::Right((result, waiting)) => {
if result.is_err() {
close_session_registrations(&cleanup);
}
waiting.await;
result
}
}
}
async fn run_attached_session_runner(
run: impl Future<Output = Result<(), crate::Error>>,
cleanup: SessionCleanup,
error_scope: RunnerErrorScope,
) -> Result<(), crate::Error> {
let result = if cleanup.is_empty() {
run.await
} else {
drive_session_cleanup(run, cleanup).await
};
error_scope.error().map_or(result, Err)
}
async fn run_session_scope<T>(
run: impl Future<Output = Result<(), crate::Error>>,
op: impl Future<Output = Result<T, crate::Error>>,
cleanup: SessionCleanup,
error_scope: RunnerErrorScope,
) -> Result<T, crate::Error> {
let result = match future::select(Box::pin(run), Box::pin(op)).await {
Either::Left((run_result, op)) => {
let result = match run_result {
Ok(()) => op.await,
Err(error) => {
drop(op);
Err(error)
}
};
close_session_registrations(&cleanup);
wait_session_cleanup(cleanup).await;
result
}
Either::Right((result, run)) => {
close_session_registrations(&cleanup);
let runner_result = drive_session_cleanup(run, cleanup).await;
result.and_then(|value| runner_result.map(|()| value))
}
};
result.and_then(|value| error_scope.error().map_or(Ok(value), Err))
}
#[derive(Debug)]
pub struct Blocking;
impl block_state::Sealed for Blocking {}
impl SessionBlockState for Blocking {}
#[derive(Debug)]
pub struct NonBlocking;
impl block_state::Sealed for NonBlocking {}
impl SessionBlockState for NonBlocking {}
mod block_state {
pub trait Sealed {}
}
pub trait SessionBlockState: block_state::Sealed + Send + 'static + Sync + std::fmt::Debug {}
impl<Counterpart: Role> ConnectionTo<Counterpart>
where
Counterpart: HasPeer<Agent>,
{
pub fn build_session(&self, cwd: impl AsRef<Path>) -> SessionBuilder<Counterpart, NullRun> {
SessionBuilder::new(self, NewSessionRequest::new(cwd.as_ref()))
}
pub fn build_session_cwd(&self) -> Result<SessionBuilder<Counterpart, NullRun>, crate::Error> {
let cwd = std::env::current_dir().map_err(|e| {
crate::Error::internal_error().data(format!("cannot get current directory: {e}"))
})?;
Ok(self.build_session(cwd))
}
pub fn build_session_from(
&self,
request: NewSessionRequest,
) -> SessionBuilder<Counterpart, NullRun> {
SessionBuilder::new(self, request)
}
pub fn load_session(
&self,
session_id: impl Into<SessionId>,
cwd: impl AsRef<Path>,
) -> LoadSessionBuilder<Counterpart> {
self.load_session_from(LoadSessionRequest::new(session_id, cwd.as_ref()))
}
pub fn load_session_from(
&self,
request: LoadSessionRequest,
) -> LoadSessionBuilder<Counterpart> {
RestoreSessionBuilder::new(self, request)
}
pub fn resume_session(
&self,
session_id: impl Into<SessionId>,
cwd: impl AsRef<Path>,
) -> ResumeSessionBuilder<Counterpart> {
self.resume_session_from(ResumeSessionRequest::new(session_id, cwd.as_ref()))
}
pub fn resume_session_from(
&self,
request: ResumeSessionRequest,
) -> ResumeSessionBuilder<Counterpart> {
RestoreSessionBuilder::new(self, request)
}
pub(crate) fn attach_session<'runner>(
&self,
response: NewSessionResponse,
mcp_handler_registrations: Vec<DynamicHandlerGuard<Counterpart>>,
) -> Result<SessionSetup<'runner, Counterpart, NewSessionResponse>, crate::Error> {
let prepared = self.prepare_session_routing(&response.session_id)?;
let session = prepared.into_active_session(
self.clone(),
response.session_id.clone(),
mcp_handler_registrations,
);
Ok(SessionSetup { session, response })
}
fn prepare_session_routing(
&self,
session_id: &SessionId,
) -> Result<PreparedSession<Counterpart>, crate::Error> {
let (update_tx, update_rx) = mpsc::unbounded();
let handler = ActiveSessionHandler::new(session_id.clone(), update_tx.clone());
let session_handler_registration = self.add_dynamic_handler(handler)?;
Ok(PreparedSession {
update_rx,
update_tx,
session_handler_registration,
})
}
}
struct PreparedSession<Counterpart: Role>
where
Counterpart: HasPeer<Agent>,
{
update_rx: mpsc::UnboundedReceiver<SessionMessage>,
update_tx: mpsc::UnboundedSender<SessionMessage>,
session_handler_registration: DynamicHandlerGuard<Counterpart>,
}
impl<Counterpart> PreparedSession<Counterpart>
where
Counterpart: HasPeer<Agent>,
{
fn into_active_session<'runner>(
self,
connection: ConnectionTo<Counterpart>,
session_id: SessionId,
mcp_handler_registrations: Vec<DynamicHandlerGuard<Counterpart>>,
) -> ActiveSession<'runner, Counterpart> {
ActiveSession {
session_id,
update_rx: self.update_rx,
update_tx: self.update_tx,
connection,
session_handler_registration: self.session_handler_registration,
mcp_handler_registrations,
_runner: PhantomData,
}
}
}
trait RestoreRequest: JsonRpcRequest {
fn session_id(&self) -> &SessionId;
}
impl RestoreRequest for LoadSessionRequest {
fn session_id(&self) -> &SessionId {
&self.session_id
}
}
impl RestoreRequest for ResumeSessionRequest {
fn session_id(&self) -> &SessionId {
&self.session_id
}
}
#[must_use = "use `start_session` or `on_session_start` to restore the session"]
#[derive(Debug)]
pub struct RestoreSessionBuilder<Counterpart, Request, BlockState = NonBlocking>
where
Counterpart: HasPeer<Agent>,
BlockState: SessionBlockState,
{
connection: ConnectionTo<Counterpart>,
request: Request,
block_state: PhantomData<BlockState>,
}
pub type LoadSessionBuilder<Counterpart, BlockState = NonBlocking> =
RestoreSessionBuilder<Counterpart, LoadSessionRequest, BlockState>;
pub type ResumeSessionBuilder<Counterpart, BlockState = NonBlocking> =
RestoreSessionBuilder<Counterpart, ResumeSessionRequest, BlockState>;
impl<Counterpart, Request> RestoreSessionBuilder<Counterpart, Request, NonBlocking>
where
Counterpart: HasPeer<Agent>,
{
fn new(connection: &ConnectionTo<Counterpart>, request: Request) -> Self {
Self {
connection: connection.clone(),
request,
block_state: PhantomData,
}
}
pub fn block_task(self) -> RestoreSessionBuilder<Counterpart, Request, Blocking> {
RestoreSessionBuilder {
connection: self.connection,
request: self.request,
block_state: PhantomData,
}
}
}
fn restored_session<Counterpart, Request>(
connection: ConnectionTo<Counterpart>,
session_id: SessionId,
prepared: PreparedSession<Counterpart>,
response: Request::Response,
) -> SessionSetup<'static, Counterpart, Request::Response>
where
Counterpart: HasPeer<Agent>,
Request: RestoreRequest,
{
let session = prepared.into_active_session(connection, session_id, Vec::new());
SessionSetup { session, response }
}
fn on_restore_session_start<Counterpart, Request, F, Fut>(
builder: RestoreSessionBuilder<Counterpart, Request>,
op: F,
) -> Result<(), crate::Error>
where
Counterpart: HasPeer<Agent>,
Request: RestoreRequest,
F: FnOnce(SessionSetup<'static, Counterpart, Request::Response>) -> Fut + Send + 'static,
Fut: Future<Output = Result<(), crate::Error>> + Send,
{
let RestoreSessionBuilder {
connection,
request,
block_state: _,
} = builder;
let session_id = request.session_id().clone();
let prepared = connection.prepare_session_routing(&session_id)?;
let routing_ready = connection.raw_connection().dynamic_handler_barrier();
connection
.raw_connection()
.send_ordered_request_to_after::<Raw, _, _, _>(Agent, request, routing_ready)
.on_receiving_result({
let connection = connection.clone();
async move |result| {
let response = result?;
let restored = restored_session::<_, Request>(
connection.clone(),
session_id,
prepared,
response,
);
connection.spawn(async move { op(restored).await })
}
})
}
async fn start_restored_session<Counterpart, Request>(
builder: RestoreSessionBuilder<Counterpart, Request, Blocking>,
) -> Result<SessionSetup<'static, Counterpart, Request::Response>, crate::Error>
where
Counterpart: HasPeer<Agent>,
Request: RestoreRequest,
{
let RestoreSessionBuilder {
connection,
request,
block_state: _,
} = builder;
let session_id = request.session_id().clone();
let prepared = connection.prepare_session_routing(&session_id)?;
let routing_ready = connection.raw_connection().dynamic_handler_barrier();
let session_connection = connection.clone();
connection
.raw_connection()
.send_ordered_request_to_after::<Raw, _, _, _>(Agent, request, routing_ready)
.block_task_with_ordered_result(move |result| {
let response = result?;
Ok(restored_session::<_, Request>(
session_connection,
session_id,
prepared,
response,
))
})
.await
}
impl<Counterpart> RestoreSessionBuilder<Counterpart, LoadSessionRequest>
where
Counterpart: HasPeer<Agent>,
{
pub fn on_session_start<F, Fut>(self, op: F) -> Result<(), crate::Error>
where
F: FnOnce(SessionSetup<'static, Counterpart, LoadSessionResponse>) -> Fut + Send + 'static,
Fut: Future<Output = Result<(), crate::Error>> + Send,
{
on_restore_session_start(self, op)
}
}
impl<Counterpart> RestoreSessionBuilder<Counterpart, ResumeSessionRequest>
where
Counterpart: HasPeer<Agent>,
{
pub fn on_session_start<F, Fut>(self, op: F) -> Result<(), crate::Error>
where
F: FnOnce(SessionSetup<'static, Counterpart, ResumeSessionResponse>) -> Fut
+ Send
+ 'static,
Fut: Future<Output = Result<(), crate::Error>> + Send,
{
on_restore_session_start(self, op)
}
}
impl<Counterpart> RestoreSessionBuilder<Counterpart, LoadSessionRequest, Blocking>
where
Counterpart: HasPeer<Agent>,
{
pub async fn start_session(
self,
) -> Result<SessionSetup<'static, Counterpart, LoadSessionResponse>, crate::Error> {
start_restored_session(self).await
}
}
impl<Counterpart> RestoreSessionBuilder<Counterpart, ResumeSessionRequest, Blocking>
where
Counterpart: HasPeer<Agent>,
{
pub async fn start_session(
self,
) -> Result<SessionSetup<'static, Counterpart, ResumeSessionResponse>, crate::Error> {
start_restored_session(self).await
}
}
#[non_exhaustive]
pub struct SessionSetup<'runner, Link, Response>
where
Link: HasPeer<Agent>,
{
pub session: ActiveSession<'runner, Link>,
pub response: Response,
}
impl<Link, Response> std::fmt::Debug for SessionSetup<'_, Link, Response>
where
Link: HasPeer<Agent>,
Response: std::fmt::Debug,
{
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("SessionSetup")
.field("session_id", self.session.session_id())
.field("response", &self.response)
.finish()
}
}
#[must_use = "use `start_session`, `run_until`, or `on_session_start` to start the session"]
#[derive(Debug)]
pub struct SessionBuilder<
Counterpart,
Run: RunWithConnectionTo<Counterpart> = NullRun,
BlockState: SessionBlockState = NonBlocking,
> where
Counterpart: HasPeer<Agent>,
{
connection: ConnectionTo<Counterpart>,
request: NewSessionRequest,
dynamic_handler_registrations: Vec<DynamicHandlerGuard<Counterpart>>,
run: Run,
block_state: PhantomData<BlockState>,
}
impl<Counterpart> SessionBuilder<Counterpart, NullRun, NonBlocking>
where
Counterpart: HasPeer<Agent>,
{
fn new(connection: &ConnectionTo<Counterpart>, request: NewSessionRequest) -> Self {
SessionBuilder {
connection: connection.clone(),
request,
dynamic_handler_registrations: Vec::default(),
run: NullRun,
block_state: PhantomData,
}
}
}
impl<Counterpart, R, BlockState> SessionBuilder<Counterpart, R, BlockState>
where
Counterpart: HasPeer<Agent>,
R: RunWithConnectionTo<Counterpart>,
BlockState: SessionBlockState,
{
#[cfg(feature = "unstable_mcp_over_acp")]
pub fn with_mcp_server<McpRun>(
mut self,
mcp_server: McpServer<Counterpart, McpRun>,
) -> Result<SessionBuilder<Counterpart, ChainRun<R, McpRun>, BlockState>, crate::Error>
where
McpRun: RunWithConnectionTo<Counterpart>,
{
let (handler, mcp_run) = mcp_server.into_handler_and_runner();
self.dynamic_handler_registrations.push(
handler.into_dynamic_handler(&mut self.request, self.connection.raw_connection())?,
);
Ok(SessionBuilder {
connection: self.connection,
request: self.request,
dynamic_handler_registrations: self.dynamic_handler_registrations,
run: ChainRun::new(self.run, mcp_run),
block_state: self.block_state,
})
}
pub fn on_session_start<F, Fut>(self, op: F) -> Result<(), crate::Error>
where
R: 'static,
F: FnOnce(SessionSetup<'static, Counterpart, NewSessionResponse>) -> Fut + Send + 'static,
Fut: Future<Output = Result<(), crate::Error>> + Send,
{
let Self {
connection,
request,
dynamic_handler_registrations,
run,
block_state: _,
} = self;
let cleanup = session_cleanup(&dynamic_handler_registrations);
let (runner_connection, error_scope) =
session_runner_connection(connection.raw_connection().clone(), &cleanup);
connection.spawn(run_attached_session_runner(
run.run_with_connection_to(runner_connection),
cleanup,
error_scope,
))?;
connection
.raw_connection()
.send_ordered_request_to::<Raw, _, _>(Agent, request)
.on_receiving_result({
let connection = connection.clone();
async move |result| {
let response = result?;
let setup =
connection.attach_session(response, dynamic_handler_registrations)?;
connection.spawn(async move { op(setup).await })
}
})
}
pub fn on_proxy_session_start<F, Fut>(
self,
responder: Responder<NewSessionResponse, crate::V1>,
op: F,
) -> Result<(), crate::Error>
where
F: FnOnce(NewSessionResponse) -> Fut + Send + 'static,
Fut: Future<Output = Result<(), crate::Error>> + Send,
Counterpart: HasPeer<Client>,
R: 'static,
{
let Self {
connection,
request,
dynamic_handler_registrations,
run,
block_state: _,
} = self;
let cleanup = session_cleanup(&dynamic_handler_registrations);
let (runner_connection, error_scope) =
session_runner_connection(connection.raw_connection().clone(), &cleanup);
connection.spawn(run_attached_session_runner(
run.run_with_connection_to(runner_connection),
cleanup,
error_scope,
))?;
let sent = connection
.raw_connection()
.send_ordered_request_to::<Raw, _, _>(Agent, request);
let sent = sent.forward_cancellation_from(responder.cancellation());
sent.on_receiving_ok_result(responder, {
let connection = connection.clone();
async move |response, responder| {
let session_id = response.session_id.clone();
responder.respond(response.clone())?;
connection
.add_dynamic_handler(ProxySessionMessages::new(session_id))?
.detach();
dynamic_handler_registrations
.into_iter()
.for_each(DynamicHandlerGuard::detach);
connection.spawn(async move { op(response).await })
}
})
}
}
impl<Counterpart, R> SessionBuilder<Counterpart, R, NonBlocking>
where
Counterpart: HasPeer<Agent>,
R: RunWithConnectionTo<Counterpart>,
{
pub fn block_task(self) -> SessionBuilder<Counterpart, R, Blocking> {
SessionBuilder {
connection: self.connection,
request: self.request,
dynamic_handler_registrations: self.dynamic_handler_registrations,
run: self.run,
block_state: PhantomData,
}
}
}
impl<Counterpart, R> SessionBuilder<Counterpart, R, Blocking>
where
Counterpart: HasPeer<Agent>,
R: RunWithConnectionTo<Counterpart>,
{
pub async fn run_until<T>(
self,
op: impl for<'runner> AsyncFnOnce(
SessionSetup<'runner, Counterpart, NewSessionResponse>,
) -> Result<T, crate::Error>,
) -> Result<T, crate::Error> {
let Self {
connection,
request,
dynamic_handler_registrations,
run,
block_state: _,
} = self;
let cleanup = session_cleanup(&dynamic_handler_registrations);
let (runner_connection, error_scope) =
session_runner_connection(connection.raw_connection().clone(), &cleanup);
run_session_scope(
run.run_with_connection_to(runner_connection),
async move {
let response = connection
.send_request_to(Agent, request)
.block_task()
.await?;
let setup = connection.attach_session(response, dynamic_handler_registrations)?;
op(setup).await
},
cleanup,
error_scope,
)
.await
}
pub async fn start_session(
self,
) -> Result<SessionSetup<'static, Counterpart, NewSessionResponse>, crate::Error>
where
R: 'static,
{
let Self {
connection,
request,
dynamic_handler_registrations,
run,
block_state: _,
} = self;
let cleanup = session_cleanup(&dynamic_handler_registrations);
let (runner_connection, error_scope) =
session_runner_connection(connection.raw_connection().clone(), &cleanup);
connection.spawn(run_attached_session_runner(
run.run_with_connection_to(runner_connection),
cleanup,
error_scope,
))?;
let session_connection = connection.clone();
connection
.raw_connection()
.send_ordered_request_to::<Raw, _, _>(Agent, request)
.block_task_with_ordered_result(move |result| {
let response = result?;
session_connection.attach_session(response, dynamic_handler_registrations)
})
.await
}
pub async fn start_session_proxy(
self,
responder: Responder<NewSessionResponse, crate::V1>,
) -> Result<NewSessionResponse, crate::Error>
where
Counterpart: HasPeer<Client>,
R: 'static,
{
let SessionSetup {
session, response, ..
} = self.start_session().await?;
responder.respond(response.clone())?;
session.proxy_remaining_messages()?;
Ok(response)
}
}
#[derive(Debug)]
pub struct ActiveSession<'runner, Link>
where
Link: HasPeer<Agent>,
{
session_id: SessionId,
update_rx: mpsc::UnboundedReceiver<SessionMessage>,
update_tx: mpsc::UnboundedSender<SessionMessage>,
connection: ConnectionTo<Link>,
session_handler_registration: DynamicHandlerGuard<Link>,
mcp_handler_registrations: Vec<DynamicHandlerGuard<Link>>,
_runner: PhantomData<&'runner ()>,
}
#[non_exhaustive]
#[derive(Debug)]
#[allow(
clippy::large_enum_variant,
reason = "messages vastly outnumber prompt responses; boxing would add a heap allocation"
)]
pub enum SessionMessage {
Message(crate::v1::Dispatch),
PromptResponse(Result<PromptResponse, crate::Error>),
}
impl<Link> ActiveSession<'_, Link>
where
Link: HasPeer<Agent>,
{
pub fn session_id(&self) -> &SessionId {
&self.session_id
}
pub fn connection(&self) -> &ConnectionTo<Link> {
&self.connection
}
pub fn send_prompt(&mut self, prompt: impl ToString) -> Result<(), crate::Error> {
self.send_prompt_blocks(vec![prompt.to_string().into()])
}
pub fn send_prompt_blocks(&mut self, prompt: Vec<ContentBlock>) -> Result<(), crate::Error> {
let update_tx = self.update_tx.clone();
self.connection
.raw_connection()
.send_ordered_request_to::<Raw, _, _>(
Agent,
PromptRequest::new(self.session_id.clone(), prompt),
)
.on_receiving_result(async move |result| {
drop(update_tx.unbounded_send(SessionMessage::PromptResponse(result)));
Ok(())
})
}
pub async fn read_update(&mut self) -> Result<SessionMessage, crate::Error> {
use futures::StreamExt;
let message =
self.update_rx.next().await.ok_or_else(|| {
crate::util::internal_error("session channel closed unexpectedly")
})?;
Ok(message)
}
pub async fn read_to_string(&mut self) -> Result<String, crate::Error> {
let mut output = String::new();
loop {
let update = self.read_update().await?;
tracing::trace!("read_to_string update");
match update {
SessionMessage::Message(dispatch) => MatchDispatch::new(dispatch.into_raw())
.if_notification(async |notif: SessionNotification| match notif.update {
SessionUpdate::AgentMessageChunk(ContentChunk {
content: ContentBlock::Text(text),
..
}) => {
output.push_str(&text.text);
Ok(())
}
_ => Ok(()),
})
.await
.otherwise_ignore()?,
SessionMessage::PromptResponse(response) => {
response?;
break;
}
}
}
Ok(output)
}
}
impl<Link> ActiveSession<'static, Link>
where
Link: HasPeer<Agent>,
{
pub fn proxy_remaining_messages(self) -> Result<(), crate::Error>
where
Link: HasPeer<Client>,
{
let ActiveSession {
session_id,
mut update_rx,
update_tx,
connection,
session_handler_registration,
mcp_handler_registrations,
_runner,
} = self;
session_handler_registration.deactivate();
drop(update_tx);
while let Ok(message) = update_rx.try_recv() {
match message {
SessionMessage::Message(dispatch) => {
connection.send_proxied_message_to(Client, dispatch)?;
}
SessionMessage::PromptResponse(_) => {
}
}
}
connection
.add_dynamic_handler(ProxySessionMessages::new(session_id))?
.detach();
drop(session_handler_registration);
for registration in mcp_handler_registrations {
registration.detach();
}
Ok(())
}
}
struct ActiveSessionHandler {
session_id: SessionId,
update_tx: mpsc::UnboundedSender<SessionMessage>,
}
impl ActiveSessionHandler {
pub fn new(session_id: SessionId, update_tx: mpsc::UnboundedSender<SessionMessage>) -> Self {
Self {
session_id,
update_tx,
}
}
}
impl<Counterpart: Role> HandleDispatchFrom<Counterpart> for ActiveSessionHandler
where
Counterpart: HasPeer<Agent>,
{
async fn handle_dispatch_from(
&mut self,
message: Dispatch,
cx: RawConnectionTo<Counterpart>,
) -> Result<Handled<Dispatch>, crate::Error> {
tracing::trace!(
method = message.method(),
handler_session_id = ?self.session_id,
"ActiveSessionHandler::handle_dispatch"
);
MatchDispatchFrom::new(message, &cx)
.if_dispatch_from(Agent, async |message| {
if let Some(session_id) = message.get_session_id()? {
tracing::trace!(
message_session_id = ?session_id,
handler_session_id = ?self.session_id,
"ActiveSessionHandler::handle_dispatch"
);
if session_id == self.session_id {
self.update_tx
.unbounded_send(SessionMessage::Message(message.into_protocol()?))
.map_err(crate::util::internal_error)?;
return Ok(Handled::Yes);
}
}
Ok(Handled::No {
message,
retry: false,
})
})
.await
.done()
}
fn describe_chain(&self) -> impl std::fmt::Debug {
format!("ActiveSessionHandler({})", self.session_id)
}
}